Як перевірити, чи повністю рядок відповідає регулярному виразу в Scala?


80

Припустимо, у мене є шаблон регулярного виразу, якому я хочу відповідати багатьом рядкам.

val Digit = """\d""".r

Я просто хочу перевірити, чи заданий рядок повністю відповідає регулярному виразу. Який хороший та ідіоматичний спосіб зробити це в Scala?

Я знаю, що можу збігати шаблони на регулярних виразах, але це синтаксично не дуже приємно в цьому випадку, оскільки у мене немає груп для вилучення:

scala> "5" match { case Digit() => true case _ => false }
res4: Boolean = true

Або я можу повернутися до основного шаблону Java:

scala> Digit.pattern.matcher("5").matches
res6: Boolean = true

що теж не елегантно.

Чи є краще рішення?


Я думаю, що це "5" match { case Digit() => true case _ => false }виглядає краще, ніж використання об'єкта базового шаблону.
Mygod

Відповіді:


66

Відповідаючи на власне запитання, я буду використовувати "сутенер моєї бібліотеки"

object RegexUtils {
  implicit class RichRegex(val underlying: Regex) extends AnyVal {
    def matches(s: String) = underlying.pattern.matcher(s).matches
  }
}

і використовувати його так

import RegexUtils._
val Digit = """\d""".r
if (Digit matches "5") println("match")
else println("no match")

якщо хтось не придумає кращого (стандартного) рішення.

Примітки

  • Я не сутенер, Stringщоб обмежити сферу потенційних побічних ефектів.

  • unapplySeq не дуже добре читає в цьому контексті.


Ви мали на увазі якийсь конкретний побічний ефект? StringНатомість я сутенер , і це працює досі, незважаючи на Stringфункцію члена matches(regex: String).
KajMagnus

1
Я теж пустив функцію misses. Матч та невідповідність :-) Це так дратує, коли потрібно писати !s.matches(r)замість s misses r. Хм
КайМагнус,

1
Як щодо вбудованого, "5" matches "\\d"який запропонував @polygenelubricants?
Ерік Каплун,

2
Дані відповідають шаблону, а не навпаки. Скаладок на Regex робить велику справу з приводу відсутності логічного значення для "сірників". Особисто я думаю, що ви поміняли гарний матч на незграбнішого, якщо-небудь. Якщо ви не дбаєте про групи, використовуйте case r(_*) =>.
som-snytt

Повинен бути спосіб зробити це, не імпортуючи зовнішню бібліотеку ...
Jameela Huq

56

Я не дуже добре знаю Скалу, але схоже, ви можете просто зробити:

"5".matches("\\d")

Список літератури


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

3
@mkneissl: тоді, схоже, ваш .pattern.matcher(text).matchesшлях. Ви можете приховати багатослівність за допомогою якогось утилітного методу або перевантаженого оператора або чогось іншого, якщо Scala це підтримує.
полігенмастильні речовини

4
Дякую, це те, що я збираюся робити, див. Мою відповідь. Я сподіваюся, що відповідь на власні запитання є прийнятною поведінкою на Stack Overflow ... Мета каже, що це ...
mkneissl

2
@ed. це ще повільніше і крихкіше, так чому?
Ерік Каплун,

Посилання, подане в якості посилання, порушено
Валі Діа,

13

Для повного збігу ви можете використовувати unapplySeq . Цей метод намагається зіставити ціль (весь матч) і повертає збіги.

scala> val Digit = """\d""".r
Digit: scala.util.matching.Regex = \d

scala> Digit unapplySeq "1"
res9: Option[List[String]] = Some(List())

scala> Digit unapplySeq "123"
res10: Option[List[String]] = None

scala> Digit unapplySeq "string"
res11: Option[List[String]] = None

4
Поки це істина, основне використання unapply та unapplySeq неявно знаходиться у cases matchблоку.
Randall Schulz,

11
  """\d""".r.unapplySeq("5").isDefined            //> res1: Boolean = true
  """\d""".r.unapplySeq("a").isDefined            //> res2: Boolean = false

Хм Навіщо розміщувати дублікат stackoverflow.com/a/3022478/158823 через два роки?
mkneissl

2
Вихідне запитання задало результат, який закінчується на „true“ чи „false“, а не „Some“ або „None“. Наскільки мені відомо, Defined не був частиною бібліотеки 2 роки тому, але, можливо, він був. У будь-якому випадку, моя відповідь не є дублікатом ;-)
Джек,

Бачу, це не дублікат. Вибачте.
mkneissl

1
Немає проблем ;-) Моя помилка, я повинен був пояснити, чому я використовую isDefined у своїй відповіді. Просто давати код як відповідь - це, як правило, погана ідея, тому це моя погана ідея.
Джек,

1

Відповідь у регулярному виразі:

val Digit = """^\d$""".r

Потім скористайтеся одним із існуючих методів.


3
Я не думаю, що тут йдеться про якорі. String/Pattern/Matcher.matches, принаймні на Java, це вже цілий рядок. Я думаю, що проблема полягає лише у стилі / ідіомі для регулярного вираження в Scala, тобто в тому, що таке "один із існуючих методів".
полігенмастильні речовини

@polygenelubricants Ну, Matcher.matchesце відхилення. Гаразд, це робить можливими деякі оптимізації, хоча я не знаю, чи насправді бібліотека Java цим користується. Але стандартний спосіб для регулярних виразів виразити, що потрібен повний збіг, - це використання якорів. Оскільки бібліотека Scala не надає методу повного збігу, то правильним способом цього є використання анкерів. Або це, або скористайтеся бібліотекою Java.
Daniel C. Sobral

Закріплення не є проблемою. Дивіться також приклад "123" у відповіді Василя.
mkneissl

5
@Daniel Ви можете пропустити суть - моє запитання було, якщо мені потрібно лише знати, чи регулярний вираз повністю відповідає, який хороший спосіб виразити це в Scala. Є багато робочих рішень, але в підсумку я думаю, що в Regex відсутній метод, який просто робить це, і нічого іншого. Щоб відповісти на запитання у вашому коментарі: Різниця від unapplySeq до findFirstMatch полягає в тому, що я повинен змінити регулярний вираз для додавання якорів. Обидва методи не відразу виражають мій намір і не повертають логічне значення, тобто мені доведеться переходити від Option до Boolean (не проблема, але додаючи більше безладу).
mkneissl

1
@mkneissl Мені не подобається концепція Java matches, але добре. Що стосується Optionvs Boolean, додайте nonEmptyдо кінця, і ви отримаєте Boolean.
Даніель К. Собрал,

0

Використовуючи стандартну бібліотеку Scala та заздалегідь скомпільований шаблон регулярного виразу та збіг шаблонів (що є сучасним рівнем Scala):

val digit = """(\d)""".r

"2" match {
  case digit( a) => println(a + " is Digit")
  case _ => println("it is something else")
}

більше читати: http://www.scala-lang.org/api/2.12.1/scala/util/matching/index.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.