How to make a regex to see if string contains a certain letter

Viewed 248

On a website I found some alternatives to "The quick brown fox jumps over the lazy dog" and I decided to write a little program to check if the alternatives were valid.

For those interested, I wrote the following program (using the filereader idea of this post) that checks a file with the sentences under each other:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TestClass {
    public static void main(String... aArgs)  {
        TestClass tc = new TestClass();
        try {
            String[] pieces = tc.splitFile("/home/user2609980/Desktop/text");
            for (String line : pieces) {
                if (line.contains("a") &&
                        line.contains("b") &&
                        line.contains("c") &&
                        line.contains("d") &&
                        line.contains("e") &&
                        line.contains("f") &&
                        line.contains("g") &&
                        line.contains("h") &&
                        line.contains("i") &&
                        line.contains("j") &&
                        line.contains("k") &&
                        line.contains("l") &&
                        line.contains("m") &&
                        line.contains("n") &&
                        line.contains("o") &&
                        line.contains("p") &&
                        line.contains("q") &&
                        line.contains("r") &&
                        line.contains("s") &&
                        line.contains("t") &&
                        line.contains("u") &&
                        line.contains("v") &&
                        line.contains("w") &&
                        line.contains("x") &&
                        line.contains("y") &&
                        line.contains("z")) {
                    System.out.println("Matches: " + line);
                } else {
                    System.out.println("Does not match: " + line);
                }
            }

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }

    public String[] splitFile(String file) throws IOException {

        BufferedReader br = new BufferedReader(new FileReader(file));
        try {
            StringBuilder sb = new StringBuilder();
            String line = br.readLine();

            while (line != null) {
                sb.append(line);
                sb.append('\n');
                line = br.readLine();
            }
            String everything = sb.toString();
            String[] pieces = everything.split("\n");
            return pieces;
        } finally {
            br.close();
        }

    }
}

And this was the output:

Matches: The quick brown fox jumps over the lazy dog
Does not match: Pack my box with five dozen liquor jugs.
Matches: Several fabulous dixieland jazz groups played with quick tempo.
Does not match: Back in my quaint garden, jaunty zinnias vie with flaunting phlox.
Does not match: Five or six big jet planes zoomed quickly by the new tower.
Matches: Exploring the zoo, we saw every kangaroo jump and quite a few carried babies.
Matches: I quickly explained that many big jobs involve few hazards.
Does not match: Jay Wolf is quite an expert on the bass violin, guitar, dulcimer, ukulele and zither.
Matches: Expect skilled signwriters to use many jazzy, quaint old alphabets effectively.
Matches: The wizard quickly jinxed the gnomes before they vaporized.

I want to improve this program in two ways. One, and that is my question, is how to make a more efficient piece of code instead of checking for each letter of the alphabet seperately. How can I make something like:

line.Contains([regex])

if that is possible?

The bonus question is how I can make this program so that it prints out exactly where it does not match. Of course I can do an if-else for every letter, but I hope there is a prettier way.

Thanks for your attention and I look forward to read your possible response.

2 Answers

Simplest in my opinion is to use a loop like this:

boolean allChars = true;
String uline = line.toUpperCase();
for (char c='A'; c<='Z'; c++) {
    if (uline.indexOf(c) < 0) {
        allChars = false;
        break;
    }
}

i.e. run a loop from 65 (A) to 90 (Z) and check existence of each character in the input string.

Here's a solution that is O(n) that attempts to be faster by avoiding the loop within a loop. However, performance tests indicate that this is not really worth it in this case.

Note that I was mistaken, when I assumed your code was O(n2). It is not, even though you do have a loop within a loop. That's because the outer loop iterates over a constant number (26 letters)

Map<char, boolean> letters = new HashMap<String,boolean>
String uline = line.toUpperCase();

for (int i=0, i < uline.length; i++) {
    letters.put(uline.charAt(i), true );
}


boolean allChars = true;
for (char c='A'; c<='Z'; c++) {
if (letters.get(c) == null) {
   allChars = false;
   break;
}

If you want a regex that represents an AND operation, you can mimic it with positive lookahead assertions, but I have a feeling it's going to be slow. See https://stackoverflow.com/a/470602/227299

(?=.*a)(?=.*b)(?=.*c)(?=.*d)(?=.*e)(?=.*f)(?=.*g)(?=.*h)(?=.*i)(?=.*j)(?=.*k)(?=.*l)(?=.*m)(?=.*n)(?=.*o)(?=.*p)(?=.*q)(?=.*r)(?=.*s)(?=.*t)(?=.*u)(?=.*v)(?=.*w)(?=.*x)(?=.*y)(?=.*z)

Be sure to use the case insensitive modifier

See it in action http://regex101.com/r/yJ4cU6

I created some performance tests to see if it made sense to use my suggested approach and it doesn't. I would stick to what anubhava suggested. Hopefully, the answer helps you think about performance (and premature optimization).

Related