将文件中的字符串与Scala中的case类匹配的最佳方式是什么?

问题描述:

我们有一个文件,其中包含我们想要与案例类匹配的数据。我知道足够的力量,但在斯卡拉寻找一种惯用的方式。将文件中的字符串与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播放列表文件格式。

你可以使用Parser Combinators

如果你有BNF中的文件格式规范或者可以写一个,那么Scala可以从这些规则中为你创建一个解析器。这可能比手工制作的基于正则表达式的解析器更健壮。这当然更多“斯卡拉”。

+0

我认为这里最好的Scala选项。在我的实际工作中,我有太多的领域坐在这里,并匹配每个人的正则表达式。还有标题字段。我认为这将是一条路。我会检查一下。 – dlite922

我没有在斯卡拉多少经验,但可以将这些正则表达式的工作:

你可以使用(?<=name:).*匹配的名称值,(?<=age:).*相匹配的时代价值。如果您使用此项,请删除找到的匹配项中的空格,否则name: bob将与之前的空格匹配bob,您可能不希望这样。

如果name:或任何其他标签在评论中,或评论是在价值之后,将匹配一些东西。如果你想避免这种情况,请留下评论。

+0

没有用。我可能会使用'sed'来删除任何不是'#record'的散列开始的行。应该删除所有评论!谢谢! – dlite922

你可以试试这个:

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

我会用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 

并得到完全相同的结果。

声明:我是该图书馆的作者。