Regex pattern in Java to replace everything except certain sequence of tokens - java

I need to make a method that will retrieve words from the text without anything (punctuation etc.) except lowercase words themselves.
BUT I've struggled for 2 hours with regex pattern and faced such a problem.
There are words like "50-year" in the text.
And with my regex, output will be like:
-year
Instead of a normal
year
But I cannot replace dash symbol "-" cause there is another words with hyphen that should be left.
Here is a code:
public List<String> retrieveWordsFromFile() {
List<String> wordsFromText = new ArrayList<>();
scanner.useDelimiter("\\n+|\\s+|'");
while (scanner.hasNext()) {
wordsFromText.add(scanner.next()
.toLowerCase()
.replaceAll("^s$", "is")
.replaceAll("[^\\p{Lower}\\-]", "")
);
}
wordsFromText.removeIf(word -> word.equals(""));
return wordsFromText;
}
So how can I say that I need to replace everything except text and words with dash starting only with a letter/s. So this regex string should probably be such a "merged" into one sequence?

Use the regex, \\b[\\p{Lower}]+\\-[\\p{Lower}]+\\b|\\b[\\p{Lower}]+\\b
Demo:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
// Test strings
String[] arr = { "Hello world", "Hello world 123", "HELLO world", "50-year", "stack-overflow" };
// Define regex pattern
Pattern pattern = Pattern.compile("\\b[\\p{Lower}]+\\-[\\p{Lower}]+\\b|\\b[\\p{Lower}]+\\b");
for (String s : arr) {
// The string to be matched
Matcher matcher = pattern.matcher(s);
while (matcher.find()) {
// Matched string
String matchedStr = matcher.group();
// Display the matched string
System.out.println(matchedStr);
}
}
}
}
Output:
world
world
world
year
stack-overflow
Explanation of regex:
\b species the word boundary.
+ specifies one or more characters.
| specifies OR
This is how you can discard the non-matching text:
public class Main {
public static void main(String[] args) {
// Test strings
String[] arr = { "Hello world", "Hello world 123", "HELLO world", "50-year", "stack-overflow", "HELLO",
"HELLO WORLD", "&^*%", "hello", "123", "1w23" };
// Regex pattern
String regex = ".*?(\\b[\\p{Lower}]+\\-[\\p{Lower}]+\\b|\\b[\\p{Lower}]+\\b).*";
for (String s : arr) {
// Replace the string with group(1)
String str = s.replaceAll(regex, "$1");
// If the replaced string does not match the regex pattern, replace it with
// empty string
s = !str.matches(regex) ? "" : str;
// Display the replaced string if it is not empty
if (!s.isEmpty()) {
System.out.println(s);
}
}
}
}
Output:
world
world
world
year
stack-overflow
hello
Explanation of replacement:
.*? matches everything reluctantly i.e. before it yields to the next pattern.
s.replaceAll(regex, "$1") will replace s with group(1)

Related

Retrieve a Sub-string from a string after the first occurrence of a character from a range of characters

i am trying to retrieve a sub-string from a string from the first occurence of any character between A-Z and a-z
for example:
if the string is 13BHO1234FO
then substring should be BHO1234FO
i.e the string from the first occurence of the character 'B'.
Try this. It simply deletes the first part of the string you don't want and returns the rest. The original string is unchanged.
String[] testCases =
{ "13BHO1234FO", "ARSTOP123!", "133KSLK", "122222" };
for (String s : testCases) {
String sub = s.replaceFirst("^[^A-Za-z]+", "");
System.out.println("'" + sub + "'");
}
Prints substrings surrounded by single quotes to show the string.
'BHO1234FO'
'ARSTOP123!'
'KSLK'
''
You can use a regex and Matcher to find the index of the first alphabetical character and make a substring starting from the index:
import java.util.regex.*;
class Main {
public static void main(String[] args) {
String text = "13BHO1234FO";
Pattern pattern = Pattern.compile("[A-Za-z]");
Matcher matcher = pattern.matcher(text);
matcher.find();
int index = matcher.start();
String substr = text.substring(index);
System.out.println(substr);
}
}
Try this one:
public static void main(String[] args) {
String test = "13BHO1234FO";
System.out.println(test.replaceFirst("^.*?(?=[A-Za-z])", ""));
}

Splitting string on spaces unless in double quotes but double quotes can have a preceding string attached

