2011-01-08 2 views
97

Мне хотелось бы найти совпадение между первой буквой слова и одной из букв в группе, такой как «ABC». В псевдокоде, это может выглядеть примерно так:Как сопоставить шаблон, используя регулярное выражение в Scala?

case Process(word) => 
    word.firstLetter match { 
     case([a-c][A-C]) => 
     case _ => 
    } 
} 

Но как я хватаю первую букву в Scala вместо Java? Как правильно выразить регулярное выражение? Возможно ли это сделать в пределах case class?

+9

Предупреждение: на языках Scala (и * ML) сопоставление шаблонов имеет другое, очень отличное от регулярных выражений. – delnan

+0

Для этого регулярного выражения вы, вероятно, хотите '[a-cA-C]'. –

+2

в scala 2.8, строки преобразуются в 'Traversable' (например,' List' и 'Array'), если вы хотите первые 3 символа, попробуйте' 'my string" .take (3) ', для первого' 'foo «.head' – shellholic

ответ

205

Вы можете сделать это, потому что регулярные выражения определяют экстракторы, но вам нужно сначала определить шаблон регулярного выражения. У меня нет доступа к Scala REPL, чтобы проверить это, но что-то вроде этого должно работать.

val Pattern = "([a-cA-C])".r 
word.firstLetter match { 
    case Pattern(c) => c bound to capture group here 
    case _ => 
} 
+4

остерегайтесь того, что вы не можете объявить группу захвата, а затем не использовать ее (например, case Pattern() здесь не будет соответствовать) –

+22

Остерегайтесь того, что вы * должны * использовать группы в своем регулярном выражении:' '' val Pattern = [a-cA-C] ". r''' не будет работать. Это связано с тем, что в случае совпадения используется '' 'unapplySeq (target: Any): Option [List [String]]' '', который возвращает соответствующие группы. – rakensi

+0

Что означает '.r' в конце' val Pattern = ... '? –

38

Как указал Дельнан, ключевое слово match в Scala не имеет ничего общего с регулярными выражениями. Чтобы узнать, соответствует ли строка регулярному выражению, вы можете использовать метод String.matches. Для того, чтобы выяснить, начинается ли строка с а, б или в нижнем или верхнем регистре, регулярное выражение будет выглядеть следующим образом:

word.matches("[a-cA-C].*") 

Вы можете прочитать это регулярное выражение, как «один из символов а, Ь, с , A, B или C, за которым следует что-либо »(. означает« любой символ »и * означает« ноль или более раз », поэтому«. * »- любая строка).

6

String.matches - способ сделать сопоставление образцов в смысле регулярного выражения.

Но как удобно в сторону, word.firstLetter в реальном коде Scala выглядит следующим образом:

word(0) 

Scala обрабатывает строки как последовательность Char, так что если по какой-то причине вы хотите явно получить первый символ струнные и сравнить его, вы могли бы использовать что-то вроде этого:

"Cat"(0).toString.matches("[a-cA-C]") 
res10: Boolean = true 

Я не предлагаю это как общий способ сделать регулярное выражение сопоставления с образцом, но в соответствии с предлагаемым вашим подходом к первому найти первый символ строки и затем сопоставить ее с регулярным выражением.

EDIT: Чтобы быть ясно, как я хотел бы сделать это, как говорят другие:

"Cat".matches("^[a-cA-C].*") 
res14: Boolean = true 

Просто хотел показать пример как можно ближе к вашему начальному псевдокоду. Ура!

+3

'' «Кошка» (0) .toString'' может быть более четко написана как «Кошка» возьмет 1'', ​​imho. –

+0

Также (хотя это старая дискуссия - я, вероятно, выкопана): вы можете удалить «. *» С конца, поскольку она не добавляет никакого значения в регулярное выражение. Просто «Cat» .matches («^ [a-cA-C]») – akauppi

+0

Сегодня на 2.11 «val r =» [A-Ca-c] ». R; «cat» (0) соответствует {case r() =>} '. –

23

Чтобы немного расширить на Andrew's answer: Тот факт, что регулярные выражения определения экстракторы могут быть использованы для разложения подстроки, совпавшие с регулярным выражением очень хорошо с помощью сопоставления с образцом в Scala, например .:

val Process = """([a-cA-C])([^\s]+)""".r // define first, rest is non-space 
for (p <- Process findAllIn "aha bah Cah dah") p match { 
    case Process("b", _) => println("first: 'a', some rest") 
    case Process(_, rest) => println("some first, rest: " + rest) 
    // etc. 
} 
+0

Я действительно смущен высокой шляпой. Я, хотя «^» означал «Сопоставить начало строки». Это не соответствует началу строки. –

+0

@MichaelLafayette: Внутри символьного класса ('[]') каретка указывает на отрицание, поэтому '[^ \ s]' означает «не-пробелы». –

102

Начиная с версии 2.10, можно использовать в Scala функцию интерполяции строк:

