Regex implementation: split, replace and replaceFirst.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
|
||||
package kotlin.text
|
||||
|
||||
private val TODO: Nothing get() = throw java.lang.UnsupportedOperationException()
|
||||
import java.util.ArrayList
|
||||
|
||||
|
||||
public enum class RegexOption(val value: String) {
|
||||
@@ -45,16 +45,55 @@ public class Regex(pattern: String, options: Set<RegexOption>) {
|
||||
public fun matchAll(input: CharSequence): Sequence<MatchResult> = sequence({ match(input) }, { match -> match.next() })
|
||||
|
||||
public fun replace(input: CharSequence, replacement: String): String = input.toString().nativeReplace(nativePattern, replacement)
|
||||
public fun replace(input: CharSequence, evaluator: (MatchResult) -> String): String = TODO
|
||||
|
||||
public fun split(input: CharSequence, limit: Int = 0): List<String> = TODO
|
||||
public inline fun replace(input: CharSequence, transform: (MatchResult) -> String): String {
|
||||
var match = match(input)
|
||||
if (match == null) return input.toString()
|
||||
|
||||
var lastStart = 0
|
||||
val length = input.length()
|
||||
val sb = StringBuilder(length)
|
||||
do {
|
||||
val foundMatch = match!!
|
||||
sb.append(input, lastStart, foundMatch.range.start)
|
||||
sb.append(transform(foundMatch))
|
||||
lastStart = foundMatch.range.end + 1
|
||||
match = foundMatch.next()
|
||||
} while (lastStart < length && match != null)
|
||||
|
||||
if (lastStart < length) {
|
||||
sb.append(input, lastStart, length)
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
public fun replaceFirst(input: CharSequence, replacement: String): String =
|
||||
input.toString().nativeReplace(RegExp(pattern, options.map { it.value }.joinToString()), replacement)
|
||||
|
||||
public fun split(input: CharSequence, limit: Int = 0): List<String> {
|
||||
require(limit >= 0, { "Limit must be non-negative, but was $limit" } )
|
||||
val matches = matchAll(input).let { if (limit == 0) it else it.take(limit - 1) }
|
||||
val result = ArrayList<String>()
|
||||
var lastStart = 0
|
||||
|
||||
for (match in matches) {
|
||||
result.add(input.subSequence(lastStart, match.range.start).toString())
|
||||
lastStart = match.range.end + 1
|
||||
}
|
||||
result.add(input.subSequence(lastStart, input.length()).toString())
|
||||
return result
|
||||
}
|
||||
|
||||
public override fun toString(): String = nativePattern.toString()
|
||||
|
||||
companion object {
|
||||
public fun fromLiteral(literal: String): Regex = Regex(escape(literal))
|
||||
public fun escape(literal: String): String = TODO
|
||||
public fun escapeReplacement(literal: String): String = literal.nativeReplace(RegExp("\\$", "g"), "$$$$")
|
||||
public fun escape(literal: String): String = literal.nativeReplace(patternEscape, "\\$&")
|
||||
public fun escapeReplacement(literal: String): String = literal.nativeReplace(replacementEscape, "$$$$")
|
||||
|
||||
private val patternEscape = RegExp("""[-\\^$*+?.()|[\]{}]""", "g")
|
||||
private val replacementEscape = RegExp("""\$""", "g")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package kotlin.io
|
||||
import java.io.File
|
||||
import java.util.Collections
|
||||
import java.util.NoSuchElementException
|
||||
import kotlin.text.Regex
|
||||
|
||||
/**
|
||||
* Estimation of a root name by a given file name.
|
||||
@@ -95,7 +96,7 @@ public fun File.filePathComponents(): FilePathComponents {
|
||||
// Split not only by / or \, but also by //, ///, \\, \\\, etc.
|
||||
val list = if (rootName.length() > 0 && subPath.isEmpty()) listOf() else
|
||||
// Looks awful but we split just by /+ or \+ depending on OS
|
||||
subPath.split("""\Q${File.separatorChar}\E+""".toPattern()).toList().map { it -> File(it) }
|
||||
subPath.split(Regex.fromLiteral(File.separatorChar.toString())).toList().map { it -> File(it) }
|
||||
return FilePathComponents(rootName, list)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ package kotlin.text
|
||||
import java.util.regex.Pattern
|
||||
import java.util.regex.Matcher
|
||||
|
||||
private val TODO: Nothing get() = throw UnsupportedOperationException()
|
||||
|
||||
public trait FlagEnum {
|
||||
public val value: Int
|
||||
public val mask: Int
|
||||
@@ -65,9 +63,35 @@ public class Regex( /* visibility? */ val nativePattern: Pattern) {
|
||||
public fun matchAll(input: CharSequence): Sequence<MatchResult> = sequence({ match(input) }, { match -> match.next() })
|
||||
|
||||
public fun replace(input: CharSequence, replacement: String): String = nativePattern.matcher(input).replaceAll(replacement)
|
||||
public fun replace(input: CharSequence, evaluator: (MatchResult) -> String): String = TODO
|
||||
|
||||
public fun split(input: CharSequence, limit: Int = 0): List<String> = nativePattern.split(input, limit).asList() // TODO: require(limit>=0)
|
||||
public fun replaceFirst(input: CharSequence, replacement: String): String = nativePattern.matcher(input).replaceFirst(replacement)
|
||||
|
||||
public inline fun replace(input: CharSequence, transform: (MatchResult) -> String): String {
|
||||
var match = match(input)
|
||||
if (match == null) return input.toString()
|
||||
|
||||
var lastStart = 0
|
||||
val length = input.length()
|
||||
val sb = StringBuilder(length)
|
||||
do {
|
||||
val foundMatch = match!!
|
||||
sb.append(input, lastStart, foundMatch.range.start)
|
||||
sb.append(transform(foundMatch))
|
||||
lastStart = foundMatch.range.end + 1
|
||||
match = foundMatch.next()
|
||||
} while (lastStart < length && match != null)
|
||||
|
||||
if (lastStart < length) {
|
||||
sb.append(input, lastStart, length)
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
public fun split(input: CharSequence, limit: Int = 0): List<String> {
|
||||
require(limit >= 0, { "Limit must be non-negative, but was $limit" } )
|
||||
return nativePattern.split(input, if (limit == 0) -1 else limit).asList()
|
||||
}
|
||||
|
||||
public override fun toString(): String = nativePattern.toString()
|
||||
|
||||
|
||||
@@ -71,4 +71,38 @@ class RegexTest {
|
||||
assertEquals("bye", m2.groups[2]?.value)
|
||||
}
|
||||
|
||||
test fun escapeLiteral() {
|
||||
val literal = """[-\/\\^$*+?.()|[\]{}]"""
|
||||
assertTrue(Regex.fromLiteral(literal).matches(literal))
|
||||
assertTrue(Regex(Regex.escape(literal)).matches(literal))
|
||||
}
|
||||
|
||||
test fun replace() {
|
||||
val input = "123-456"
|
||||
val pattern = "(\\d+)".toRegex()
|
||||
assertEquals("(123)-(456)", pattern.replace(input, "($1)"))
|
||||
|
||||
}
|
||||
|
||||
test fun replaceEvaluator() {
|
||||
val input = "/12/456/7890/"
|
||||
val pattern = "\\d+".toRegex()
|
||||
assertEquals("/2/3/4/", pattern.replace(input, { it.value.length().toString() } ))
|
||||
}
|
||||
|
||||
|
||||
test fun split() {
|
||||
val input = """
|
||||
some ${"\t"} word
|
||||
split
|
||||
""".trim()
|
||||
|
||||
assertEquals(listOf("some", "word", "split"), "\\s+".toRegex().split(input))
|
||||
|
||||
assertEquals(listOf("name", "value=5"), "=".toRegex().split("name=value=5", limit = 2))
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user