From 0b03caf3e7e83a83745f665808b7fbbd7e7cc4e6 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 9 Apr 2015 21:23:14 +0300 Subject: [PATCH] Uniform implementation of String.replace and String.matches(Regex) across JVM and JS. Deprecated String.matches(String) and String.replaceAll methods. --- js/js.libraries/src/core/string.kt | 4 --- js/js.libraries/src/core/stringsCode.kt | 10 ++++--- libraries/stdlib/src/kotlin/text/Strings.kt | 27 +++++++++++++++++++ .../stdlib/src/kotlin/text/StringsJVM.kt | 26 ++++++++++++------ libraries/stdlib/test/text/StringTest.kt | 10 +++++++ 5 files changed, 61 insertions(+), 16 deletions(-) diff --git a/js/js.libraries/src/core/string.kt b/js/js.libraries/src/core/string.kt index 3bd7d6630ca..7369efddc55 100644 --- a/js/js.libraries/src/core/string.kt +++ b/js/js.libraries/src/core/string.kt @@ -54,10 +54,6 @@ public fun CharSequence.isEmpty(): Boolean = noImpl -// TODO: internal -// native because we need to escape newValue -//native("replace") -//public fun String.nativeReplace(oldValue: String, newValue: String): String = noImpl // TODO: internal native("replace") public fun String.nativeReplace(pattern: RegExp, replacement: String): String = noImpl diff --git a/js/js.libraries/src/core/stringsCode.kt b/js/js.libraries/src/core/stringsCode.kt index 88494401cf8..9975e9aeec5 100644 --- a/js/js.libraries/src/core/stringsCode.kt +++ b/js/js.libraries/src/core/stringsCode.kt @@ -1,5 +1,7 @@ package kotlin.js +import kotlin.text.Regex + // TODO: make internal public inline fun String.nativeIndexOf(ch : Char, fromIndex : Int) : Int = nativeIndexOf(ch.toString(), fromIndex) public inline fun String.nativeLastIndexOf(ch : Char, fromIndex : Int) : Int = nativeLastIndexOf(ch.toString(), fromIndex) @@ -80,8 +82,8 @@ public inline fun String.decapitalize(): String { } -public fun String.replace(oldValue: String, newValue: String): String = - nativeReplace(RegExp(kotlin.text.Regex.escape(oldValue),"g"), kotlin.text.Regex.escapeReplacement(newValue)) +public fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = + nativeReplace(RegExp(Regex.escape(oldValue), if (ignoreCase) "gi" else "g"), Regex.escapeReplacement(newValue)) -public fun String.replace(oldChar: Char, newChar: Char): String = - nativeReplace(RegExp(kotlin.text.Regex.escape(oldChar.toString()),"g"), newChar.toString()) +public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String = + nativeReplace(RegExp(Regex.escape(oldChar.toString()), if (ignoreCase) "gi" else "g"), newChar.toString()) diff --git a/libraries/stdlib/src/kotlin/text/Strings.kt b/libraries/stdlib/src/kotlin/text/Strings.kt index bcaabc3190e..0f93aa39e50 100644 --- a/libraries/stdlib/src/kotlin/text/Strings.kt +++ b/libraries/stdlib/src/kotlin/text/Strings.kt @@ -1,6 +1,7 @@ package kotlin import java.util.NoSuchElementException +import kotlin.text.MatchResult import kotlin.text.Regex /** Returns the string with leading and trailing text matching the given string removed */ @@ -494,6 +495,32 @@ public fun String.replaceBeforeLast(delimiter: String, replacement: String, miss return if (index == -1) missingDelimiterValue else replaceRange(0, index, replacement) } + +// public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean): String // JVM- and JS-specific +// public fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean): String // JVM- and JS-specific + +/** + * Returns a new string obtained by replacing each substring of this string that matches the given regular expression + * with the given [replacement]. + * + * The [replacement] can consist of any combination of literal text and $-substitutions. To treat the replacement string + * literally escape it with the [kotlin.text.Regex.Companion.escapeReplacement] method. + */ +public fun String.replace(regex: Regex, replacement: String): String = regex.replace(this, replacement) + +/** + * Returns a new string obtained by replacing each substring of this string that matches the given regular expression + * with the result of the given function [transform] that takes [MatchResult] and returns a string to be used as a + * replacement for that match. + */ +public fun String.replace(regex: Regex, transform: (MatchResult) -> String): String = regex.replace(this, transform) + +/** + * Returns `true` if this string matches the given regular expression. + */ +public fun String.matches(regex: Regex): Boolean = regex.matches(this) + + /** * Returns `true` if this string starts with the specified character. */ diff --git a/libraries/stdlib/src/kotlin/text/StringsJVM.kt b/libraries/stdlib/src/kotlin/text/StringsJVM.kt index cd7134e2109..95e66d5df86 100644 --- a/libraries/stdlib/src/kotlin/text/StringsJVM.kt +++ b/libraries/stdlib/src/kotlin/text/StringsJVM.kt @@ -6,6 +6,7 @@ import java.util.Locale import java.util.regex.MatchResult import java.util.regex.Pattern import java.nio.charset.Charset +import kotlin.text.Regex /** @@ -50,12 +51,25 @@ public fun String.equals(anotherString: String, ignoreCase: Boolean = false): Bo /** * Returns a copy of this string with all occurrences of [oldChar] replaced with [newChar]. */ -public fun String.replace(oldChar: Char, newChar: Char): String = (this as java.lang.String).replace(oldChar, newChar) +public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String { + if (!ignoreCase) + return (this as java.lang.String).replace(oldChar, newChar) + else + return splitToSequence(oldChar, ignoreCase = ignoreCase).joinToString(separator = newChar.toString()) +} + +/** + * Returns a new string obtained by replacing all occurrences of the [oldValue] substring in this string + * with the specified [newValue] string. + */ +public fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = + splitToSequence(oldValue, ignoreCase = ignoreCase).joinToString(separator = newValue) /** * Returns a new string obtained by replacing each substring of this string that matches the given regular expression * with the given [replacement]. */ +deprecated("Use String.replace(Regex, String) instead. You can convert regex parameter with .toRegex() extension function.") public fun String.replaceAll(regex: String, replacement: String): String = (this as java.lang.String).replaceAll(regex, replacement) /** @@ -94,7 +108,7 @@ public fun String.split(regex: Pattern, limit: Int = 0): List = regex.sp * Splits this string around matches of the given regular expression. */ deprecated("Convert an argument to regex with toRegex or use splitBy instead.") -public fun String.split(regex: String): Array = split(regex.toPattern()).toTypedArray() +public fun String.split(regex: String): Array = split(regex.toRegex()).toTypedArray() /** @@ -277,6 +291,7 @@ public fun String.isBlank(): Boolean = length() == 0 || all { it.isWhitespace() /** * Returns `true` if this string matches the given regular expression. */ +deprecated("Use String.matches(Regex) instead. You can convert regex parameter with .toRegex() extension function.") public fun String.matches(regex: String): Boolean = (this as java.lang.String).matches(regex) /** @@ -308,12 +323,6 @@ public fun String.regionMatches(thisOffset: Int, other: String, otherOffset: Int else (this as java.lang.String).regionMatches(ignoreCase, thisOffset, other, otherOffset, length) -/** - * Returns a new string obtained by replacing all occurrences of the [target] substring in this string - * with the specified [replacement] string. - */ -public fun String.replace(target: CharSequence, replacement: CharSequence): String = (this as java.lang.String).replace(target, replacement) - /** * Returns a copy of this string converted to lower case using the rules of the specified locale. */ @@ -478,6 +487,7 @@ public inline fun String.takeWhileTo(result: T, predicate: (Cha * Replaces every [regexp] occurence in the text with the value returned by the given function [body] that * takes a [MatchResult]. */ +deprecated("Use String.replace(Regex, (MatchResult)->String) instead. You can convert regex parameter with .toRegex() extension function.") public fun String.replaceAll(regexp: String, body: (java.util.regex.MatchResult) -> String): String { val sb = StringBuilder(this.length()) val p = regexp.toPattern() diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index 967f689fedd..3bf0bda2e50 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -527,4 +527,14 @@ class StringTest { assertFalse("sample".equals("Sample", ignoreCase = false)) assertTrue("sample".equals("Sample", ignoreCase = true)) } + + + test fun replace() { + val input = "abbAb" + assertEquals("abb${'$'}b", input.replace('A', '$')) + assertEquals("/bb/b", input.replace('A', '/', ignoreCase = true)) + + assertEquals("${'$'}bAb", input.replace("ab", "$")) + assertEquals("/b/", input.replace("ab", "/", ignoreCase = true)) + } }