1

Having this data file:

    Test 1
    Test 2 Test 3 Test 11

I would like to have a program to match each 'Test (.)', so I will get the output:

    Match: 1
    Match: 2
    Match: 3
    Match: 11

The program below gives this output:

    Match: 1

The program is:

void addMysteries( Path path, String output) {
    Pattern p = Pattern.compile(".?Test (.)");
    try (Stream<String> lines = Files.lines( path)) {
        lines.map( p::matcher)
                .filter(Matcher::matches)
                .forEach( matcher -> System.out.println("Match: " + matcher.group(1)));
    } catch( Exception e) {
        e.printStackTrace();
    }
}

In Java 7, the software below is working fine. Why is this working and the above not?

pattern = Pattern.compile(".?Test (.)");
input = "Test 1\n\rOtherStuff\nTest 2 Test 3";
matcher = pattern.matcher(input);
while (matcher.find()) {
    System.out.println(  "Match: " + matcher.group( 1));
}   
tm1701
  • 7,307
  • 17
  • 79
  • 168
  • 2
    Why one is working and the other not? One is `find`ing over the whole text, the other using `matches` on each line – user85421 Jun 10 '18 at 19:29

2 Answers2

3

You have to use Matcher for each line, your code should look like this :

try (Stream<String> lines = Files.lines(path)) {
    lines.forEach(line ->
            {
                Matcher matcher = pattern.matcher(line);
                while (matcher.find()){
                    System.out.println("Match: " + matcher.group(1));
                }
            }
    );
} catch (Exception e) {
    e.printStackTrace();
}

Java 9+ features

In Java 9+ you can use Matcher#results() in the second example, so it can be :

Pattern pattern = Pattern.compile(".?Test (.)");
try (Stream<String> lines = Files.lines(path)) {
    lines.forEach(input -> pattern.matcher(input)
            .results()
            .forEach(matcher -> System.out.println("Match: " + matcher.group(1)))
    );
} catch (Exception e) {
    e.printStackTrace();
}

Or as @Holger mention you can use flatMap with Matcher#results() like so :

try (Stream<String> lines = Files.lines(path)) {
    lines.flatMap(input ->
            pattern.matcher(input).results()
    ).forEach(matchResult -> System.out.println("Match: " + matchResult.group(1)));
} catch (IOException e) {
    e.printStackTrace();
}

Or even you can read the whole file using Scanner, where you can use Scanner::findAll which return a Stream like so :

try (Scanner scanner = new Scanner(path)) {
    scanner.findAll(pattern).forEach(matchResult ->
            System.out.println("Match: " + matchResult.group(1))
    );
} catch (IOException e) {
    e.printStackTrace();
}

Outputs

Match: 1
Match: 2
Match: 3

You can take a look at this two back-ports of results() and findAll() by @Holger :

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
  • 1
    Instead of nested `forEach`, you may consider `flatMap`: `lines.flatMap(input -> pattern.matcher(input).results()) .forEach(matchResult -> System.out.println("Match: " + matchResult.group(1)));`. Or even simpler: `try(Scanner scanner = new Scanner(path)) { scanner.findAll(pattern).forEach(matchResult -> System.out.println("Match: " + matchResult.group(1))); }`. Since this is the cleanest solution, I’d suggest either, [this back-port of `results()`](https://stackoverflow.com/a/28150956/2711488) or [this back-port of `findAll()`](https://stackoverflow.com/a/42978216/2711488) for Java 8. – Holger Jun 11 '18 at 15:09
  • When giving this data file, the result is "Test 1" and "Test 1". Not exactly what I expect. BUT - thanks anyway! +1 – tm1701 Jun 11 '18 at 16:25
  • Thank you @Holger for the information, I really learn from this I appreciate it – Youcef LAIDANI Jun 11 '18 at 16:44
  • @tjm1706 I try all the solution I already mention and all returns `Match: 1 Match: 2 Match: 3` can you share the file you test with please? – Youcef LAIDANI Jun 11 '18 at 16:46
  • @YCF_L - thank you for your answer. I've changed the input file and question. – tm1701 Jun 11 '18 at 16:57
  • 1
    @tjm1706 In this case you can just change your regex to `.?Test (\d+)` ;) – Youcef LAIDANI Jun 11 '18 at 16:59
1

It's simple Test 2 Test 3 does not match the regex .?Test (.)

You could add the following flatMap before the map(p::matches) so that you have your input lines splitted and have Test at each beginning of string

.flatMap(x -> Arrays.stream(x.split(".?(?=Test)")))

Complete Code

void addMysteries( Path path, String output) {
    Pattern p = Pattern.compile(".?Test (.)");
    try (Stream<String> lines = Files.lines( path)) {
        lines.flatMap(x -> Arrays.stream(x.split(".?(?=Test)")))
                .map( p::matcher)
                .filter(Matcher::matches)
                .forEach( matcher -> System.out.println("Match: " + matcher.group(1)));
    } catch( Exception e) {
        e.printStackTrace();
    }
}
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89