hahahakebab
hahahakebab

Reputation: 326

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.

Upvotes: 0

Views: 1440

Answers (1)

VGR
VGR

Reputation: 44404

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.

Upvotes: 1

Related Questions