Regex - Find words of certain length between two specified words

Viewed 96

Good day,

I have recently started working with regex (in java) and have stumbled onto a problem I require some assistance/guidance.

I am looking to find words of a certain length (in this case 4 characters long or more) between two words jack and james.

The following is the text I am using to test my regex against.

james was playing with jack yesterday (line 1)
jack was playing with james yersterday (line 2)
jack and james are best friends (line 3)
james will be helping jack with his homework (line 4)
yesterday, james come over jack's house (line 5)

What I hope to achieve is the following

playing with(line 1)
playing with(line 2)
no matches(line 3)
will helping(line 4)
come over(line 5)

I have come up with the following

(?<=james)(.*)(?=jack)|(?<=jack)(.*)(?=james)

But this particular regex, returns all characters between the two words. I also tried the following unsuccessfully (as well as many others before frustration started taking over). Also, I omitted

(?<=james)(\\b\w{4,}\\b)(?=jack)|(?<=jack)(\\b\w{4,}\\b)(?=james)

Any guidance would be greatly appreciated.

Sincerely

2 Answers

This seems to work as required.

  • (?<=) positive look behind for the two names
  • (?=) positve look ahead for the two names.
  • \\w{4,} a word of more than three characters
  • .* used to gobble up the chars between the two zero width assertions.
String[] lines =  {"james was playing with jack yesterday (line 1)",
    "jack was playing with james yersterday (line 2)",
    "jack and james are best friends (line 3)",
    "james will be helping jack with his homework (line 4)",
    "yesterday, james come over jack's house (line 5)"};

Pattern p = Pattern.compile("(?<=(?:jack|james).*)(\\w{4,})(?=.*(?:jack|james))");

for (String line : lines) {
      Matcher m = p.matcher(line);
      // a flag for printing a new line.     
      boolean flag = false;
      while(m.find()) {
          flag = true;
          System.out.print(m.group(1) + " " );
      }
      if (flag) {
          System.out.println();
      }
}

Prints

playing with 
playing with 
will helping 
come over 

Use

(?:\G(?<!^)|(jack|james))(?:\W+\w{1,3})*\W+(\w{4,})(?=(?:(?!\1).)*?(?!\1)(jack|james))

See proof. You need the values that are held inside Group 2.

Explanation

                         EXPLANATION
--------------------------------------------------------------------------------
  (?:                      group, but do not capture:
--------------------------------------------------------------------------------
    \G                       where the last m//g left off
--------------------------------------------------------------------------------
    (?<!                     look behind to see if there is not:
--------------------------------------------------------------------------------
      ^                        the beginning of the string
--------------------------------------------------------------------------------
    )                        end of look-behind
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    (                        group and capture to \1:
--------------------------------------------------------------------------------
      jack                     'jack'
--------------------------------------------------------------------------------
     |                        OR
--------------------------------------------------------------------------------
      james                    'james'
--------------------------------------------------------------------------------
    )                        end of \1
--------------------------------------------------------------------------------
  )                        end of grouping
--------------------------------------------------------------------------------
  (?:                      group, but do not capture (0 or more times
                           (matching the most amount possible)):
--------------------------------------------------------------------------------
    \W+                      non-word characters (all but a-z, A-Z, 0-
                             9, _) (1 or more times (matching the
                             most amount possible))
--------------------------------------------------------------------------------
    \w{1,3}                  word characters (a-z, A-Z, 0-9, _)
                             (between 1 and 3 times (matching the
                             most amount possible))
--------------------------------------------------------------------------------
  )*                       end of grouping
--------------------------------------------------------------------------------
  \W+                      non-word characters (all but a-z, A-Z, 0-
                           9, _) (1 or more times (matching the most
                           amount possible))
--------------------------------------------------------------------------------
  (                        group and capture to \2:
--------------------------------------------------------------------------------
    \w{4,}                   word characters (a-z, A-Z, 0-9, _) (at
                             least 4 times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \2
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the least amount
                             possible)):
--------------------------------------------------------------------------------
      (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
        \1                       what was matched by capture \1
--------------------------------------------------------------------------------
      )                        end of look-ahead
--------------------------------------------------------------------------------
      .                        any character except \n
--------------------------------------------------------------------------------
    )*?                      end of grouping
--------------------------------------------------------------------------------
    (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
      \1                       what was matched by capture \1
--------------------------------------------------------------------------------
    )                        end of look-ahead
--------------------------------------------------------------------------------
    (                        group and capture to \3:
--------------------------------------------------------------------------------
      jack                     'jack'
--------------------------------------------------------------------------------
     |                        OR
--------------------------------------------------------------------------------
      james                    'james'
--------------------------------------------------------------------------------
    )                        end of \3
--------------------------------------------------------------------------------
  )                        end of look-ahead

Java:

import java.util.*;
import java.util.regex.*;
import java.lang.*;
import java.io.*;

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        String regex = "(?:\\G(?<!^)|(jack|james))(?:\\W+\\w{1,3})*\\W+(\\w{4,})(?=(?:(?!\\1).)*?(?!\\1)(jack|james))";
        String string = "james was playing with jack yesterday";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(string);
        List<String> results = new ArrayList<>();
        while (matcher.find()) {
            results.add(matcher.group(2));
        }
        System.out.println(String.join(" ", results));
    }
}

Result: playing with

Related