¿Cómo comprobar si una cadena coincide completamente con una expresión regular en Scala?

80

Supongamos que tengo un patrón Regex con el que quiero hacer coincidir muchas cadenas.

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

Solo quiero verificar si una cadena determinada coincide completamente con la expresión regular. ¿Cuál es una forma buena e idiomática de hacer esto en Scala?

Sé que puedo hacer coincidir patrones en Regexes, pero esto no es sintácticamente muy agradable en este caso, porque no tengo grupos para extraer:

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

O podría volver al patrón de Java subyacente:

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

que tampoco es elegante.

¿Existe una solución mejor?

mkneissl
fuente
Creo que se "5" match { case Digit() => true case _ => false }ve mejor que usar un objeto de patrón subyacente.
Mygod

Respuestas:

66

Para responder a mi propia pregunta, usaré el patrón "proxeneta mi biblioteca"

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

y úsalo así

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

a menos que a alguien se le ocurra una solución mejor (estándar).

Notas

  • No me propongo Stringlimitar el alcance de los posibles efectos secundarios.

  • unapplySeq no lee muy bien en ese contexto.

mkneissl
fuente
¿Tenías en mente algún efecto secundario en particular? En Stringlugar de eso, hice proxenetismo y esto funciona bien hasta ahora, a pesar de Stringla función de miembro matches(regex: String).
KajMagnus
1
Yo también probé una función misses. Match y missmatch :-) Es tan molesto tener que escribir en !s.matches(r)lugar de s misses r. Hmm
KajMagnus
1
¿Qué tal el incorporado "5" matches "\\d"que sugirió @polygenelubricants?
Erik Kaplun
2
Los datos coinciden con un patrón, no al revés. El scaladoc en Regex hace un gran problema sobre la falta de un booleano para "coincidencias". Personalmente, creo que has cambiado una buena combinación por una más torpe. Si no le importan los grupos, use case r(_*) =>.
som-snytt
Tiene que haber una manera de hacer esto sin importar una biblioteca externa ...
Jameela Huq
56

No conozco muy bien a Scala, pero parece que puedes hacer:

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

Referencias

poligenelubricantes
fuente
25
Bueno, eso funciona, pero tiene la desventaja de que el patrón se compila en cada intento de igualar. Me gustaría evitar eso por motivos de rendimiento.
mkneissl
3
@mkneissl: entonces parece que tu .pattern.matcher(text).matcheses el camino a seguir. Puede ocultar la verbosidad bajo algún método de utilidad u operador sobrecargado o algo si Scala lo admite.
polygenelubricants
4
Gracias, eso es lo que voy a hacer, mira mi respuesta. Espero responder a las preguntas propias de uno se acepta el comportamiento de desbordamiento de pila ... Meta dice que es ...
mkneissl
2
@ed. eso es aún más lento y cruft, entonces ¿por qué?
Erik Kaplun
El enlace dado como referencia está roto
Valy Dia
13

Para la coincidencia completa, puede usar unapplySeq . Este método intenta hacer coincidir el objetivo (coincidencia completa) y devuelve las coincidencias.

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
Vasil Remeniuk
fuente
4
Si bien es cierto, el uso principal de unapply y unapplySeq está implícitamente en la cases de un matchbloque.
Randall Schulz
11
  """\d""".r.unapplySeq("5").isDefined            //> res1: Boolean = true
  """\d""".r.unapplySeq("a").isDefined            //> res2: Boolean = false
Jack
fuente
Hmm. ¿Por qué publicar un duplicado de stackoverflow.com/a/3022478/158823 dos años después?
mkneissl
2
Su pregunta original pedía un resultado que terminara en 'verdadero' o 'falso', no en 'Algunos' o 'Ninguno'. Hasta donde yo sé, isDefined no formaba parte de la biblioteca hace 2 años, pero tal vez sí. De todos modos, mi respuesta no es un duplicado ;-)
Jack
Ya veo, no es un duplicado. Lo siento.
mkneissl
1
No hay problemas ;-) Mi error, debería haber explicado por qué estoy usando isDefined en mi respuesta. Solo dar código como respuesta es generalmente una mala idea, así que es mi mala.
Jack
1

La respuesta está en la expresión regular:

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

Luego use uno de los métodos existentes.

Daniel C. Sobral
fuente
3
No creo que las anclas sean el problema aquí. String/Pattern/Matcher.matches, en Java al menos, la cadena completa ya coincide. Creo que el problema es solo el estilo / idioma para expresiones regulares en Scala, es decir, cuáles son esos "uno de los métodos existentes".
polygenelubricants
@polygenelubricants Bueno, Matcher.matcheses una aberración. Ok, hace posibles algunas optimizaciones, aunque no sé si la biblioteca de Java realmente lo aprovecha. Pero la forma estándar para que las expresiones regulares expresen que se requiere una coincidencia completa es usar anclas. Dado que la biblioteca de Scala no proporciona un método de coincidencia completo, la forma correcta de hacerlo es usar anclajes. O eso, o usa la biblioteca de Java.
Daniel C. Sobral
Anclar no es el problema. Vea también el ejemplo "123" en la respuesta de Vasil.
mkneissl
5
@Daniel Es posible que se esté perdiendo el punto: mi pregunta fue, si solo necesito saber si una expresión regular coincide completamente, cuál es una buena manera de expresar eso en Scala. Hay muchas soluciones de trabajo, pero en resumen, creo que falta un método en Regex que solo hace eso y nada más. Para responder a la pregunta en su comentario: La diferencia entre unapplySeq y findFirstMatch es que tengo que cambiar Regex para agregar los anclajes. Ambos métodos no expresan inmediatamente mi intención ni devuelven un valor booleano, es decir, tendría que pasar de Option a Boolean (no hay problema, pero agregando más desorden).
mkneissl
1
@mkneissl No me gusta el concepto de Java matches, pero está bien. En cuanto a Optionvs Boolean, agregue nonEmptyal final y obtendrá el Boolean.
Daniel C. Sobral
0

Usando la biblioteca estándar de Scala y un patrón de expresiones regulares precompilado y coincidencia de patrones (que es el estado de la técnica de Scala):

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

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

más para leer: http://www.scala-lang.org/api/2.12.1/scala/util/matching/index.html

Sven
fuente