Java regex confusion - java

I have the following (Java) code:
public class TestBlah {
private static final String PATTERN = ".*\\$\\{[.a-zA-Z0-9]+\\}.*";
public static void main(String[] s) throws IOException {
String st = "foo ${bar}\n";
System.out.println(st.matches(PATTERN));
System.out.println(Pattern.compile(PATTERN).matcher(st).find());
System.exit(0);
}
}
Running this code, the former System.out.println outputs false, while the latter outputs true
Am I not understanding something here?

This is because the . will not match the new line character. Thus, your String that contains a new line, will not match a string that ends with .*. So, when you call matches(), it returns false, because the new line doesn't match.
The second one returns true because it finds a match inside the input string. It doesn't necessarily match the whole string.
From the Pattern javadocs:
. Any character (may or may not match line terminators)

String.matches(..) behaves like Matcher.matches(..). From the documentation of Matcher
find(): Attempts to find the next subsequence of
the input sequence that matches the pattern.
matches(): Attempts to match the entire input sequence
against the pattern.
So you could think of matches() as if it surrounded your regexp with ^ and $ to make sure the beginning of the string matches the beginning of your regular expression and the end of the string matches the end of the regular expression.

There is a difference between matching a pattern and finding the pattern in a String
String.matches() :
Tells whether or not this string matches the given regular expression.
Your whole string must match the pattern.
Matcher.matches() :
Attempts to match the entire input sequence against the pattern.
Again your whole string must match.
Matcher.find() :
Attempts to find the next subsequence of the input sequence that matches the pattern.
Here you only need a "partial match".
As #Justin said :
Your matches() can't work as the . won't match new line characters (\n, \r and \r\n).
Resources :
Javadoc - String.matches()
Javadoc - Matcher.matches()
Javadoc - Matcher.find()

Related

Regular expressions in multi-line text code in Java [duplicate]

