2013-03-26 17 views
1

應該使用Scala模式匹配和正則表達式逐行解析文本文件。如果一行以"names:\t"開頭,則後續製表符分隔的名稱應該作爲Seq[String](或類似名稱)提供。與可變數量的匹配捕獲組匹配的正則表達式模式

這裏的非工作代碼例如:

val Names = "^names:(?:\t([a-zA-Z0-9_]+))+$".r 

"names:\taaa\tbbb\tccc" match { 
    case Names(names @ _*) => println(names) 
    // […] other cases 
    case _ => println("no match") 
} 

輸出:List(ccc)
求購輸出:List(aaa, bbb, ccc)

下面的代碼工作如期望...

object NamesObject { 
    private val NamesLine = "^names:\t([a-zA-Z0-9_]+(?:\t[a-zA-Z0-9_]+)*)$".r 

    def unapplySeq(s: String): Option[Seq[String]] = s match { 
    case NamesLine(nameString) => Some(nameString.split("\t")) 
    case _ => None 
    } 
} 

"names:\taaa\tbbb\tccc" match { 
    case NamesObject(names @ _*) => println(names) 
    // […] other cases 
    case _ => println("no match") 
} 

輸出(如希望):WrappedArray(aaa, bbb, ccc)

我想知道:這是可能的更簡單的方式,而不創建一個object,就像在第一個,但不工作的代碼示例?

+0

斯卡拉'RegEx'提取器是_match_,而不是_find_,所以'^'和'$'是多餘的。如果你想要_find_語義,你需要'。*'在開始和/或結束。 (雖然與您正在嘗試解決的問題無關) – 2013-03-26 15:23:41

+0

@RandallSchulz謝謝,很高興知道。 – hiddenbit 2013-03-26 15:26:30

回答

0

正如蘭德爾·舒爾茨說,似乎不可以使用正則表達式。因此,我的問題的簡短答案是

我目前的解決方案是這樣的:我使用這個類...

class SeparatedLinePattern(Pattern: Regex, separator: String = "\t") { 
    def unapplySeq(s: String): Option[Seq[String]] = s match { 
    case Pattern(nameString) => Some(nameString.split(separator).toSeq) 
    case _ => None 
    } 
} 

...創建模式:

val Names = new SeparatedLinePattern("""names:\t([A-Za-z]+(?:\t[A-Za-z]+)*)""".r) 
val Ints = new SeparatedLinePattern("""ints:\t(\d+(?:\t\d+)*)""".r) 
val ValuesWithID = new SeparatedLinePattern("""id-value:\t(\d+\t\w+(?:\t\d+\t\w+)*)""".r) 

下面的例子他們如何能在一個相當靈活的方式使用:

val testStrings = List("names:\taaa", "names:\tbbb\tccc", "names:\tddd\teee\tfff\tggg\thhh", 
         "ints:\t123", "ints:\t456\t789", "ints:\t100\t200\t300", 
         "id-value:\t42\tbaz", "id-value:\t2\tfoo\t5\tbar\t23\tbla") 

for (s <- testStrings) s match { 
    case Names(name) => println(s"The name is '$name'") 
    case Names(a, b) => println(s"The two names are '$a' and '$b'") 
    case Names(names @ _*) => println("Many names: " + names.mkString(", ")) 

    case Ints(a) => println(s"Just $a") 
    case Ints(a, b) => println(s"$a + $b == ${a.toInt + b.toInt}") 
    case Ints(nums @ _*) => println("Sum is " + (nums map (_.toInt)).sum) 

    case ValuesWithID(id, value) => println(s"ID of '$value' is $id") 
    case ValuesWithID(values @ _*) => println("As map: " + (values.grouped(2) map (x => x(0).toInt -> x(1))).toMap) 

    case _ => println("No match") 
} 

輸出:

The name is 'aaa' 
The two names are 'bbb' and 'ccc' 
Many names: ddd, eee, fff, ggg, hhh 
Just 123 
456 + 789 == 1245 
Sum is 600 
ID of 'baz' is 42 
As map: Map(2 -> foo, 5 -> bar, 23 -> bla) 
1

使用你的工作正則表達式。(\w[a-zA-Z0-9_]預定義的字符類)

val Names = """names:\t(\w+(?:\t\w+)*)""".r 
    "names:\taaa\tbbb\tccc" match { 
    case Names(names) => println(names.split("\t") toSeq) 
    case _ => println("no match") 
    } 

隨着第一,第二&尾綁定,

val Names = """names:\t(\w+)?\t?(\w+)?\t?((?:\w+?\t?)*)""".r 
    "names:\taaa\tbbb\tccc\tddd" match { 
    case Names(first, second, tail) => 
     println(first + ", " + second + ", " + (tail.split("\t") toSeq)); 
    case _ => println("no match") 
    } 
+0

你打敗了我。我相信這是唯一的解決方案,即將其分解爲兩部分(或使用低級別的RegEx API,這比OP嘗試的方法和此解決方案的好處不大)。 – 2013-03-26 15:32:14

+0

這個解決方案很簡短,但是像名稱(第一,第二,尾巴@ _ *)這樣的東西是不可能的,但它對於靈活性會很好。 – hiddenbit 2013-03-26 15:40:19

+0

@Radon,答案用第一,第二和尾部綁定更新 – 2013-03-27 02:22:09