I need to split a string in Java (first remove whitespaces between quotes and then split at whitespaces.)
"abc test=\"x y z\" magic=\" hello \" hola"
becomes:
firstly:
"abc test=\"xyz\" magic=\"hello\" hola"
and then:
abc
test="xyz"
magic="hello"
hola
Scenario :
I am getting a string something like above from input and I want to break it into parts as above. One way to approach was first remove the spaces between quotes and then split at spaces. Also string before quotes complicates it. Second one was split at spaces but not if inside quote and then remove spaces from individual split. I tried capturing quotes with "\"([^\"]+)\"" but I'm not able to capture just the spaces inside quotes. I tried some more but no luck.
We can do this using a formal pattern matcher. The secret sauce of the answer below is to use the not-much-used Matcher#appendReplacement method. We pause at each match, and then append a custom replacement of anything appearing inside two pairs of quotes. The custom method removeSpaces() strips all whitespace from each quoted term.
public static String removeSpaces(String input) {
return input.replaceAll("\\s+", "");
}
String input = "abc test=\"x y z\" magic=\" hello \" hola";
Pattern p = Pattern.compile("\"(.*?)\"");
Matcher m = p.matcher(input);
StringBuffer sb = new StringBuffer("");
while (m.find()) {
m.appendReplacement(sb, "\"" + removeSpaces(m.group(1)) + "\"");
}
m.appendTail(sb);
String[] parts = sb.toString().split("\\s+");
for (String part : parts) {
System.out.println(part);
}
abc
test="xyz"
magic="hello"
hola
Demo
The big caveat here, as the above comments hinted at, is that we are really using a regex engine as a rudimentary parser. To see where my solution would fail fast, just remove one of the quotes by accident from a quoted term. But, if you are sure you input is well formed as you have showed us, this answer might work for you.
I wanted to mention the java 9's Matcher.replaceAll lambda extension:
// Find quoted strings and remove there whitespace:
s = Pattern.compile("\"[^\"]*\"").matcher(s)
.replaceAll(mr -> mr.group().replaceAll("\\s", ""));
// Turn the remaining whitespace in a comma and brace all.
s = '{' + s.trim().replaceAll("\\s+", ", ") + '}';
Probably the other answer is better but still I have written it so I will post it here ;) It takes a different approach
public static void main(String[] args) {
String test="abc test=\"x y z\" magic=\" hello \" hola";
Pattern pattern = Pattern.compile("([^\\\"]+=\\\"[^\\\"]+\\\" )");
Matcher matcher = pattern.matcher(test);
int lastIndex=0;
while(matcher.find()) {
String[] parts=matcher.group(0).trim().split("=");
boolean newLine=false;
for (String string : parts[0].split("\\s+")) {
if(newLine)
System.out.println();
newLine=true;
System.out.print(string);
}
System.out.println("="+parts[1].replaceAll("\\s",""));
lastIndex=matcher.end();
}
System.out.println(test.substring(lastIndex).trim());
}
Result is
abc
test="xyz"
magic="hello"
hola
It sounds like you want to write a basic parser/Tokenizer. My bet is that after you make something that can deal with pretty printing in this structure, you will soon want to start validating that there arn't any mis-matching "'s.
But in essence, you have a few stages for this particular problem, and Java has a built in tokenizer that can prove useful.
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
public class Q50151376{
private static class Whitespace{
Whitespace(){ }
#Override
public String toString() {
return "\n";
}
}
private static class QuotedString {
public final String string;
QuotedString(String string) {
this.string = "\"" + string.trim() + "\"";
}
#Override
public String toString() {
return string;
}
}
public static void main(String[] args) {
String test = "abc test=\"x y z\" magic=\" hello \" hola";
StringTokenizer tokenizer = new StringTokenizer(test, "\"");
boolean inQuotes = false;
List<Object> out = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
final String token = tokenizer.nextToken();
if (inQuotes) {
out.add(new QuotedString(token));
} else {
out.addAll(TokenizeWhitespace(token));
}
inQuotes = !inQuotes;
}
System.out.println(joinAsStrings(out));
}
private static String joinAsStrings(List<Object> out) {
return out.stream()
.map(Object::toString)
.collect(Collectors.joining());
}
public static List<Object> TokenizeWhitespace(String in){
List<Object> out = new LinkedList<>();
StringTokenizer tokenizer = new StringTokenizer(in, " ", true);
boolean ignoreWhitespace = false;
while (tokenizer.hasMoreTokens()){
String token = tokenizer.nextToken();
boolean whitespace = token.equals(" ");
if(!whitespace){
out.add(token);
ignoreWhitespace = false;
} else if(!ignoreWhitespace) {
out.add(new Whitespace());
ignoreWhitespace = true;
}
}
return out;
}
}

Regx to test space alphanumeric text in Java [duplicate]