implicit class Regex(sc: StringContext) { 
    def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*) 
} 

scala> "123" match { case r"\d+" => true case _ => false } 
res34: Boolean = true 

Еще лучше можно связать регулярное выражение группы:

scala> "123" match { case r"(\d+)$d" => d.toInt case _ => 0 } 
res36: Int = 123 

scala> "10+15" match { case r"(\d\d)${first}\+(\d\d)${second}" => first.toInt+second.toInt case _ => 0 } 
res38: Int = 25 

Также можно установить более подробные обязательные механизмы:

scala> object Doubler { def unapply(s: String) = Some(s.toInt*2) } 
defined module Doubler 

scala> "10" match { case r"(\d\d)${Doubler(d)}" => d case _ => 0 } 
res40: Int = 20 

scala> object isPositive { def unapply(s: String) = s.toInt >= 0 } 
defined module isPositive 

scala> "10" match { case r"(\d\d)${d @ isPositive()}" => d.toInt case _ => 0 } 
res56: Int = 10 

Впечатляющий пример того, что возможно с Dynamic показано в сообщении блога Introduction to Type Dynamic:

object T { 

    class RegexpExtractor(params: List[String]) { 
    def unapplySeq(str: String) = 
     params.headOption flatMap (_.r unapplySeq str) 
    } 

    class StartsWithExtractor(params: List[String]) { 
    def unapply(str: String) = 
     params.headOption filter (str startsWith _) map (_ => str) 
    } 

    class MapExtractor(keys: List[String]) { 
    def unapplySeq[T](map: Map[String, T]) = 
     Some(keys.map(map get _)) 
    } 

    import scala.language.dynamics 

    class ExtractorParams(params: List[String]) extends Dynamic { 
    val Map = new MapExtractor(params) 
    val StartsWith = new StartsWithExtractor(params) 
    val Regexp = new RegexpExtractor(params) 

    def selectDynamic(name: String) = 
     new ExtractorParams(params :+ name) 
    } 

    object p extends ExtractorParams(Nil) 

    Map("firstName" -> "John", "lastName" -> "Doe") match { 
    case p.firstName.lastName.Map(
      Some(p.Jo.StartsWith(fn)), 
      Some(p.`.*(\\w)$`.Regexp(lastChar))) => 
     println(s"Match! $fn ...$lastChar") 
    case _ => println("nope") 
    } 
} 
+0

Очень понравился ответ, но когда он попытался использовать его вне REPL, он заблокирован (т. Е. Точно такой же код, который работал в REPL, не работал в запущенном приложении). Также существует проблема с использованием знака '$' как шаблона конца строки: компилятор жалуется на отсутствие прерывания строки. – Rajish

+0

@Rajish: Не знаю, в чем проблема. Все в моем ответе является действительным кодом Scala с 2.10. – sschaef

+0

@sschaef: that 'case p.firstName.lastName.Map (...' pattern-how на земле я это читаю? –

8

Обратите внимание, что подход с @ ответом AndrewMyers совпадает строку всего к регулярному выражению, с эффектом закрепления регулярного выражения на обоих концах строки, используя ^ и $. Пример:

scala> val MY_RE = "(foo|bar).*".r 
MY_RE: scala.util.matching.Regex = (foo|bar).* 

scala> val result = "foo123" match { case MY_RE(m) => m; case _ => "No match" } 
result: String = foo 

scala> val result = "baz123" match { case MY_RE(m) => m; case _ => "No match" } 
result: String = No match 

scala> val result = "abcfoo123" match { case MY_RE(m) => m; case _ => "No match" } 
result: String = No match 

И не .* в конце:

scala> val MY_RE2 = "(foo|bar)".r 
MY_RE2: scala.util.matching.Regex = (foo|bar) 

scala> val result = "foo123" match { case MY_RE2(m) => m; case _ => "No match" } 
result: String = No match 
+1

Идиоматически, 'val MY_RE2 =" (foo | bar) ". R.unanchored; «foo123» соответствует {case MY_RE2 (_ *) =>} '. Более идиоматично, 'val re' без всяких шапок. –

4

Во-первых, мы должны знать, что регулярное выражение может использоваться отдельно. Вот пример:

import scala.util.matching.Regex 
val pattern = "Scala".r // <=> val pattern = new Regex("Scala") 
val str = "Scala is very cool" 
val result = pattern findFirstIn str 
result match { 
    case Some(v) => println(v) 
    case _ => 
} // output: Scala 

Во-вторых, мы должны заметить, что объединение регулярного выражения с совпадением шаблонов было бы очень мощным. Вот простой пример.

val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r 
"2014-11-20" match { 
    case date(year, month, day) => "hello" 
} // output: hello 

Фактически, само регулярное выражение уже очень сильно; единственное, что нам нужно сделать, это сделать его более мощным Scala. Вот еще примеры в документе Scala: http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex