Search code examples
javaregexfull-text-searchjtextfield

Java WildCard replacement


I have a text field where a user is required to input information.

JTextField data = new JTextField();

I have a requirment that if a user enters in *. Then it should be treated as a regex wildcard for when I search for the data.

I am looping through a series of files and reading each line one by one.

for(int i = 0; i < files.length; i++) {
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(files[i]));
            String text = null;

            while ((text = reader.readLine()) != null) {
                if(text.contains(data) return text; // Line that requires wildcard check
            }
        } catch(IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {}
        }
    }

How could I achieve this wildcard check? I require to make the '*' become any character once entered by user.


Solution

  • The problem is that the search string may contain other characters which would be significant in a regular expression, so it isn’t safe to just blindly convert * to .*.

    You’ll want to use Pattern.quote on all parts of the search string except for the asterisks:

    String[] parts = data.split("\\*");
    Pattern pattern = Pattern.compile(
        Stream.of(parts).map(Pattern::quote).collect(Collectors.joining(".*")));
    
    if (pattern.matcher(text).find()) {
        return text;
    }
    

    Breaking down the above statement:

    • data.split("\\*") splits the string by a regular expression that literally matches the * character, into an array of substrings. Examples:
      • "ab*cd"{ "ab", "cd" }
      • "1*2345*67"{ "1", "2345", "67" }
    • Stream.of(parts) creates a Stream from the array of substrings.
    • map(Pattern:quote) replaces each element in the Stream with its quoted equivalent, so any regex metacharacters (other than *) will be treated as ordinary characters. Thus, "1+1" in the original user input would actually match those three characters in the searched files.
    • collect(Collectors.joining(".*")) reassembles the elements in the stream into a single String, with .* between each quoted part.

    On a side note, you can avoid writing a finally block by placing your BufferedReader in a try-with-resources statement:

    String[] parts = data.split("\\*");
    Pattern pattern = Pattern.compile(
        Stream.of(parts).map(Pattern::quote).collect(Collectors.joining(".*")));
    Matcher matcher = pattern.matcher("");
    
    for (File file : files) {
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String text;
            while ((text = reader.readLine()) != null) {
                if (matcher.reset(text).find()) {
                    return text;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    Because BufferedReader implements AutoCloseable, an implicit finally block will be created which essentially does what you were doing: try to close the BufferedReader while suppressing any exception that might arise from the close attempt.