How would I find if a whole word, i.e. "EU", exists within the String "I am in the EU.", while not also matching cases like "I am in Europe."?
Basically, I'd like some sort of regex for the word i.e. "EU" with non-alphabetical characters on either side.
.*\bEU\b.*
public static void main(String[] args) {
String regex = ".*\\bEU\\b.*";
String text = "EU is an acronym for EUROPE";
//String text = "EULA should not match";
if(text.matches(regex)) {
System.out.println("It matches");
} else {
System.out.println("Doesn't match");
}
}
You could do something like
String str = "I am in the EU.";
Matcher matcher = Pattern.compile("\\bEU\\b").matcher(str);
if (matcher.find()) {
System.out.println("Found word EU");
}
Use a pattern with word boundaries:
String str = "I am in the EU.";
if (str.matches(".*\\bEU\\b.*"))
doSomething();
Take a look at the docs for Pattern.

Regex back reference to match a number (or any char sequence) with itself

I am missing something basic here. I have this regex (.*)=\1 and I am using it to match 100=100 and its failing. When I remove the back reference from the regex and continue to use the capturing group, it shows that the captured group is '100'. Why does it not work when I try to use the back reference?
package test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String eqPattern = "(.*)=\1";
String input[] = {"1=1"};
testAndPrint(eqPattern, input); // this does not work
eqPattern = "(.*)=";
input = new String[]{"1=1"};
testAndPrint(eqPattern, input); // this works when the backreference is removed from the expr
}
static void testAndPrint(String regexPattern, String[] input) {
System.out.println("\n Regex pattern is "+regexPattern);
Pattern p = Pattern.compile(regexPattern, Pattern.CASE_INSENSITIVE);
boolean found = false;
for (String str : input) {
System.out.println("Testing "+str);
Matcher matcher = p.matcher(str);
while (matcher.find()) {
System.out.println("I found the text "+ matcher.group() +" starting at " + "index "+ matcher.start()+" and ending at index "+matcher.end());
found = true;
System.out.println("Group captured "+matcher.group(1));
}
if (!found) {
System.out.println("No match found");
}
}
}
}
When I run this, I get the following output
Regex pattern is (.*)=\1
Testing 100=100
No match found
Regex pattern is (.*)=
Testing 100=100
I found the text 100= starting at index 0 and ending at index 4
Group captured 100 -->If the group contains 100, why doesnt it match when I add \1 above
?
You have to escape the pattern string.
String eqPattern = "(.*)=\\1";
I think you need to escape the backslash.
String eqPattern = "(.*)=\\1";

Regex after a special character in Java

I am using regex in java to get a specific output from a list of rooms at my University.
A outtake from the list looks like this:
(A55:G260) Laboratorium 260
(A55:G292) Grupperom 292
(A55:G316) Grupperom 316
(A55:G366) Grupperom 366
(HDS:FLØYEN) Fløyen (appendix)
(ODO:PC-STUE) Pulpakammeret (PC-stue)
(SALEM:KONF) Konferanserom
I want to get the value that comes between the colon and the parenthesis.
The regex I am using at the moment is:
pattern = Pattern.compile("[:]([A-Za-z0-9ÆØÅæøå-]+)");
matcher = pattern.matcher(room.text());
I've included ÆØÅ, because some of the rooms have Norwegian letters in them.
Unfortunately the regex includes the building code also (e.g. "A55") in the output... Comes out like this:
A55
A55
A55
:G260
:G292
:G316
Any ideas on how to solve this?
The problem is not your regular expression. You need to reference group(1) for the match result.
while (matcher.find()) {
System.out.println(matcher.group(1));
}
However, you may consider using a negated character class instead.
pattern = Pattern.compile(":([^)]+)");
You can try a regex like this :
public static void main(String[] args) {
String s = "(HDS:FLØYEN) Fløyen (appendix)";
// select everything after ":" upto the first ")" and replace the entire regex with the selcted data
System.out.println(s.replaceAll(".*?:(.*?)\\).*", "$1"));
String s1 = "ODO:PC-STUE) Pulpakammeret (PC-stue)";
System.out.println(s1.replaceAll(".*?:(.*?)\\).*", "$1"));
}
O/P :
FLØYEN
PC-STUE
Can try with String Opreations as follows,
String val = "(HDS:FLØYEN) Fløyen (appendix)";
if(val.contains(":")){
String valSub = val.split("\\s")[0];
System.out.println(valSub);
valSub = valSub.substring(1, valSub.length()-1);
String valA = valSub.split(":")[0];
String valB = valSub.split(":")[1];
System.out.println(valA);
System.out.println(valB);
}
Output :
(HDS:FLØYEN)
HDS
FLØYEN
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class test
{
public static void main( String args[] ){
// String to be scanned to find the pattern.
String line = "(HDS:FLØYEN) Fløyen (appendix)";
String pattern = ":([^)]+)";
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
// Now create matcher object.
Matcher m = r.matcher(line);
while (m.find()) {
System.out.println(m.group(1));
}
}
}

Categories

Resources