I am trying to match a multi line text using java. When I use the Pattern class with the Pattern.MULTILINE modifier, I am able to match, but I am not able to do so with (?m).
The same pattern with (?m) and using String.matches does not seem to work.
I am sure I am missing something, but no idea what. Am not very good at regular expressions.
This is what I tried
String test = "User Comments: This is \t a\ta \n test \n\n message \n";
String pattern1 = "User Comments: (\\W)*(\\S)*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find()); //true
String pattern2 = "(?m)User Comments: (\\W)*(\\S)*";
System.out.println(test.matches(pattern2)); //false - why?
First, you're using the modifiers under an incorrect assumption.
Pattern.MULTILINE or (?m) tells Java to accept the anchors ^ and $ to match at the start and end of each line (otherwise they only match at the start/end of the entire string).
Pattern.DOTALL or (?s) tells Java to allow the dot to match newline characters, too.
Second, in your case, the regex fails because you're using the matches() method which expects the regex to match the entire string - which of course doesn't work since there are some characters left after (\\W)*(\\S)* have matched.
So if you're simply looking for a string that starts with User Comments:, use the regex
^\s*User Comments:\s*(.*)
with the Pattern.DOTALL option:
Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL);
Matcher regexMatcher = regex.matcher(subjectString);
if (regexMatcher.find()) {
ResultString = regexMatcher.group(1);
}
ResultString will then contain the text after User Comments:
This has nothing to do with the MULTILINE flag; what you're seeing is the difference between the find() and matches() methods. find() succeeds if a match can be found anywhere in the target string, while matches() expects the regex to match the entire string.
Pattern p = Pattern.compile("xyz");
Matcher m = p.matcher("123xyzabc");
System.out.println(m.find()); // true
System.out.println(m.matches()); // false
Matcher m = p.matcher("xyz");
System.out.println(m.matches()); // true
Furthermore, MULTILINE doesn't mean what you think it does. Many people seem to jump to the conclusion that you have to use that flag if your target string contains newlines--that is, if it contains multiple logical lines. I've seen several answers here on SO to that effect, but in fact, all that flag does is change the behavior of the anchors, ^ and $.
Normally ^ matches the very beginning of the target string, and $ matches the very end (or before a newline at the end, but we'll leave that aside for now). But if the string contains newlines, you can choose for ^ and $ to match at the start and end of any logical line, not just the start and end of the whole string, by setting the MULTILINE flag.
So forget about what MULTILINE means and just remember what it does: changes the behavior of the ^ and $ anchors. DOTALL mode was originally called "single-line" (and still is in some flavors, including Perl and .NET), and it has always caused similar confusion. We're fortunate that the Java devs went with the more descriptive name in that case, but there was no reasonable alternative for "multiline" mode.
In Perl, where all this madness started, they've admitted their mistake and gotten rid of both "multiline" and "single-line" modes in Perl 6 regexes. In another twenty years, maybe the rest of the world will have followed suit.
str.matches(regex) behaves like Pattern.matches(regex, str) which attempts to match the entire input sequence against the pattern and returns
true if, and only if, the entire input sequence matches this matcher's pattern
Whereas matcher.find() attempts to find the next subsequence of the input sequence that matches the pattern and returns
true if, and only if, a subsequence of the input sequence matches this matcher's pattern
Thus the problem is with the regex. Try the following.
String test = "User Comments: This is \t a\ta \ntest\n\n message \n";
String pattern1 = "User Comments: [\\s\\S]*^test$[\\s\\S]*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find()); //true
String pattern2 = "(?m)User Comments: [\\s\\S]*^test$[\\s\\S]*";
System.out.println(test.matches(pattern2)); //true
Thus in short, the (\\W)*(\\S)* portion in your first regex matches an empty string as * means zero or more occurrences and the real matched string is User Comments: and not the whole string as you'd expect. The second one fails as it tries to match the whole string but it can't as \\W matches a non word character, ie [^a-zA-Z0-9_] and the first character is T, a word character.
The multiline flag tells regex to match the pattern to each line as opposed to the entire string for your purposes a wild card will suffice.

Regex first character not matching

I am having some Java Pattern problems. This is my pattern:
"^[\\p{L}\\p{Digit}~._-]+$"
It matches any letter of the US-ASCII, numerals, some special characters, basically anything that wouldn't scramble an URL.
What I would like is to find the first letter in a word that does not match this pattern. Basically the user sends a text as an input and I have to validate it and to throw an exception if I find an illegal character.
I tried negating this pattern, but it wouldn't compile properly. Also find() didn't help out much.
A legal input would be hello while ?hello should not be, and my exception should point out that ? is not proper.
I would prefer a suggestion using Java's Matcher, Pattern or something using util.regex. Its not a necessity, but checking each character in the string individually is not a solution.
Edit: I came up with a better regex to match unreserved URI characters
Try this :
^[\\p{L}\\p{Digit}.'-.'_]*([^\\p{L}\\p{Digit}.'-.'_]).*$
The first character non matching is the group n°1
I made a few try here : http://fiddle.re/gkkzm61
Explanation :
I negate your pattern, so i built this :
[^\\p{L}\\p{Digit}.'-.'_] [^...] means every character except for
^ ^ the following ones.
| your pattern inside |
The pattern has 3 parts :
^[\\p{L}\\p{Digit}.'-.'_]*
Checks the regex from the first character until he meets a non matching character
([^\\p{L}\\p{Digit}.'-.'_])
The non-matching character (negation) inside a capturing group
.*$
Any character until the end of the string.
Hope it helps you
EDIT :
The correct regex shoud be :
^[\\p{L}\\p{Digit}~._-]*([^\\p{L}\\p{Digit}~._-]).*$
It is the same method, i only change the contents of the first and second part.
I tried and it seems to work.
The "^[\\p{L}\\p{Digit}.'-.'_]+$" pattern matches any string containing 1+ characters defined inside the character class. Note that double ' and . are suspicious and you might be unaware of the fact that '-. creates a range and matches '()*+,-.. If it is not on purpose, I think you meant to use .'_-.
To check if a string starts with a character other than the one defined in the character class, you can negated the character class, and check the first character in the string only:
if (str.matches("[^\\p{L}\\p{Digit}.'_-].*")) {
/* String starts with the disallowed character */
}
I also think you can shorten the regex to "(?U)[^\\w.'-].*". At any rate, \\p{Digit} can be replaced with \\d.
Try out this one to find the first non valid char:
Pattern negPattern = Pattern.compile(".*?([^\\p{L}^\\p{Digit}^.^'-.'^_]+).*");
Matcher matcher = negPattern.matcher("hel?lo");
if (matcher.matches())
{
System.out.println("'" + matcher.group(1).charAt(0) + "'");
}

Why won't this string regex match?

I have a string and a simple pattern (a string with a wildcard). When I use the match function I would it expect it to return true for my text, but it doesn't it returns false.
String text = "test_1_2_3";
String pattern = "test_*"
text.matches(pattern);//this returns false
_* will matches the character _ literally between zero and more times ,instead you need .* that match any character between zero and more times:
"test_.*"
Demo
pattern = "test_*" means "test" and 0 or more "_"
Because your test_* pattern, combined with Matcher#matches, will match a whole input (i.e. from start to end), that matches the following conditions:
starts with test
followed by (and ending with) 0 instance of _, or more (greedy-quantified here).
Using Matcher#find would return true in this case, since it would match a partial test_.
So, your matches invocation would return true with the given Pattern, with inputs such as:
test_
test__
... and so on.
See API.
Your regexp will match test followed by zero or more '_' character.
I think you want this:
String text = "test_1_2_3";
String pattern = "test_.*";

How to match a string's end using a regex pattern in Java?

I want a regular expression pattern that will match with the end of a string.
I'm implementing a stemming algorithm that will remove suffixes of a word.
E.g. for a word 'Developers' it should match 's'.
I can do it using following code :
Pattern p = Pattern.compile("s");
Matcher m = p.matcher("Developers");
m.replaceAll(" "); // it will replace all 's' with ' '
I want a regular expression that will match only a string's end something like replaceLast().
You need to match "s", but only if it is the last character in a word. This is achieved with the boundary assertion $:
input.replaceAll("s$", " ");
If you enhance the regular expression, you can replace multiple suffixes with one call to replaceAll:
input.replaceAll("(ed|s)$", " ");
Use $:
Pattern p = Pattern.compile("s$");
public static void main(String[] args)
{
String message = "hi this message is a test message";
message = message.replaceAll("message$", "email");
System.out.println(message);
}
Check this,
http://docs.oracle.com/javase/tutorial/essential/regex/bounds.html
When matching a character at the end of string, mind that the $ anchor matches either the very end of string or the position before the final line break char if it is present even when the Pattern.MULTILINE option is not used.
That is why it is safer to use \z as the very end of string anchor in a Java regex.
For example:
Pattern p = Pattern.compile("s\\z");
will match s at the end of string.
See a related Whats the difference between \z and \Z in a regular expression and when and how do I use it? post.
NOTE: Do not use zero-length patterns with \z or $ after them because String.replaceAll(regex) makes the same replacement twice in that case. That is, do not use input.replaceAll("s*\\z", " ");, since you will get two spaces at the end, not one. Either use "s\\z" to replace one s, or use "s+\\z" to replace one or more.
If you still want to use replaceAll with a zero-length pattern anchored at the end of string to replace with a single occurrence of the replacement, you can use a workaround similar to the one in the How to make a regular expression for this seemingly simple case? post (writing "a regular expression that works with String replaceAll() to remove zero or more spaces from the end of a line and replace them with a single period (.)").

How to find the exact word using a regex in Java?

Consider the following code snippet:
String input = "Print this";
System.out.println(input.matches("\\bthis\\b"));
Output
false
What could be possibly wrong with this approach? If it is wrong, then what is the right solution to find the exact word match?
PS: I have found a variety of similar questions here but none of them provide the solution I am looking for.
Thanks in advance.
When you use the matches() method, it is trying to match the entire input. In your example, the input "Print this" doesn't match the pattern because the word "Print" isn't matched.
So you need to add something to the regex to match the initial part of the string, e.g.
.*\\bthis\\b
And if you want to allow extra text at the end of the line too:
.*\\bthis\\b.*
Alternatively, use a Matcher object and use Matcher.find() to find matches within the input string:
Pattern p = Pattern.compile("\\bthis\\b");
Matcher m = p.matcher("Print this");
m.find();
System.out.println(m.group());
Output:
this
If you want to find multiple matches in a line, you can call find() and group() repeatedly to extract them all.
Full example method for matcher:
public static String REGEX_FIND_WORD="(?i).*?\\b%s\\b.*?";
public static boolean containsWord(String text, String word) {
String regex=String.format(REGEX_FIND_WORD, Pattern.quote(word));
return text.matches(regex);
}
Explain:
(?i) - ignorecase
.*? - allow (optionally) any characters before
\b - word boundary
%s - variable to be changed by String.format (quoted to avoid regex
errors)
\b - word boundary
.*? - allow (optionally) any characters after
For a good explanation, see: http://www.regular-expressions.info/java.html
myString.matches("regex") returns true or false depending whether the
string can be matched entirely by the regular expression. It is
important to remember that String.matches() only returns true if the
entire string can be matched. In other words: "regex" is applied as if
you had written "^regex$" with start and end of string anchors. This
is different from most other regex libraries, where the "quick match
test" method returns true if the regex can be matched anywhere in the
string. If myString is abc then myString.matches("bc") returns false.
bc matches abc, but ^bc$ (which is really being used here) does not.
This writes "true":
String input = "Print this";
System.out.println(input.matches(".*\\bthis\\b"));
You may use groups to find the exact word. Regex API specifies groups by parentheses. For example:
A(B(C))D
This statement consists of three groups, which are indexed from 0.
0th group - ABCD
1st group - BC
2nd group - C
So if you need to find some specific word, you may use two methods in Matcher class such as: find() to find statement specified by regex, and then get a String object specified by its group number:
String statement = "Hello, my beautiful world";
Pattern pattern = Pattern.compile("Hello, my (\\w+).*");
Matcher m = pattern.matcher(statement);
m.find();
System.out.println(m.group(1));
The above code result will be "beautiful"
Is your searchString going to be regular expression? if not simply use String.contains(CharSequence s)
System.out.println(input.matches(".*\\bthis$"));
Also works. Here the .* matches anything before the space and then this is matched to be word in the end.

Categories