move regex module from existing module to core-java-regex module
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
=========
|
||||
|
||||
## Core Java 8 Cookbooks and Examples
|
||||
|
||||
### Relevant Articles:
|
||||
- [An Overview of Regular Expressions Performance in Java](https://www.baeldung.com/java-regex-performance)
|
||||
- [A Guide To Java Regular Expressions API](http://www.baeldung.com/regular-expressions-java)
|
||||
- [Guide to Escaping Characters in Java RegExps](http://www.baeldung.com/java-regexp-escape-char)
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>core-java-regex</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<name>core-java-regex</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-java</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-java</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh-core.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>core-java-regex</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.baeldung.patternreuse;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@Fork(value = 1, warmups = 1)
|
||||
@Warmup(iterations = 5)
|
||||
@State(Scope.Benchmark)
|
||||
public class PatternPerformanceComparison {
|
||||
|
||||
private static final String PATTERN = "\\d*[02468]";
|
||||
private static List<String> values;
|
||||
|
||||
private static Matcher matcherFromPreCompiledPattern;
|
||||
private static Pattern preCompiledPattern;
|
||||
|
||||
public static void main(String[] args) throws IOException, RunnerException {
|
||||
org.openjdk.jmh.Main.main(args);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void matcherFromPreCompiledPatternResetMatches(Blackhole bh) {
|
||||
//With pre-compiled pattern and reusing the matcher
|
||||
// 1 Pattern object created
|
||||
// 1 Matcher objects created
|
||||
for (String value : values) {
|
||||
bh.consume(matcherFromPreCompiledPattern.reset(value).matches());
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void preCompiledPatternMatcherMatches(Blackhole bh) {
|
||||
// With pre-compiled pattern
|
||||
// 1 Pattern object created
|
||||
// 5_000_000 Matcher objects created
|
||||
for (String value : values) {
|
||||
bh.consume(preCompiledPattern.matcher(value).matches());
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void patternCompileMatcherMatches(Blackhole bh) {
|
||||
// Above approach "Pattern.matches(PATTERN, value)" makes this internally
|
||||
// 5_000_000 Pattern objects created
|
||||
// 5_000_000 Matcher objects created
|
||||
for (String value : values) {
|
||||
bh.consume(Pattern.compile(PATTERN).matcher(value).matches());
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void patternMatches(Blackhole bh) {
|
||||
// Above approach "value.matches(PATTERN)" makes this internally
|
||||
// 5_000_000 Pattern objects created
|
||||
// 5_000_000 Matcher objects created
|
||||
for (String value : values) {
|
||||
bh.consume(Pattern.matches(PATTERN, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void stringMatchs(Blackhole bh) {
|
||||
// 5_000_000 Pattern objects created
|
||||
// 5_000_000 Matcher objects created
|
||||
Instant start = Instant.now();
|
||||
for (String value : values) {
|
||||
bh.consume(value.matches(PATTERN));
|
||||
}
|
||||
}
|
||||
|
||||
@Setup()
|
||||
public void setUp() {
|
||||
preCompiledPattern = Pattern.compile(PATTERN);
|
||||
matcherFromPreCompiledPattern = preCompiledPattern.matcher("");
|
||||
|
||||
values = new ArrayList<>();
|
||||
for (int x = 1; x <= 5_000_000; x++) {
|
||||
values.add(String.valueOf(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.baeldung.patternreuse;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PatternUnitTest {
|
||||
|
||||
private static final Pattern FIRST_LAST_NAME_PRE_COMPILED_PATTERN = Pattern.compile("[a-zA-Z]{3,} [a-zA-Z]{3,}");
|
||||
private static final Pattern SPLIT_PRE_COMPILED_PATTERN = Pattern.compile("__");
|
||||
|
||||
@Test
|
||||
public void givenPreCompiledPattern_whenCallMatcher_thenReturnAMatcherToMatches() {
|
||||
Matcher matcherName1 = FIRST_LAST_NAME_PRE_COMPILED_PATTERN.matcher("Fabio Silva");
|
||||
Matcher matcherName2 = FIRST_LAST_NAME_PRE_COMPILED_PATTERN.matcher("Mr. Silva");
|
||||
|
||||
boolean matchesName1 = matcherName1.matches();
|
||||
boolean matchesName2 = matcherName2.matches();
|
||||
|
||||
assertTrue(matchesName1);
|
||||
assertFalse(matchesName2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPreCompiledPattern_whenCallAsPredicate_thenReturnPredicateToFindPatternInTheList() {
|
||||
List<String> namesToValidate = Arrays.asList("Fabio Silva", "Mr. Silva");
|
||||
Predicate<String> patternsAsPredicate = FIRST_LAST_NAME_PRE_COMPILED_PATTERN.asPredicate();
|
||||
|
||||
List<String> validNames = namesToValidate.stream()
|
||||
.filter(patternsAsPredicate)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertEquals(1, validNames.size());
|
||||
assertTrue(validNames.contains("Fabio Silva"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPreCompiledPattern_whenCallSplit_thenReturnArrayWithValuesSplitByThePattern() {
|
||||
String[] textSplit = SPLIT_PRE_COMPILED_PATTERN.split("My_Name__is__Fabio_Silva");
|
||||
|
||||
assertEquals("My_Name", textSplit[0]);
|
||||
assertEquals("is", textSplit[1]);
|
||||
assertEquals("Fabio_Silva", textSplit[2]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPreCompiledPattern_whenCallSplitAsStream_thenReturnArraySplitByThePattern() {
|
||||
Stream<String> textSplitAsStream = SPLIT_PRE_COMPILED_PATTERN.splitAsStream("My_Name__is__Fabio_Silva");
|
||||
String[] textSplit = textSplitAsStream.toArray(String[]::new);
|
||||
|
||||
assertEquals("My_Name", textSplit[0]);
|
||||
assertEquals("is", textSplit[1]);
|
||||
assertEquals("Fabio_Silva", textSplit[2]);
|
||||
}
|
||||
}
|
||||
+501
File diff suppressed because it is too large
Load Diff
+63
@@ -0,0 +1,63 @@
|
||||
package com.baeldung.regex.matcher;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class MatcherUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenFindFourDigitWorks_thenCorrect() {
|
||||
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
|
||||
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
|
||||
|
||||
assertTrue(m.find());
|
||||
assertEquals(8, m.start());
|
||||
assertEquals("2019", m.group());
|
||||
assertEquals(12, m.end());
|
||||
|
||||
assertTrue(m.find());
|
||||
assertEquals(25, m.start());
|
||||
assertEquals("2020", m.group());
|
||||
assertEquals(29, m.end());
|
||||
|
||||
assertFalse(m.find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenStartIndex_whenFindFourDigitWorks_thenCorrect() {
|
||||
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
|
||||
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
|
||||
|
||||
assertTrue(m.find(20));
|
||||
assertEquals(25, m.start());
|
||||
assertEquals("2020", m.group());
|
||||
assertEquals(29, m.end());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMatchFourDigitWorks_thenFail() {
|
||||
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
|
||||
Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
|
||||
assertFalse(m.matches());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMatchFourDigitWorks_thenCorrect() {
|
||||
Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
|
||||
Matcher m = stringPattern.matcher("2019");
|
||||
|
||||
assertTrue(m.matches());
|
||||
assertEquals(0, m.start());
|
||||
assertEquals("2019", m.group());
|
||||
assertEquals(4, m.end());
|
||||
|
||||
assertTrue(m.matches());// matches will always return the same return
|
||||
}
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.baeldung.regexp;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class EscapingCharsUnitTest {
|
||||
@Test
|
||||
public void givenRegexWithDot_whenMatchingStr_thenMatches() {
|
||||
String strInput = "foof";
|
||||
String strRegex = "foo.";
|
||||
|
||||
assertEquals(true, strInput.matches(strRegex));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() {
|
||||
String strInput = "foof";
|
||||
String strRegex = "foo\\.";
|
||||
|
||||
assertEquals(false, strInput.matches(strRegex));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() {
|
||||
String strInput = "foo|bar|hello|world";
|
||||
String strRegex = "\\Q|\\E";
|
||||
|
||||
assertEquals(4, strInput.split(strRegex).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() {
|
||||
String strInput = "foo|bar|hello|world";
|
||||
String strRegex = "|";
|
||||
|
||||
assertEquals(4, strInput.split(Pattern.quote(strRegex)).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRegexWithDollar_whenReplacing_thenNotReplace() {
|
||||
String strInput = "I gave $50 to my brother."
|
||||
+ "He bought candy for $35. Now he has $15 left.";
|
||||
String strRegex = "$";
|
||||
String strReplacement = "£";
|
||||
String output = "I gave £50 to my brother."
|
||||
+ "He bought candy for £35. Now he has £15 left.";
|
||||
Pattern p = Pattern.compile(strRegex);
|
||||
Matcher m = p.matcher(strInput);
|
||||
|
||||
assertThat(output, not(equalTo(m.replaceAll(strReplacement))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRegexWithDollarEsc_whenReplacing_thenReplace() {
|
||||
String strInput = "I gave $50 to my brother."
|
||||
+ "He bought candy for $35. Now he has $15 left.";
|
||||
String strRegex = "\\$";
|
||||
String strReplacement = "£";
|
||||
String output = "I gave £50 to my brother."
|
||||
+ "He bought candy for £35. Now he has £15 left.";
|
||||
Pattern p = Pattern.compile(strRegex);
|
||||
Matcher m = p.matcher(strInput);
|
||||
|
||||
assertEquals(output, m.replaceAll(strReplacement));
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.baeldung.regexp.optmization;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class OptimizedMatcherManualTest {
|
||||
|
||||
private String action;
|
||||
|
||||
private List<String> items;
|
||||
|
||||
private class TimeWrapper {
|
||||
private long time;
|
||||
private long mstimePreCompiled;
|
||||
private long mstimeNotPreCompiled;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Random random = new Random();
|
||||
items = new ArrayList<String>();
|
||||
long average = 0;
|
||||
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int characters = random.nextInt(7) + 1;
|
||||
for (int k = 0; k < characters; ++ k) {
|
||||
char c = (char)(random.nextInt('Z' - 'A') + 'A');
|
||||
int rep = random.nextInt(95) + 5;
|
||||
for (int j = 0; j < rep; ++ j)
|
||||
s.append(c);
|
||||
average += rep;
|
||||
}
|
||||
items.add(s.toString());
|
||||
}
|
||||
|
||||
average /= 100000;
|
||||
System.out.println("generated data, average length: " + average);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenANotPreCompiledAndAPreCompiledPatternA_whenMatcheItems_thenPreCompiledFasterThanNotPreCompiled() {
|
||||
TimeWrapper timeObj = new TimeWrapper();
|
||||
testPatterns("A*", timeObj);
|
||||
assertTrue(timeObj.mstimePreCompiled < timeObj.mstimeNotPreCompiled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenANotPreCompiledAndAPreCompiledPatternABC_whenMatcheItems_thenPreCompiledFasterThanNotPreCompiled() {
|
||||
TimeWrapper timeObj = new TimeWrapper();
|
||||
testPatterns("A*B*C*", timeObj);
|
||||
assertTrue(timeObj.mstimePreCompiled < timeObj.mstimeNotPreCompiled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenANotPreCompiledAndAPreCompiledPatternECWF_whenMatcheItems_thenPreCompiledFasterThanNotPreCompiled() {
|
||||
TimeWrapper timeObj = new TimeWrapper();
|
||||
testPatterns("E*C*W*F*", timeObj);
|
||||
assertTrue(timeObj.mstimePreCompiled < timeObj.mstimeNotPreCompiled);
|
||||
}
|
||||
|
||||
private void testPatterns(String regex, TimeWrapper timeObj) {
|
||||
timeObj.time = System.nanoTime();
|
||||
int matched = 0;
|
||||
int unmatched = 0;
|
||||
|
||||
for (String item : this.items) {
|
||||
if (item.matches(regex)) {
|
||||
++matched;
|
||||
}
|
||||
else {
|
||||
++unmatched;
|
||||
}
|
||||
}
|
||||
|
||||
this.action = "uncompiled: regex=" + regex + " matched=" + matched + " unmatched=" + unmatched;
|
||||
|
||||
timeObj.mstimeNotPreCompiled = (System.nanoTime() - timeObj.time) / 1000000;
|
||||
System.out.println(this.action + ": " + timeObj.mstimeNotPreCompiled + "ms");
|
||||
|
||||
timeObj.time = System.nanoTime();
|
||||
|
||||
Matcher matcher = Pattern.compile(regex).matcher("");
|
||||
matched = 0;
|
||||
unmatched = 0;
|
||||
|
||||
for (String item : this.items) {
|
||||
if (matcher.reset(item).matches()) {
|
||||
++matched;
|
||||
}
|
||||
else {
|
||||
++unmatched;
|
||||
}
|
||||
}
|
||||
|
||||
this.action = "compiled: regex=" + regex + " matched=" + matched + " unmatched=" + unmatched;
|
||||
|
||||
timeObj.mstimePreCompiled = (System.nanoTime() - timeObj.time) / 1000000;
|
||||
System.out.println(this.action + ": " + timeObj.mstimePreCompiled + "ms");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user