Regex \P{IsHan} does not work well in Java 8 - java

I want to remove all non-Chinese characters in a String, and retain Chinese Characters.
Here is an example:
input -> 勇𣌀hi你好👋()【】「」{}[]()
output -> 勇𣌀你好
First of all, I try to extract all Chinese characters and append each to a StringBuilder using the following code:
public static String extractAllChineseCharacters(String input){
Matcher matcher = Pattern.compile("\\p{IsHan}").matcher(input);
StringBuilder output = new StringBuilder();
while(matcher.find()){
output.append(input.substring(matcher.start(),matcher.end()));
}
return output.toString();
}
The code works well, but i want a more concise code.
Then i try to replace all non-Chinese character with "" using the following code
public static String replaceCharacters(String input){
Matcher matcher = Pattern.compile("\\P{IsHan}").matcher(input);
return matcher.replaceAll("");
}
but the code doesn't work as i expect.
When the input is 勇𣌀hi你好👋()【】「」{}[](),
the output is ??你好
i enter the debug mode, and find out the output's charSequences is \uD87E\uD84e你好
i know that java adopt UTF-16 to store a codeunit in a char variable, so 勇(U+2F825)is presents as a surrogate pair \uD87E\uDC25 in charSequence,and 𣌀(U+23300)is presents as \uD84C\uDF00
My question is:
the regex pattern "\p{IsHan}"can match \uD87E\uDC25 two chars as a Chinese character "勇",but the regex pattern "\P{IsHan}" can‘t precisely match non-Chinese character.Why is that so?
Can anyone give some help? Thanks in advance!

The use of \p{IsHan} works in Java 8 - see below. Perhaps the input is encoded is a way that makes the matching fail?
Regarding "The code works well, but I want a more concise code", rather than match what you want, match what you don't want and remove it:
public static String extractAllChineseCharacters(String input) {
return input.replaceAll("\\P{IsHan}", "");
}
See live demo running Java 8.
FYI:
\p{IsHan}" matches any Chinese character
\P{IsHan}" matches any non Chinese character.
Test:
String input = "\uD87E\uDC25\uD84C\uDF00hi你好\uD83D\uDC4B()【】「」{}[]()";
System.out.println(input);
System.out.println(input.replaceAll("\\P{IsHan}", ""));
Output:
勇𣌀hi你好👋()【】「」{}[]()
勇𣌀你好

Related

Regex Java when we have specific text upto a pattern

As i haven't much worked on regex, can someone help me out in getting the answer for below thing:
(1)I want to remove a text say Element
(2)It may of may not followed by delimiter say pipe(||)
I tried below thing, but it is not working in the way i want:
String str = "String:abc||Element:abc||Value:abc"; // Sample text 1
String str1 = "String:abc||Element:abc"; // Sample text 2
System.out.println(str.replaceFirst("Element.*\\||", ""));
System.out.println(str1.replaceFirst("Element.*\\||", ""));
Required output in above cases:
String:abc||Value:abc //for the first case
String:abc //for the second case
Assuming that you can decide to give another value to the original pattern which is Element in this case, you can use Pattern.quote to escape it as below:
String str = "String:abc||Element:abc||Value:abc"; // Sample text 1
String str1 = "String:abc||Element:abc"; // Sample text 2
String originalPattern = "Element";
String pattern = String.format("\\|{2}%s[^\\|]+", Pattern.quote(originalPattern));
System.out.println(str.replaceFirst(pattern, ""));
System.out.println(str1.replaceFirst(pattern, ""));
Your patter is then generic and its value is String.format("\\|{2}%s[^\\|]+", Pattern.quote(originalPattern))
Output:
String:abc||Value:abc
String:abc
You put the escape wrong. It should be:
Element(.*?\|\||.*$)
Put the escape on each pipe, and use ? for non greedy Regex so you only replace just enough string, not everything.
String text = "String:abc||Element:abc||Value:abc";
text = text.replaceAll("\\belement\\b", "");
you might need to use replace all this will replace all element from your string here i am using '\b' word boundary in java regular expression in between the words

