स्केल में नियमित अभिव्यक्ति का उपयोग करके पैटर्न कैसे मेल खाता है?


124

मैं एक शब्द के पहले अक्षर, और "एबीसी" जैसे समूह में अक्षरों में से एक के बीच एक मैच खोजने में सक्षम होना चाहूंगा। स्यूडोकोड में, यह कुछ इस तरह दिख सकता है:

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

लेकिन मैं जावा के बजाय स्काला में पहला अक्षर कैसे पकड़ सकता हूं? मैं नियमित अभिव्यक्ति को ठीक से कैसे व्यक्त करूं? क्या किसी केस क्लास के भीतर ऐसा करना संभव है ?


9
सावधान रहें: स्काला (और * एमएल भाषाओं) में, पैटर्न मिलान एक और है, जो रेगीक्स से बहुत अलग है, जिसका अर्थ है।

1
आप शायद [a-cA-C]उस नियमित अभिव्यक्ति के लिए चाहते हैं ।

2
scala 2.8 में, स्ट्रिंग्स को Traversable(जैसे Listऔर Array) रूपांतरित किया जाता है , यदि आप पहले 3 वर्ण चाहते हैं, तो "my string".take(3)पहले"foo".head
शेलहोलिक

जवाबों:


237

आप ऐसा कर सकते हैं क्योंकि नियमित अभिव्यक्तियाँ एक्सट्रैक्टर्स को परिभाषित करती हैं लेकिन आपको पहले रेगेक्स पैटर्न को परिभाषित करने की आवश्यकता है। मेरे पास इसे जांचने के लिए एक Scala REPL तक नहीं है, लेकिन कुछ इस तरह से काम करना चाहिए।

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

5
सावधान रहें कि आप एक कैप्चर ग्रुप की घोषणा नहीं कर सकते हैं और फिर इसका उपयोग नहीं कर सकते (यानी केस पैटर्न () यहाँ मेल नहीं खाएगा)
जेरेमी लीपज़िग

34
सावधान रहें कि आपको अपनी नियमित अभिव्यक्ति में समूहों का उपयोग करना चाहिए : val Pattern = "[a-cA-C]".rकाम नहीं करेगा। ऐसा इसलिए है क्योंकि मैच-केस उपयोग करता है unapplySeq(target: Any): Option[List[String]], जो मिलान समूहों को वापस करता है ।
राकेंसी

2
यह StringLike पर एक विधि है जो एक Regex देता है ।
asm

11
@ आरकेसी नं val r = "[A-Ca-c]".r ; 'a' match { case r() => } scala-lang.org/api/current/#scala.util.matching.Regex
som-snytt

3
@JeremyLeipzig समूहों की अनदेखी val r = "([A-Ca-c])".r ; "C" match { case r(_*) => }:।
सोम-संवत

120

2.10 संस्करण के बाद से, एक स्काला के स्ट्रिंग प्रक्षेप सुविधा का उपयोग कर सकता है:

implicit class RegexOps(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ब्लॉग पोस्ट में दिखाया गया है टाइप डायनेमिक का परिचय :

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")
  }
}

उत्तर को बहुत पसंद किया, लेकिन जब इसे REPL के बाहर उपयोग करने की कोशिश की गई तो इसे लॉक कर दिया गया (अर्थात बिल्कुल उसी कोड को जिसने REPL में काम किया था, रनिंग ऐप में काम नहीं किया)। इसके अलावा $लाइन अंत पैटर्न के रूप में साइन का उपयोग करने में समस्या है : संकलक स्ट्रिंग समाप्ति की कमी के बारे में शिकायत करता है।
राजिष जू

@ राजिश: पता नहीं क्या समस्या हो सकती है। मेरे जवाब में सब कुछ 2.10 के बाद से वैध स्कैला कोड है।
किर्त्सुके

