在 Scala 中将文件中的字符串与案例类匹配的最佳方法是什么?



我们有一个文件,其中包含我们希望与case类匹配的数据。我知道足够的蛮力,但在scala中寻找一种惯用的方法。

给定文件:

#record
name:John Doe
age: 34
#record
name: Smith Holy
age: 33 
# some comment
#record
# another comment
name: Martin Fowler
age: 99 

(两行字段值无效,例如name:Johnn Smith should error)

和case类

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

我想返回一个Seq类型,如Stream:

val records: Stream records

我正在使用但到目前为止还没有实现的几个想法是:

  1. 删除所有新行并将整个文件视为一个长字符串。然后grep匹配字符串"((?!name).)+((?!age).)+age:([sd]+)",并为每个匹配创建我的case类的新对象,但到目前为止,我的正则表达式foo很低,无法匹配周围的注释。

  2. 递归思想:遍历每行找到与record匹配的第一行,然后递归调用函数匹配name,然后是age。当在name之后遇到下一个record(即从未遇到age)时,尾部递归返回Some(new Record(cumulativeMap.get(name), cumulativeMap.get(age))None

  3. ? ?更好的主意吗?

感谢阅读!文件比上面更复杂,但所有规则都是相同的。对于好奇:我试图解析自定义M3U播放列表文件格式。

我会用粤语。一个相当简单的基于正则表达式的解决方案。

如果没有花哨的非形状派生,可以这样写:

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

得到完全相同的结果

免责声明:我是这个库的作者。

您可以使用解析器组合子。

如果你有BNF的文件格式规范或者可以编写一个,那么Scala可以根据这些规则为你创建一个解析器。这可能比手工制作的基于正则表达式的解析器更健壮。

我没有太多Scala的经验,但是这些正则表达式可以工作吗?

您可以使用(?<=name:).*来匹配名称值,(?<=age:).*来匹配年龄值。如果您使用此方法,请删除找到的匹配中的空格,否则name: bob将在bob之前使用空格进行匹配,您可能不希望这样做。

如果name:或任何其他标记在comment中,或者comment在value之后,则将匹配某些内容。如果你想避免这种情况,请留下评论

你可以试试:

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

相关内容

  • 没有找到相关文章

最新更新