Word not preceded by a regular expression

There are plenty of these questions but they all focus on having a couple of characters.
In a text file i have TXX and txx and i need to find those. But I also have Base64 encoded pictures.
Meaning I have
"picture":"/9j/4AAQSkTXX . . .
Basically TXX, txx can appear randomly in Base64-encoded pictures.
I used the following regular expression:
(?<!"picture":")(?:(\w|\/|\+)+)(TXX|txx)
I also realized it should probably be changed into:
(?<!"picture":")(?:(\d|\w|\/|\+|\=)+)(TXX|txx)
But it says I'm doing a catastrophic backtracking, and even without the (?:) (non-capturing group) it still doesn't work. Basically it just doesn't take the "picture":" and the first char and takes everything else.
Since I cannot put a regular expression inside the negative look-behind with a quantifier like
(?<!"picture":".+)TXX|txx
How should I form that regular expression so that these pass
"something-txx": "somerandomstring"
value not picture: "some other stringtxxsome string"
But this doesn't
"picture":"txxl5l71JGwnxMXAmJGOt8ZPwN24JNgtZpYHPBQLTViqVatk4ZoZhY+husj7Pgv3ag4NmpJ4CBlXudzydA5c+5QecmgaPz9vLrSbzRa+tNns0GjUfD+NSa5ZHo9KRf2nCWLl7360x2Kx8zA6dquNqubjoElpVRo2Dq0GOmZ8HMycktxxH08veKg84OPlCZvdDqvNxkPhOB0sn5wly+vdgx1Di82KzMxMlAoJQZkSJdGjZ0+UrlCJi/Xysc5GCPETtxxgUAgEAieNoQQLygg/P8K8VLaFCVVez+/SfMmPo74sNyxGz+/0YI8QKBQCAQCP4DPG6MeLrZcQvihFar46L6govdPE69movlMhIPh0NYaRJTtu2e+FQWyPkqDSsLqker0fKJVR0Oe5ap1RqoWD+pfuo7hefhbVJcfA8VlK42ycudJlIlMd1iMrnakePok5BPDyoUSvnhBMsEs9XMQ+PYrDQRqwd0Oj2vh/eVleXj5OMF7BSqhq2YjEa2TQ83nNDrPeHp5YWQEmXg4+vPPeLzIoR4gUAgEAcvvgETxtCiBcI/ifY2Y2aA57eWu7lJBAIBAKBQCB4eP62EC/JYWmoPBnFeieRnGKnk7e3yWTiYjN5fZPYLId5kcV67sHtcLBt+vZG4VzIu93lVe8SqUmsdzpsrDz7jse2tZrs+O/kxc7z5oGE/PtB+XOWs7tCtpB4z9NIkGf9YU3JeSmb0yV422np5AI8eaTXX"
Sample input is on :
http://pastebin.com/5XJVNqGS
(I know pastebin is bad since the expiration but i'm having problem pasting that amount of text as the page stucks)
And the results should be:
Result1: "some-txx": value
Result2: hereisTXX: "1235"
Result3: "GROUPDATA" : "{DATA1: sample, TXX-value:12312 ,DATA2: sample2}"
I believe you can use a rather useful Java "to-some-extent" variable-width look-behind:
(?<!"picture":"[^"]{0,10000})(?i:txx)
You can adjust the 10000 value in case you have longer Base64-encoded strings.
Tested on RegexPlanet
In case you have very large images, use a reverse-string trick with a reversed regex (look-aheads can be of undefined variable size):
String rx = "(?i)\"[^\"]*\"\\s*:\\s*\"[^\"]*xxt[^\"]*\"(?![^\"]*\":\"erutcip\")";
Sample Java program on Ideone:
import java.util.regex.*;
class HelloWorld{
public static void main(String []args){
String str = "THE_HUIGE_STRING_THAT_CAUSED_Body is limited to 30000 characters;you entered 53501_ISSUE";
str = new StringBuilder(str).reverse().toString();
String rx = "\"?[^\"]*\"?\\s*\"?[^\"\\n\\r]*(?:xxt|XXT)[^\"\\n\\r]*(?![^\"]*\":\"erutcip\")";
Pattern ptrn = Pattern.compile(rx);
Matcher m = ptrn.matcher(str);
while (m.find()) {
System.out.println(new StringBuilder(m.group(0)).reverse().toString());
}
m = ptrn.matcher(new StringBuilder("\"something-txx\": \"somerandomstring\"").reverse().toString());
while (m.find()) {
System.out.println(new StringBuilder(m.group(0)).reverse().toString());
}
}
}

Java Regex is including new line in match

I'm trying to match a regular expression to textbook definitions that I get from a website.
The definition always has the word with a new line followed by the definition. For example:
Zither
Definition: An instrument of music used in Austria and Germany It has from thirty to forty wires strung across a shallow sounding board which lies horizontally on a table before the performer who uses both hands in playing on it Not to be confounded with the old lute shaped cittern or cithern
In my attempts to get just the word (in this case "Zither") I keep getting the newline character.
I tried both ^(\w+)\s and ^(\S+)\s without much luck. I thought that maybe ^(\S+)$ would work, but that doesn't seem to successfully match the word at all. I've been testing with rubular, http://rubular.com/r/LPEHCnS0ri; which seems to successfully match all my attempts the way I want, despite the fact that Java doesn't.
Here's my snippet
String str = ...; //Here the string is assigned a word and definition taken from the internet like given in the example above.
Pattern rgx = Pattern.compile("^(\\S+)$");
Matcher mtch = rgx.matcher(str);
if (mtch.find()) {
String result = mtch.group();
terms.add(new SearchTerm(result, System.nanoTime()));
}
This is easily solved by triming the resulting string, but that seems like it should be unnecessary if I'm already using a regular expression.
All help is greatly appreciated. Thanks in advance!
Try using the Pattern.MULTILINE option
Pattern rgx = Pattern.compile("^(\\S+)$", Pattern.MULTILINE);
This causes the regex to recognise line delimiters in your string, otherwise ^ and $ just match the start and end of the string.
Although it makes no difference for this pattern, the Matcher.group() method returns the entire match, whereas the Matcher.group(int) method returns the match of the particular capture group (...) based on the number you specify. Your pattern specifies one capture group which is what you want captured. If you'd included \s in your Pattern as you wrote you tried, then Matcher.group() would have included that whitespace in its return value.
With regular expressions the first group is always the complete matching string. In your case you want group 1, not group 0.
So changing mtch.group() to mtch.group(1) should do the trick:
String str = ...; //Here the string is assigned a word and definition taken from the internet like given in the example above.
Pattern rgx = Pattern.compile("^(\\w+)\s");
Matcher mtch = rgx.matcher(str);
if (mtch.find()) {
String result = mtch.group(1);
terms.add(new SearchTerm(result, System.nanoTime()));
}
A late response, but if you are not using Pattern and Matcher, you can use this alternative of DOTALL in your regex string
(?s)[Your Expression]
Basically (?s) also tells dot to match all characters, including line breaks
Detailed information: http://www.vogella.com/tutorials/JavaRegularExpressions/article.html
Just replace:
String result = mtch.group();
By:
String result = mtch.group(1);
This will limit your output to the contents of the capturing group (e.g. (\\w+)) .
Try the next:
/* The regex pattern: ^(\w+)\r?\n(.*)$ */
private static final REGEX_PATTERN =
Pattern.compile("^(\\w+)\\r?\\n(.*)$");
public static void main(String[] args) {
String input = "Zither\n Definition: An instrument of music";
System.out.println(
REGEX_PATTERN.matcher(input).matches()
); // prints "true"
System.out.println(
REGEX_PATTERN.matcher(input).replaceFirst("$1 = $2")
); // prints "Zither = Definition: An instrument of music"
System.out.println(
REGEX_PATTERN.matcher(input).replaceFirst("$1")
); // prints "Zither"
}

How to remove special characters from a string?

I want to remove special characters like:
- + ^ . : ,
from an String using Java.
That depends on what you define as special characters, but try replaceAll(...):
String result = yourString.replaceAll("[-+.^:,]","");
Note that the ^ character must not be the first one in the list, since you'd then either have to escape it or it would mean "any but these characters".
Another note: the - character needs to be the first or last one on the list, otherwise you'd have to escape it or it would define a range ( e.g. :-, would mean "all characters in the range : to ,).
So, in order to keep consistency and not depend on character positioning, you might want to escape all those characters that have a special meaning in regular expressions (the following list is not complete, so be aware of other characters like (, {, $ etc.):
String result = yourString.replaceAll("[\\-\\+\\.\\^:,]","");
If you want to get rid of all punctuation and symbols, try this regex: \p{P}\p{S} (keep in mind that in Java strings you'd have to escape back slashes: "\\p{P}\\p{S}").
A third way could be something like this, if you can exactly define what should be left in your string:
String result = yourString.replaceAll("[^\\w\\s]","");
This means: replace everything that is not a word character (a-z in any case, 0-9 or _) or whitespace.
Edit: please note that there are a couple of other patterns that might prove helpful. However, I can't explain them all, so have a look at the reference section of regular-expressions.info.
Here's less restrictive alternative to the "define allowed characters" approach, as suggested by Ray:
String result = yourString.replaceAll("[^\\p{L}\\p{Z}]","");
The regex matches everything that is not a letter in any language and not a separator (whitespace, linebreak etc.). Note that you can't use [\P{L}\P{Z}] (upper case P means not having that property), since that would mean "everything that is not a letter or not whitespace", which almost matches everything, since letters are not whitespace and vice versa.
Additional information on Unicode
Some unicode characters seem to cause problems due to different possible ways to encode them (as a single code point or a combination of code points). Please refer to regular-expressions.info for more information.
This will replace all the characters except alphanumeric
replaceAll("[^A-Za-z0-9]","");
As described here
http://developer.android.com/reference/java/util/regex/Pattern.html
Patterns are compiled regular expressions. In many cases, convenience methods such as String.matches, String.replaceAll and String.split will be preferable, but if you need to do a lot of work with the same regular expression, it may be more efficient to compile it once and reuse it. The Pattern class and its companion, Matcher, also offer more functionality than the small amount exposed by String.
public class RegularExpressionTest {
public static void main(String[] args) {
System.out.println("String is = "+getOnlyStrings("!&(*^*(^(+one(&(^()(*)(*&^%$##!#$%^&*()("));
System.out.println("Number is = "+getOnlyDigits("&(*^*(^(+91-&*9hi-639-0097(&(^("));
}
public static String getOnlyDigits(String s) {
Pattern pattern = Pattern.compile("[^0-9]");
Matcher matcher = pattern.matcher(s);
String number = matcher.replaceAll("");
return number;
}
public static String getOnlyStrings(String s) {
Pattern pattern = Pattern.compile("[^a-z A-Z]");
Matcher matcher = pattern.matcher(s);
String number = matcher.replaceAll("");
return number;
}
}
Result
String is = one
Number is = 9196390097
Try replaceAll() method of the String class.
BTW here is the method, return type and parameters.
public String replaceAll(String regex,
String replacement)
Example:
String str = "Hello +-^ my + - friends ^ ^^-- ^^^ +!";
str = str.replaceAll("[-+^]*", "");
It should remove all the {'^', '+', '-'} chars that you wanted to remove!
To Remove Special character
String t2 = "!##$%^&*()-';,./?><+abdd";
t2 = t2.replaceAll("\\W+","");
Output will be : abdd.
This works perfectly.
Use the String.replaceAll() method in Java.
replaceAll should be good enough for your problem.
You can remove single char as follows:
String str="+919595354336";
String result = str.replaceAll("\\\\+","");
System.out.println(result);
OUTPUT:
919595354336
If you just want to do a literal replace in java, use Pattern.quote(string) to escape any string to a literal.
myString.replaceAll(Pattern.quote(matchingStr), replacementStr)

Regular expression to match unescaped special characters only

I'm trying to come up with a regular expression that can match only characters not preceded by a special escape sequence in a string.
For instance, in the string Is ? stranded//? , I want to be able to replace the ? which hasn't been escaped with another string, so I can have this result : **Is Dave stranded?**
But for the life of me I have not been able to figure out a way. I have only come up with regular expressions that eat all the replaceable characters.
How do you construct a regular expression that matches only characters not preceded by an escape sequence?
Use a negative lookbehind, it's what they were designed to do!
(?<!//)[?]
To break it down:
(
?<! #The negative look behind. It will check that the following slashes do not exist.
// #The slashes you are trying to avoid.
)
[\?] #Your special charactor list.
Only if the // cannot be found, it will progress with the rest of the search.
I think in Java it will need to be escaped again as a string something like:
Pattern p = Pattern.compile("(?<!//)[\\?]");
Try this Java code:
str="Is ? stranded//?";
Pattern p = Pattern.compile("(?<!//)([?])");
m = p.matcher(str);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, m.group(1).replace("?", "Dave"));
}
m.appendTail(sb);
String s = sb.toString().replace("//", "");
System.out.println("Output: " + s);
OUTPUT
Output: Is Dave stranded?
I was thinking about this and have a second simplier solution, avoiding regexs. The other answers are probably better but I thought I might post it anyway.
String input = "Is ? stranded//?";
String output = input
.replace("//?", "a717efbc-84a9-46bf-b1be-8a9fb714fce8")
.replace("?", "Dave")
.replace("a717efbc-84a9-46bf-b1be-8a9fb714fce8", "?");
Just protect the "//?" by replacing it with something unique (like a guid). Then you know any remaining question marks are fair game.
Use grouping. Here's one example:
import java.util.regex.*;
class Test {
public static void main(String[] args) {
Pattern p = Pattern.compile("([^/][^/])(\\?)");
String s = "Is ? stranded//?";
Matcher m = p.matcher(s);
if (m.matches)
s = m.replaceAll("$1XXX").replace("//", "");
System.out.println(s + " -> " + s);
}
}
Output:
$ java Test
Is ? stranded//? -> Is XXX stranded?
In this example, I'm:
first replacing any non-escaped ? with "XXX",
then, removing the "//" escape sequences.
EDIT Use if (m.matches) to ensure that you handle non-matching strings properly.
This is just a quick-and-dirty example. You need to flesh it out, obviously, to make it more robust. But it gets the general idea across.
Match on a set of characters OTHER than an escape sequence, then a regex special character. You could use an inverted character class ([^/]) for the first bit. Special case an unescaped regex character at the front of the string.
String aString = "Is ? stranded//?";
String regex = "(?<!//)[^a-z^A-Z^\\s^/]";
System.out.println(aString.replaceAll(regex, "Dave"));
The part of the regular expression [^a-z^A-Z^\\s^/] matches non-alphanumeric, whitespace or non-forward slash charaters.
The (?<!//) part does a negative lookbehind - see docco here for more info
This gives the output Is Dave stranded//?
try matching:
(^|(^.)|(.[^/])|([^/].))[special characters list]
I used this one:
((?:^|[^\\])(?:\\\\)*[ESCAPABLE CHARACTERS HERE])
Demo: https://regex101.com/r/zH1zO3/4

Categories

Resources