2016-11-11 48 views
2

我們有一個文件,其中包含我們想要與案例類匹配的數據。我知道足夠的力量,但在斯卡拉尋找一種慣用的方式。將文件中的字符串與Scala中的case類匹配的最佳方式是什麼?

給定的文件:

#record 
name:John Doe 
age: 34 

#record 
name: Smith Holy 
age: 33 

# some comment 

#record 
# another comment 
name: Martin Fowler 
age: 99 

(兩行字段值是無效的,例如姓名:約翰\ n史密斯應該出錯)

和案件類

case class Record(name:String, age:Int) 

我想返回Stream之類的Seq類型:

val records: Stream records 

這對夫妻的想法我的工作,但到目前爲止還沒有實現的是:

  1. 刪除所有新生產線和治療的整個文件當作一個很長的字符串。然後grep匹配字符串「((?!name)。)+((?! age)。)+ age:([\ s \ d] +)」併爲每個匹配創建一個我的case類的新對象,但到目前爲止,我的正則表達式foo很低,無法匹配評論。

  2. 遞歸思想:遍歷每一行以找到匹配記錄的第一行,然後遞歸地調用該函數以匹配名稱,然後是年齡。打name之後的下一個record(即age從來沒有遇到過)

  3. 當尾遞歸返回Some(new Record(cumulativeMap.get(name), cumulativeMap.get(age))None?更好的主意?

感謝您的閱讀!該文件比上面更復雜,但所有規則都是相同的。對於好奇:我試圖解析一個自定義的M3U播放列表文件格式。

回答

1

你可以使用Parser Combinators

如果你有BNF中的文件格式規範或者可以寫一個,那麼Scala可以從這些規則中爲你創建一個解析器。這可能比手工製作的基於正則表達式的解析器更健壯。這當然更多「斯卡拉」。

+0

我認爲這裏最好的Scala選項。在我的實際工作中,我有太多的領域坐在這裏,並匹配每個人的正則表達式。還有標題字段。我認爲這將是一條路。我會檢查一下。 – dlite922

1

我沒有在斯卡拉多少經驗,但可以將這些正則表達式的工作:

你可以使用(?<=name:).*匹配的名稱值,(?<=age:).*相匹配的時代價值。如果您使用此項,請刪除找到的匹配項中的空格,否則name: bob將與之前的空格匹配bob,您可能不希望這樣。

如果name:或任何其他標籤在評論中,或評論是在價值之後,將匹配一些東西。如果你想避免這種情況,請留下評論。

+0

沒有用。我可能會使用'sed'來刪除任何不是'#record'的散列開始的行。應該刪除所有評論!謝謝! – dlite922

1

你可以試試這個:

Path file = Paths.get("file.txt"); 
val lines = Files.readAllLines(file, Charset.defaultCharset()); 

val records = lines.filter(s => s.startsWith("age:") || s.startsWith("name:")) 
        .grouped(2).toList.map { 
    case List(a, b) => Record(a.replaceAll("name:", "").trim, 
          b.replaceAll("age:", "").trim.toInt) 
} 
2

我會用kantan.regex爲一個相當平凡的基於正則表達式的解決方案。

沒有花哨的無形的推導,你可以寫:

import kantan.regex._ 
import kantan.regex.implicits._ 

case class Record(name:String, age:Int) 
implicit val decoder = MatchDecoder.ordered(Record.apply _) 
input.evalRegex[Record](rx"(?:name:\s*([^\n]+))\n(?:age:\s*([0-9]+))").toList 

這產生了:

List(Success(Record(John Doe,34)), Success(Record(Smith Holy,33)), Success(Record(Martin Fowler,99))) 

注意,這種解決方案需要你手動編寫decoder,但它往往可以自動的。如果你不介意沒有形狀的依賴,你可以簡單地寫:

import kantan.regex._ 
import kantan.regex.implicits._ 
import kantan.regex.generic._ 

case class Record(name:String, age:Int) 
input.evalRegex[Record](rx"(?:name:\s*([^\n]+))\n(?:age:\s*([0-9]+))").toList 

並得到完全相同的結果。

聲明:我是該圖書館的作者。

相關問題