diff --git a/src/changes/changes.xml b/src/changes/changes.xml index fdceb08a9..5960d00bc 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -63,6 +63,7 @@ The type attribute can be add,update,fix,remove. Improve Javadoc for StringUtils.isAnyEmpty(null) Add API SystemUtils.String getEnvironmentVariable(final String name, final String defaultValue) org.apache.commons.lang3.SystemUtils should not write to System.err. + Add RegexUtils class instead of overloadinh methods in StringUtils that take a regex to take precompiled Pattern. diff --git a/src/main/java/org/apache/commons/lang3/RegExUtils.java b/src/main/java/org/apache/commons/lang3/RegExUtils.java new file mode 100644 index 000000000..27463e34d --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/RegExUtils.java @@ -0,0 +1,471 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.regex.Pattern; + +/** + *

Helpers to process Strings using regular expressions.

+ * * @see java.util.regex.Pattern + * @since 3.8 + */ +public class RegExUtils { + + /** + *

Removes each substring of the text String that matches the given regular expression.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *

Unlike in the {@link #removePattern(String, String)} method, the {@link Pattern#DOTALL} option + * is NOT automatically added. + * To use the DOTALL option prepend "(?s)" to the regex. + * DOTALL is also known as single-line mode in Perl.

+ * + *
+     * StringUtils.removeAll(null, *)      = null
+     * StringUtils.removeAll("any", (String) null)  = "any"
+     * StringUtils.removeAll("any", "")    = "any"
+     * StringUtils.removeAll("any", ".*")  = ""
+     * StringUtils.removeAll("any", ".+")  = ""
+     * StringUtils.removeAll("abc", ".?")  = ""
+     * StringUtils.removeAll("A<__>\n<__>B", "<.*>")      = "A\nB"
+     * StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>")  = "AB"
+     * StringUtils.removeAll("ABCabc123abc", "[a-z]")     = "ABC123"
+     * 
+ * + * @param text text to remove from, may be null + * @param regex the regular expression to which this string is to be matched + * @return the text with any removes processed, + * {@code null} if null String input + * + * @throws java.util.regex.PatternSyntaxException + * if the regular expression's syntax is invalid + * + * @see #replaceAll(String, String, String) + * @see #removePattern(String, String) + * @see String#replaceAll(String, String) + * @see java.util.regex.Pattern + * @see java.util.regex.Pattern#DOTALL + * @since 3.5 + */ + public static String removeAll(final String text, final String regex) { + return replaceAll(text, regex, StringUtils.EMPTY); + } + + /** + *

Removes each substring of the text String that matches the given regular expression pattern.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.removeAll(null, *)      = null
+     * StringUtils.removeAll("any", (Pattern) null)  = "any"
+     * StringUtils.removeAll("any", Pattern.compile(""))    = "any"
+     * StringUtils.removeAll("any", Pattern.compile(".*"))  = ""
+     * StringUtils.removeAll("any", Pattern.compile(".+"))  = ""
+     * StringUtils.removeAll("abc", Pattern.compile(".?"))  = ""
+     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
+     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
+     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
+     * StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
+     * 
+ * + * @param text text to remove from, may be null + * @param regex the regular expression to which this string is to be matched + * @return the text with any removes processed, + * {@code null} if null String input + * + * @see #replaceAll(String, Pattern, String) + * @see java.util.regex.Matcher#replaceAll(String) + * @see java.util.regex.Pattern + * @since 3.8 + */ + public static String removeAll(final String text, final Pattern regex) { + return replaceAll(text, regex, StringUtils.EMPTY); + } + + /** + *

Removes the first substring of the text string that matches the given regular expression.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *

The {@link Pattern#DOTALL} option is NOT automatically added. + * To use the DOTALL option prepend "(?s)" to the regex. + * DOTALL is also known as single-line mode in Perl.

+ * + *
+     * StringUtils.removeFirst(null, *)      = null
+     * StringUtils.removeFirst("any", (String) null)  = "any"
+     * StringUtils.removeFirst("any", "")    = "any"
+     * StringUtils.removeFirst("any", ".*")  = ""
+     * StringUtils.removeFirst("any", ".+")  = ""
+     * StringUtils.removeFirst("abc", ".?")  = "bc"
+     * StringUtils.removeFirst("A<__>\n<__>B", "<.*>")      = "A\n<__>B"
+     * StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>")  = "AB"
+     * StringUtils.removeFirst("ABCabc123", "[a-z]")          = "ABCbc123"
+     * StringUtils.removeFirst("ABCabc123abc", "[a-z]+")      = "ABC123abc"
+     * 
+ * + * @param text text to remove from, may be null + * @param regex the regular expression to which this string is to be matched + * @return the text with the first replacement processed, + * {@code null} if null String input + * + * @throws java.util.regex.PatternSyntaxException + * if the regular expression's syntax is invalid + * + * @see #replaceFirst(String, String, String) + * @see String#replaceFirst(String, String) + * @see java.util.regex.Pattern + * @see java.util.regex.Pattern#DOTALL + * @since 3.5 + */ + public static String removeFirst(final String text, final String regex) { + return replaceFirst(text, regex, StringUtils.EMPTY); + } + + /** + *

Removes the first substring of the text string that matches the given regular expression pattern.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.removeFirst(null, *)      = null
+     * StringUtils.removeFirst("any", (Pattern) null)  = "any"
+     * StringUtils.removeFirst("any", Pattern.compile(""))    = "any"
+     * StringUtils.removeFirst("any", Pattern.compile(".*"))  = ""
+     * StringUtils.removeFirst("any", Pattern.compile(".+"))  = ""
+     * StringUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
+     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
+     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
+     * StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
+     * StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
+     * 
+ * + * @param text text to remove from, may be null + * @param regex the regular expression pattern to which this string is to be matched + * @return the text with the first replacement processed, + * {@code null} if null String input + * + * @see #replaceFirst(String, Pattern, String) + * @see java.util.regex.Matcher#replaceFirst(String) + * @see java.util.regex.Pattern + * @since 3.8 + */ + public static String removeFirst(final String text, final Pattern regex) { + return replaceFirst(text, regex, StringUtils.EMPTY); + } + + /** + *

Replaces each substring of the source String that matches the given regular expression with the given + * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.

+ * + * This call is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replacePattern(null, *, *)       = null
+     * StringUtils.replacePattern("any", (String) null, *)   = "any"
+     * StringUtils.replacePattern("any", *, null)   = "any"
+     * StringUtils.replacePattern("", "", "zzz")    = "zzz"
+     * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
+     * StringUtils.replacePattern("", ".+", "zzz")  = ""
+     * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
+     * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
+     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
+     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
+     * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
+     * 
+ * + * @param text + * the source string + * @param regex + * the regular expression to which this string is to be matched + * @param replacement + * the string to be substituted for each match + * @return The resulting {@code String} + * @see #replaceAll(String, String, String) + * @see String#replaceAll(String, String) + * @see Pattern#DOTALL + * @since 3.2 + * @since 3.5 Changed {@code null} reference passed to this method is a no-op. + */ + public static String replacePattern(final String text, final String regex, final String replacement) { + if (text == null || regex == null || replacement == null) { + return text; + } + return Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement); + } + + /** + *

Removes each substring of the source String that matches the given regular expression using the DOTALL option.

+ * + * This call is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.removePattern(null, *)       = null
+     * StringUtils.removePattern("any", (String) null)   = "any"
+     * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
+     * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
+     * 
+ * + * @param text + * the source string + * @param regex + * the regular expression to which this string is to be matched + * @return The resulting {@code String} + * @see #replacePattern(String, String, String) + * @see String#replaceAll(String, String) + * @see Pattern#DOTALL + * @since 3.2 + * @since 3.5 Changed {@code null} reference passed to this method is a no-op. + * + */ + public static String removePattern(final String text, final String regex) { + return replacePattern(text, regex, StringUtils.EMPTY); + } + + /** + *

Replaces each substring of the text String that matches the given regular expression + * with the given replacement.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *

Unlike in the {@link #replacePattern(String, String, String)} method, the {@link Pattern#DOTALL} option + * is NOT automatically added. + * To use the DOTALL option prepend "(?s)" to the regex. + * DOTALL is also known as single-line mode in Perl.

+ * + *
+     * StringUtils.replaceAll(null, *, *)       = null
+     * StringUtils.replaceAll("any", (String) null, *)   = "any"
+     * StringUtils.replaceAll("any", *, null)   = "any"
+     * StringUtils.replaceAll("", "", "zzz")    = "zzz"
+     * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
+     * StringUtils.replaceAll("", ".+", "zzz")  = ""
+     * StringUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
+     * StringUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
+     * StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
+     * StringUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
+     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
+     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
+     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param regex the regular expression to which this string is to be matched + * @param replacement the string to be substituted for each match + * @return the text with any replacements processed, + * {@code null} if null String input + * + * @throws java.util.regex.PatternSyntaxException + * if the regular expression's syntax is invalid + * + * @see #replacePattern(String, String, String) + * @see String#replaceAll(String, String) + * @see java.util.regex.Pattern + * @see java.util.regex.Pattern#DOTALL + * @since 3.5 + */ + public static String replaceAll(final String text, final String regex, final String replacement) { + if (text == null || regex == null || replacement == null) { + return text; + } + return text.replaceAll(regex, replacement); + } + + /** + *

Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replaceAll(null, *, *)       = null
+     * StringUtils.replaceAll("any", (Pattern) null, *)   = "any"
+     * StringUtils.replaceAll("any", *, null)   = "any"
+     * StringUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
+     * StringUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
+     * StringUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
+     * StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
+     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
+     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
+     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
+     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
+     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
+     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
+     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param regex the regular expression pattern to which this string is to be matched + * @param replacement the string to be substituted for each match + * @return the text with any replacements processed, + * {@code null} if null String input + * + * @see java.util.regex.Matcher#replaceAll(String) + * @see java.util.regex.Pattern + * @since 3.8 + */ + public static String replaceAll(final String text, final Pattern regex, final String replacement) { + if (text == null || regex == null || replacement == null) { + return text; + } + return regex.matcher(text).replaceAll(replacement); + } + + /** + *

Replaces the first substring of the text string that matches the given regular expression + * with the given replacement.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *

The {@link Pattern#DOTALL} option is NOT automatically added. + * To use the DOTALL option prepend "(?s)" to the regex. + * DOTALL is also known as single-line mode in Perl.

+ * + *
+     * StringUtils.replaceFirst(null, *, *)       = null
+     * StringUtils.replaceFirst("any", (String) null, *)   = "any"
+     * StringUtils.replaceFirst("any", *, null)   = "any"
+     * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
+     * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
+     * StringUtils.replaceFirst("", ".+", "zzz")  = ""
+     * StringUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
+     * StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
+     * StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
+     * StringUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
+     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
+     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
+     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param regex the regular expression to which this string is to be matched + * @param replacement the string to be substituted for the first match + * @return the text with the first replacement processed, + * {@code null} if null String input + * + * @throws java.util.regex.PatternSyntaxException + * if the regular expression's syntax is invalid + * + * @see String#replaceFirst(String, String) + * @see java.util.regex.Pattern + * @see java.util.regex.Pattern#DOTALL + * @since 3.5 + */ + public static String replaceFirst(final String text, final String regex, final String replacement) { + if (text == null || regex == null|| replacement == null ) { + return text; + } + return text.replaceFirst(regex, replacement); + } + + /** + *

Replaces the first substring of the text string that matches the given regular expression pattern + * with the given replacement.

+ * + * This method is a {@code null} safe equivalent to: + * + * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replaceFirst(null, *, *)       = null
+     * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
+     * StringUtils.replaceFirst("any", *, null)   = "any"
+     * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
+     * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
+     * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
+     * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
+     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
+     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
+     * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
+     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
+     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
+     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param regex the regular expression pattern to which this string is to be matched + * @param replacement the string to be substituted for the first match + * @return the text with the first replacement processed, + * {@code null} if null String input + * + * @see java.util.regex.Matcher#replaceFirst(String) + * @see java.util.regex.Pattern + * @since 3.8 + */ + public static String replaceFirst(final String text, final Pattern regex, final String replacement) { + if (text == null || regex == null|| replacement == null ) { + return text; + } + return regex.matcher(text).replaceFirst(replacement); + } + +} diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java index c6ec2b60f..495e4ec8e 100644 --- a/src/main/java/org/apache/commons/lang3/StringUtils.java +++ b/src/main/java/org/apache/commons/lang3/StringUtils.java @@ -5059,7 +5059,7 @@ public class StringUtils { * *
      * StringUtils.removeAll(null, *)      = null
-     * StringUtils.removeAll("any", null)  = "any"
+     * StringUtils.removeAll("any", (String) null)  = "any"
      * StringUtils.removeAll("any", "")    = "any"
      * StringUtils.removeAll("any", ".*")  = ""
      * StringUtils.removeAll("any", ".+")  = ""
@@ -5083,9 +5083,12 @@ public class StringUtils {
      * @see java.util.regex.Pattern
      * @see java.util.regex.Pattern#DOTALL
      * @since 3.5
+     *
+     * @deprecated Moved to RegExUtils.
      */
+    @Deprecated
     public static String removeAll(final String text, final String regex) {
-        return replaceAll(text, regex, StringUtils.EMPTY);
+        return RegExUtils.removeAll(text, regex);
     }
 
     /**
@@ -5105,7 +5108,7 @@ public class StringUtils {
      *
      * 
      * StringUtils.removeFirst(null, *)      = null
-     * StringUtils.removeFirst("any", null)  = "any"
+     * StringUtils.removeFirst("any", (String) null)  = "any"
      * StringUtils.removeFirst("any", "")    = "any"
      * StringUtils.removeFirst("any", ".*")  = ""
      * StringUtils.removeFirst("any", ".+")  = ""
@@ -5129,7 +5132,10 @@ public class StringUtils {
      * @see java.util.regex.Pattern
      * @see java.util.regex.Pattern#DOTALL
      * @since 3.5
+     *
+     * @deprecated Moved to RegExUtils.
      */
+    @Deprecated
     public static String removeFirst(final String text, final String regex) {
         return replaceFirst(text, regex, StringUtils.EMPTY);
     }
@@ -5206,7 +5212,7 @@ public class StringUtils {
      *
      * 
      * StringUtils.replacePattern(null, *, *)       = null
-     * StringUtils.replacePattern("any", null, *)   = "any"
+     * StringUtils.replacePattern("any", (String) null, *)   = "any"
      * StringUtils.replacePattern("any", *, null)   = "any"
      * StringUtils.replacePattern("", "", "zzz")    = "zzz"
      * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
@@ -5230,12 +5236,12 @@ public class StringUtils {
      * @see Pattern#DOTALL
      * @since 3.2
      * @since 3.5 Changed {@code null} reference passed to this method is a no-op.
+     *
+     * @deprecated Moved to RegExUtils.
      */
+    @Deprecated
     public static String replacePattern(final String source, final String regex, final String replacement) {
-        if (source == null || regex == null || replacement == null) {
-            return source;
-        }
-        return Pattern.compile(regex, Pattern.DOTALL).matcher(source).replaceAll(replacement);
+        return RegExUtils.replacePattern(source, regex, replacement);
     }
 
     /**
@@ -5252,7 +5258,7 @@ public class StringUtils {
      *
      * 
      * StringUtils.removePattern(null, *)       = null
-     * StringUtils.removePattern("any", null)   = "any"
+     * StringUtils.removePattern("any", (String) null)   = "any"
      * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
      * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
      * 
@@ -5267,9 +5273,12 @@ public class StringUtils { * @see Pattern#DOTALL * @since 3.2 * @since 3.5 Changed {@code null} reference passed to this method is a no-op. + * + * @deprecated Moved to RegExUtils. */ + @Deprecated public static String removePattern(final String source, final String regex) { - return replacePattern(source, regex, StringUtils.EMPTY); + return RegExUtils.removePattern(source, regex); } /** @@ -5291,7 +5300,7 @@ public class StringUtils { * *
      * StringUtils.replaceAll(null, *, *)       = null
-     * StringUtils.replaceAll("any", null, *)   = "any"
+     * StringUtils.replaceAll("any", (String) null, *)   = "any"
      * StringUtils.replaceAll("any", *, null)   = "any"
      * StringUtils.replaceAll("", "", "zzz")    = "zzz"
      * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
@@ -5319,12 +5328,12 @@ public class StringUtils {
      * @see java.util.regex.Pattern
      * @see java.util.regex.Pattern#DOTALL
      * @since 3.5
+     *
+     * @deprecated Moved to RegExUtils.
      */
+    @Deprecated
     public static String replaceAll(final String text, final String regex, final String replacement) {
-        if (text == null || regex == null|| replacement == null ) {
-            return text;
-        }
-        return text.replaceAll(regex, replacement);
+        return RegExUtils.replaceAll(text, regex, replacement);
     }
 
     /**
@@ -5345,7 +5354,7 @@ public class StringUtils {
      *
      * 
      * StringUtils.replaceFirst(null, *, *)       = null
-     * StringUtils.replaceFirst("any", null, *)   = "any"
+     * StringUtils.replaceFirst("any", (String) null, *)   = "any"
      * StringUtils.replaceFirst("any", *, null)   = "any"
      * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
      * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
@@ -5372,12 +5381,12 @@ public class StringUtils {
      * @see java.util.regex.Pattern
      * @see java.util.regex.Pattern#DOTALL
      * @since 3.5
+     *
+     * @deprecated Moved to RegExUtils.
      */
+    @Deprecated
     public static String replaceFirst(final String text, final String regex, final String replacement) {
-        if (text == null || regex == null|| replacement == null ) {
-            return text;
-        }
-        return text.replaceFirst(regex, replacement);
+        return RegExUtils.replaceFirst(text, regex, replacement);
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/lang3/RegExUtilsTest.java b/src/test/java/org/apache/commons/lang3/RegExUtilsTest.java
new file mode 100644
index 000000000..ab89f5eff
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/RegExUtilsTest.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for methods of {@link org.apache.commons.lang3.RegExUtils}
+ * which been moved to their own test classes.
+ */
+public class RegExUtilsTest {
+
+    @Test
+    public void testReplacePattern_StringStringString() {
+        assertNull(RegExUtils.replacePattern(null, "", ""));
+        assertEquals("any", RegExUtils.replacePattern("any", (String) null, ""));
+        assertEquals("any", RegExUtils.replacePattern("any", "", null));
+
+        assertEquals("zzz", RegExUtils.replacePattern("", "", "zzz"));
+        assertEquals("zzz", RegExUtils.replacePattern("", ".*", "zzz"));
+        assertEquals("", RegExUtils.replacePattern("", ".+", "zzz"));
+
+        assertEquals("z", RegExUtils.replacePattern("<__>\n<__>", "<.*>", "z"));
+        assertEquals("z", RegExUtils.replacePattern("<__>\\n<__>", "<.*>", "z"));
+        assertEquals("X", RegExUtils.replacePattern("\nxy\n", ".*", "X"));
+
+        assertEquals("ABC___123", RegExUtils.replacePattern("ABCabc123", "[a-z]", "_"));
+        assertEquals("ABC_123", RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_"));
+        assertEquals("ABC123", RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", ""));
+        assertEquals("Lorem_ipsum_dolor_sit",
+                RegExUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2"));
+    }
+
+    @Test
+    public void testRemovePattern_StringString() {
+        assertNull(RegExUtils.removePattern(null, ""));
+        assertEquals("any", RegExUtils.removePattern("any", (String) null));
+
+        assertEquals("", RegExUtils.removePattern("", ""));
+        assertEquals("", RegExUtils.removePattern("", ".*"));
+        assertEquals("", RegExUtils.removePattern("", ".+"));
+
+        assertEquals("AB", RegExUtils.removePattern("A<__>\n<__>B", "<.*>"));
+        assertEquals("AB", RegExUtils.removePattern("A<__>\\n<__>B", "<.*>"));
+        assertEquals("", RegExUtils.removePattern("x\\ny", ".*"));
+        assertEquals("", RegExUtils.removePattern("\nxy\n", ".*"));
+
+        assertEquals("ABC123", RegExUtils.removePattern("ABCabc123", "[a-z]"));
+    }
+
+    @Test
+    public void testReplaceAll_StringStringString() {
+        assertNull(RegExUtils.replaceAll(null, "", ""));
+
+        assertEquals("any", RegExUtils.replaceAll("any", (String) null, ""));
+        assertEquals("any", RegExUtils.replaceAll("any", "", null));
+
+        assertEquals("zzz", RegExUtils.replaceAll("", "", "zzz"));
+        assertEquals("zzz", RegExUtils.replaceAll("", ".*", "zzz"));
+        assertEquals("", RegExUtils.replaceAll("", ".+", "zzz"));
+        assertEquals("ZZaZZbZZcZZ", RegExUtils.replaceAll("abc", "", "ZZ"));
+
+        assertEquals("z\nz", RegExUtils.replaceAll("<__>\n<__>", "<.*>", "z"));
+        assertEquals("z", RegExUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z"));
+
+        assertEquals("ABC___123", RegExUtils.replaceAll("ABCabc123", "[a-z]", "_"));
+        assertEquals("ABC_123", RegExUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_"));
+        assertEquals("ABC123", RegExUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", ""));
+        assertEquals("Lorem_ipsum_dolor_sit",
+                RegExUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2"));
+
+        try {
+            RegExUtils.replaceAll("any", "{badRegexSyntax}", "");
+            fail("RegExUtils.replaceAll expecting PatternSyntaxException");
+        } catch (final PatternSyntaxException ex) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testReplaceAll_StringPatternString() {
+        assertNull(RegExUtils.replaceAll(null, Pattern.compile(""), ""));
+
+        assertEquals("any", RegExUtils.replaceAll("any", (Pattern) null, ""));
+        assertEquals("any", RegExUtils.replaceAll("any", Pattern.compile(""), null));
+
+        assertEquals("zzz", RegExUtils.replaceAll("", Pattern.compile(""), "zzz"));
+        assertEquals("zzz", RegExUtils.replaceAll("", Pattern.compile(".*"), "zzz"));
+        assertEquals("", RegExUtils.replaceAll("", Pattern.compile(".+"), "zzz"));
+        assertEquals("ZZaZZbZZcZZ", RegExUtils.replaceAll("abc", Pattern.compile(""), "ZZ"));
+
+        assertEquals("z\nz", RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z"));
+        assertEquals("z", RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z"));
+
+        assertEquals("z", RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z"));
+        assertEquals("z", RegExUtils.replaceAll("<__>\\n<__>", Pattern.compile("<.*>"), "z"));
+        assertEquals("X", RegExUtils.replaceAll("\nxy\n", Pattern.compile(".*", Pattern.DOTALL), "X"));
+
+        assertEquals("ABC___123", RegExUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_"));
+        assertEquals("ABC_123", RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_"));
+        assertEquals("ABC123", RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), ""));
+        assertEquals("Lorem_ipsum_dolor_sit",
+                RegExUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2"));
+    }
+
+    @Test
+    public void testReplaceFirst_StringStringString() {
+        assertNull(RegExUtils.replaceFirst(null, "", ""));
+
+        assertEquals("any", RegExUtils.replaceFirst("any", (String) null, ""));
+        assertEquals("any", RegExUtils.replaceFirst("any", "", null));
+
+        assertEquals("zzz", RegExUtils.replaceFirst("", "", "zzz"));
+        assertEquals("zzz", RegExUtils.replaceFirst("", ".*", "zzz"));
+        assertEquals("", RegExUtils.replaceFirst("", ".+", "zzz"));
+        assertEquals("ZZabc", RegExUtils.replaceFirst("abc", "", "ZZ"));
+
+        assertEquals("z\n<__>", RegExUtils.replaceFirst("<__>\n<__>", "<.*>", "z"));
+        assertEquals("z", RegExUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z"));
+
+        assertEquals("ABC_bc123", RegExUtils.replaceFirst("ABCabc123", "[a-z]", "_"));
+        assertEquals("ABC_123abc", RegExUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_"));
+        assertEquals("ABC123abc", RegExUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", ""));
+        assertEquals("Lorem_ipsum  dolor   sit",
+                RegExUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2"));
+
+        try {
+            RegExUtils.replaceFirst("any", "{badRegexSyntax}", "");
+            fail("RegExUtils.replaceFirst expecting PatternSyntaxException");
+        } catch (final PatternSyntaxException ex) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testReplaceFirst_StringPatternString() {
+        assertNull(RegExUtils.replaceFirst(null, Pattern.compile(""), ""));
+
+        assertEquals("any", RegExUtils.replaceFirst("any", (Pattern) null, ""));
+        assertEquals("any", RegExUtils.replaceFirst("any", Pattern.compile(""), null));
+
+        assertEquals("zzz", RegExUtils.replaceFirst("", Pattern.compile(""), "zzz"));
+        assertEquals("zzz", RegExUtils.replaceFirst("", Pattern.compile(".*"), "zzz"));
+        assertEquals("", RegExUtils.replaceFirst("", Pattern.compile(".+"), "zzz"));
+        assertEquals("ZZabc", RegExUtils.replaceFirst("abc", Pattern.compile(""), "ZZ"));
+
+        assertEquals("z\n<__>", RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z"));
+        assertEquals("z", RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z"));
+
+        assertEquals("ABC_bc123", RegExUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_"));
+        assertEquals("ABC_123abc", RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_"));
+        assertEquals("ABC123abc", RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), ""));
+        assertEquals("Lorem_ipsum  dolor   sit",
+                     RegExUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2"));
+    }
+
+    @Test
+    public void testRemoveAll_StringString() {
+        assertNull(RegExUtils.removeAll(null, ""));
+        assertEquals("any", RegExUtils.removeAll("any", (String) null));
+
+        assertEquals("any", RegExUtils.removeAll("any", ""));
+        assertEquals("", RegExUtils.removeAll("any", ".*"));
+        assertEquals("", RegExUtils.removeAll("any", ".+"));
+        assertEquals("", RegExUtils.removeAll("any", ".?"));
+
+        assertEquals("A\nB", RegExUtils.removeAll("A<__>\n<__>B", "<.*>"));
+        assertEquals("AB", RegExUtils.removeAll("A<__>\n<__>B", "(?s)<.*>"));
+        assertEquals("ABC123", RegExUtils.removeAll("ABCabc123abc", "[a-z]"));
+
+        try {
+            RegExUtils.removeAll("any", "{badRegexSyntax}");
+            fail("RegExUtils.removeAll expecting PatternSyntaxException");
+        } catch (final PatternSyntaxException ex) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testRemoveAll_StringPattern() {
+        assertNull(RegExUtils.removeAll(null, Pattern.compile("")));
+        assertEquals("any", RegExUtils.removeAll("any", (Pattern) null));
+
+        assertEquals("any", RegExUtils.removeAll("any", Pattern.compile("")));
+        assertEquals("", RegExUtils.removeAll("any", Pattern.compile(".*")));
+        assertEquals("", RegExUtils.removeAll("any", Pattern.compile(".+")));
+        assertEquals("", RegExUtils.removeAll("any", Pattern.compile(".?")));
+
+        assertEquals("A\nB", RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>")));
+        assertEquals("AB", RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>")));
+        assertEquals("ABC123", RegExUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]")));
+
+        assertEquals("AB", RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL)));
+        assertEquals("AB", RegExUtils.removeAll("A<__>\\n<__>B", Pattern.compile("<.*>")));
+        assertEquals("", RegExUtils.removeAll("x\\ny", Pattern.compile(".*")));
+        assertEquals("", RegExUtils.removeAll("\nxy\n", Pattern.compile(".*", Pattern.DOTALL)));
+    }
+
+    @Test
+    public void testRemoveFirst_StringString() {
+        assertNull(RegExUtils.removeFirst(null, ""));
+        assertEquals("any", RegExUtils.removeFirst("any", (String) null));
+
+        assertEquals("any", RegExUtils.removeFirst("any", ""));
+        assertEquals("", RegExUtils.removeFirst("any", ".*"));
+        assertEquals("", RegExUtils.removeFirst("any", ".+"));
+        assertEquals("bc", RegExUtils.removeFirst("abc", ".?"));
+
+        assertEquals("A\n<__>B", RegExUtils.removeFirst("A<__>\n<__>B", "<.*>"));
+        assertEquals("AB", RegExUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>"));
+        assertEquals("ABCbc123", RegExUtils.removeFirst("ABCabc123", "[a-z]"));
+        assertEquals("ABC123abc", RegExUtils.removeFirst("ABCabc123abc", "[a-z]+"));
+
+        try {
+            RegExUtils.removeFirst("any", "{badRegexSyntax}");
+            fail("RegExUtils.removeFirst expecting PatternSyntaxException");
+        } catch (final PatternSyntaxException ex) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testRemoveFirst_StringPattern() {
+        assertNull(RegExUtils.removeFirst(null, Pattern.compile("")));
+        assertEquals("any", RegExUtils.removeFirst("any", (Pattern) null));
+
+        assertEquals("any", RegExUtils.removeFirst("any", Pattern.compile("")));
+        assertEquals("", RegExUtils.removeFirst("any", Pattern.compile(".*")));
+        assertEquals("", RegExUtils.removeFirst("any", Pattern.compile(".+")));
+        assertEquals("bc", RegExUtils.removeFirst("abc", Pattern.compile(".?")));
+
+        assertEquals("A\n<__>B", RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>")));
+        assertEquals("AB", RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>")));
+        assertEquals("ABCbc123", RegExUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]")));
+        assertEquals("ABC123abc", RegExUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+")));
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
index 36f8964f9..c4299b8d1 100644
--- a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
@@ -1190,7 +1190,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testReplacePattern() {
+    public void testReplacePattern_StringStringString() {
         assertNull(StringUtils.replacePattern(null, "", ""));
         assertEquals("any", StringUtils.replacePattern("any", null, ""));
         assertEquals("any", StringUtils.replacePattern("any", "", null));
@@ -1211,7 +1211,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testRemovePattern() {
+    public void testRemovePattern_StringString() {
         assertNull(StringUtils.removePattern(null, ""));
         assertEquals("any", StringUtils.removePattern("any", null));
 
@@ -1228,7 +1228,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testReplaceAll() {
+    public void testReplaceAll_StringStringString() {
         assertNull(StringUtils.replaceAll(null, "", ""));
 
         assertEquals("any", StringUtils.replaceAll("any", null, ""));
@@ -1257,7 +1257,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testReplaceFirst() {
+    public void testReplaceFirst_StringStringString() {
         assertNull(StringUtils.replaceFirst(null, "", ""));
 
         assertEquals("any", StringUtils.replaceFirst("any", null, ""));
@@ -2773,7 +2773,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testRemoveAll() {
+    public void testRemoveAll_StringString() {
         assertNull(StringUtils.removeAll(null, ""));
         assertEquals("any", StringUtils.removeAll("any", null));
 
@@ -2795,7 +2795,7 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testRemoveFirst() {
+    public void testRemoveFirst_StringString() {
         assertNull(StringUtils.removeFirst(null, ""));
         assertEquals("any", StringUtils.removeFirst("any", null));