@ चचा: वह case p.firstName.lastName.Map(...पैटर्न — मैं धरती पर कैसे पढ़ूं ?
एरिक कप्लून

1
@ErikAllik ने इसे कुछ इस तरह पढ़ा "जब 'FirstName' 'Jo' से शुरू होता है और'NName 'दिए गए रेग्क्स से मेल खाता है, तो मैच सफल होता है"। यह स्केलस पावर का एक उदाहरण है, मैं इस उपयोग के मामले को इस तरह से उत्पादन कोड में नहीं लिखूंगा। Btw, एक मानचित्र के उपयोग को एक सूची द्वारा प्रतिस्थापित किया जाना चाहिए, क्योंकि एक मानचित्र अनियंत्रित है और अधिक मूल्यों के लिए यह गारंटी नहीं है कि सही चर सही मिलानकर्ता से मेल खाता है।
किरित्सुकु

1
यह त्वरित प्रोटोटाइप के लिए बहुत सुविधाजनक है, लेकिन ध्यान दें कि यह Regexमैच की जाँच करने के लिए हर बार एक नया उदाहरण बनाता है। और यह काफी महंगा ऑपरेशन है जिसमें रेगेक्स पैटर्न का संकलन शामिल है।
एचआरजे

51

जैसा कि डेलन ने बताया, matchस्काला के कीवर्ड का रेगेक्स से कोई लेना-देना नहीं है। यह पता लगाने के लिए कि क्या एक स्ट्रिंग एक रेगेक्स से मेल खाती है, आप String.matchesविधि का उपयोग कर सकते हैं । यह पता लगाने के लिए कि क्या कोई स्ट्रिंग निम्न या ऊपरी मामले में ए, बी या सी से शुरू होती है, रेगेक्स इस तरह दिखाई देगा:

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

आप इस रेगेक्स को "ए, बी, सी, ए, बी या सी में से एक के बाद कुछ भी" ( .मतलब "किसी भी चरित्र" और *"शून्य या अधिक बार", इसलिए ""। * "किसी भी स्ट्रिंग है) के रूप में पढ़ सकते हैं । ।


25

एंड्रयू के जवाब पर थोड़ा विस्तार करने के लिए : तथ्य यह है कि एक्स्ट्रेक्ट को परिभाषित करने वाले रेग्युलर एक्सप्रैस का इस्तेमाल किया जा सकता है, जो रेगेक्स द्वारा मिलान किए गए सब्सट्रिंग को बहुत अच्छी तरह से स्कैला के पैटर्न से मेल खाते हुए, जैसे:

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.
}

मैं वास्तव में उच्च टोपी से भ्रमित हूं ^। मैं हालांकि "^" का अर्थ "लाइन की शुरुआत से मेल खाता हूं"। यह रेखा की शुरुआत से मेल नहीं खा रहा है।
माइकल लाफयेट

@MichaelLafayette: एक चरित्र वर्ग के अंदर ( []), कैरट नकार का संकेत देता है, इसलिए [^\s]इसका अर्थ है 'गैर-व्हाट्सएप'।
फैबियन स्टील

9

String.matches रेगेक्स अर्थ में पैटर्न मिलान करने का तरीका है।

लेकिन एक तरफ एक शब्द के रूप में, वास्तविक स्काला कोड में word.firstLetter दिखता है:

word(0)

स्काला स्ट्रिंग्स को चार के अनुक्रम के रूप में मानते हैं, इसलिए यदि किसी कारण से आप स्पष्ट रूप से स्ट्रिंग के पहले चरित्र को प्राप्त करना चाहते थे और उससे मेल खाते थे, तो आप इस तरह से कुछ का उपयोग कर सकते हैं:

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

मैं इसे रेगेक्स पैटर्न मिलान करने के सामान्य तरीके के रूप में प्रस्तावित नहीं कर रहा हूं, लेकिन यह आपके प्रस्तावित दृष्टिकोण के अनुरूप है कि पहले एक स्ट्रिंग के पहले चरित्र को ढूंढें और फिर इसे एक रेक्स के खिलाफ मैच करें।

संपादित करें: स्पष्ट होने के लिए, मैं यह करूँगा, जैसा कि अन्य ने कहा है:

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

बस अपने प्रारंभिक छद्म कोड के करीब के रूप में एक उदाहरण दिखाना चाहता था। चीयर्स!


3
"Cat"(0).toStringअधिक स्पष्ट रूप से लिखा जा सकता है "Cat" take 1, इम्हो।
डेविड विंसलो

इसके अलावा (हालांकि यह एक पुरानी चर्चा है - मैं शायद कब्र खोद रहा हूं): आप '*।' को अंत से हटा सकते हैं क्योंकि यह रेगेक्स में कोई मूल्य नहीं जोड़ता है। जस्ट "कैट" .मैचेज़ ("^ [a-
cA

2.11 पर आज val r = "[A-Ca-c]".r ; "cat"(0) match { case r() => }
सोम-संवत

हाय हैट (^) का क्या अर्थ है?
माइकल लाफायेट

यह एक एंकर है जिसका अर्थ है 'लाइन की शुरुआत' ( cs.duke.edu/csl/docs/unix_course/intro-73.html )। तो हाय हैट का अनुसरण करने वाली हर चीज पैटर्न से मेल खाएगी यदि यह लाइन पर पहली चीज है।
जेंक्स

9

ध्यान दें कि @ एंड्रयूमैयर्स के उत्तर से दृष्टिकोण पूरी स्ट्रिंग को नियमित अभिव्यक्ति के साथ मेल खाता है , जिसके प्रभाव से स्ट्रिंग के दोनों सिरों पर नियमित अभिव्यक्ति को एंकरिंग किया जाता है ^और $। उदाहरण:

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" match { case MY_RE2(_*) => },। अधिक मुहावरेदार, val reसभी टोपी के बिना।
सोम-संवत

9

पहले हमें यह जानना चाहिए कि नियमित अभिव्यक्ति को अलग से इस्तेमाल किया जा सकता है। यहाँ एक उदाहरण है:

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

वास्तव में, नियमित अभिव्यक्ति स्वयं पहले से ही बहुत शक्तिशाली है; केवल एक चीज जो हमें करने की जरूरत है, वह है स्काला द्वारा इसे और अधिक शक्तिशाली बनाना। यहाँ स्काला डॉक्यूमेंट के और उदाहरण हैं: http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.