Kotlin Regex найти "в начале ввода" - PullRequest
0 голосов
/ 24 сентября 2018

При использовании regex.find(input,pos) можно ли заставить kotlin рассматривать pos как начало строки?

т.е.:

val s = "foo(2)"

/*let's say I already extracted "foo"
  and now want to extract tokens '(', '2' and ')'
*/

val r1a = "\\(".toRegex()
val r1b = "\\)".toRegex()

println(r1a.find(s,3)?.let{"found '${it.value}'"} ?: "Nothing found")
println(r1b.find(s,3)?.let{"found '${it.value}'"} ?: "Nothing found")
println()

//this finds both
//but I only want to find '(' because it's at the beginning of the remaining string

val r2a = "^\\(".toRegex()
val r2b = "^\\)".toRegex()

println(r2a.find(s,3)?.let{"found '${it.value}'"} ?: "Nothing found")
println(r2b.find(s,3)?.let{"found '${it.value}'"} ?: "Nothing found")
println()

//this finds neither.
//I want the following behaviour:

val ss = s.substring(3)
println(r2a.find(ss,0)?.let{"found '${it.value}'"} ?: "Nothing found")
println(r2b.find(ss,0)?.let{"found '${it.value}'"} ?: "Nothing found")
println()

/*which finds '(' but not ')',
  but without having to explicitly split the string
*/

( ideone version )

Есть ли способ сделать это?

РЕДАКТИРОВАТЬ

Я НЕ хочу сопоставить "foo (2) ".

Я хочу, чтобы можно было подать эту строку в список соответствий, которые будут соответствовать сначала foo, затем (, затем 2, затем ).

fun tokenizeLine(line:String){
    var pos = 0
    while(pos < line.length){
        val result = nextToken(line,pos)
        pos += result.consumed
        result.token?.let { tokens.add(it) }
    }
    tokens.add(Token.EOL)
}

, где каждый сопоставитель возвращает один из

sealed class TokenizerResult(val consumed : Int, val token:Token?){
    class Something(consumed:Int, token:Token):TokenizerResult(consumed,token)
    class Skip(consumed:Int=0):TokenizerResult(consumed,null)
    object Nothing:TokenizerResult(0,null)
}

и fun nextToken(input:String, pos:Int) : TokenizerResult просматривает список сопоставителей, пока не закончится совпадение, чтобы попытаться или , один из сопоставителей возвращает что-тоэто не TokenizerResult.Nothing.

val matchers = listOf( skipWhitespace, number, parensOpen, parensClose, identifier, ... )

for(m in matchers){
    result = m(input,pos)
    if(result != TNothing) break
}

if(result == TNothing){
    ...
}

return result

РЕДАКТИРОВАТЬ 2

Сопоставители обычно работают так:

class RawMatch(val regex:Regex) : Pattern{
    override fun match(input: String, pos: Int, createToken: (value: String) -> Token): TokenizerResult {
        return regex.find(input,pos)?.let { TSomething(it.value.length,createToken(it.value)) } ?: TNothing
    }
}

1 Ответ

0 голосов
/ 24 сентября 2018

Если вы хотите найти значение того, что находится в скобках, вы можете использовать другое регулярное выражение наряду с find и groupValues:

val str = "foo(2)"
val regex = "\\s*\\((\\d*)\\)".toRegex()

println(regex.find(str)?.groupValues?.last())

Регулярное выражение ищет любую строкузначение в начале, затем число внутри скобок.Само число сгруппировано по ряду скобок, которые должны быть извлечены через переменную groupValues.Без экранирования строки регулярное выражение выглядит так:

\s*\((\d*)\)
...