我正在尝试改进CSV解析例程,并认为提取器在这里很有用,但无法解决它们。假设有一个包含用户ID和电子邮件的文件:
1,alice@alice.com
2,bob@bob.com
3,carol@carol.com
如果User
类被定义为case class User(id: Int, email: String)
,那么一切都非常简单
lines map { line =>
line split "," match {
case Array(id, email) => User(id.toInt, email)
}
}
我不明白的是如何处理User
类具有复杂属性的情况,例如
case class Email(username: String, host: string)
case class User(id: Int, email: Email)
答案 0 :(得分:2)
您可能希望使用正则表达式来提取电子邮件地址的内容。也许是这样的:
val lines = Vector(
"1,alice@alice.com",
"2,bob@bob.com",
"3,carol@carol.com")
case class Email(username: String, host: String)
case class User(id: Int, email: Email)
val EmailRe = """(.*)@(.*\.com)""".r // substitute a real email address regex here
lines.map { line =>
line.split(",") match {
case Array(id, EmailRe(uname, host)) => User(id.toInt, Email(uname, host))
}
}
答案 1 :(得分:1)
在这里,您可以使用自定义提取器。
// 1,Alice,21212,Baltimore,MD" -> User(1, Alice, Address(21212, Baltimore, MD))
定义一个自定义Extractor,用于创建给定String的对象:
object UserExtractor {
def unapply(s: String) : Option[User] = try {
Some( User(s) )
}
catch {
// bettor handling of bad cases
case e: Throwable => None
}
}
用于在User上的Comapnion对象上使用自定义应用来保存数据的案例类:
case class Address(code: String, cit: String, county: String)
case class User(id: Int, name: String, address: Address)
object User {
def apply(s: String) : User = s.split(",") match {
case Array(id, name, code, city, county) => User(id.toInt, name, Address(code, city, county) )
}
}
取消应用有效字符串(在示例中,有效表示正确的字段数)。
"1,Alice,21212,Baltimore,MD" match { case UserExtractor(u) => u }
res0: User = User(1,Alice,Address(21212,Baltimore,MD))
可以使用更多自定义应用方法添加更多测试。
答案 2 :(得分:0)
我使用一个RegexExtractor:
val lines = List(
"1,alice@alice.com",
"2,bob@bob.com",
"3,carol@carol.com"
)
case class Email(username: String, host: String)
case class User(id: Int, email: Email)
val idAndEMail = """^([^,]+),([^@]+)@(.+)$""".r
并定义一个将行转换为用户的函数:
def lineToUserWithMail(line: String) : Option[User] =
idAndEMail.findFirstMatchIn(line) map {
case userWithEmail(id,user,host) => User(id.toInt, Email(user,host) )
}
将该功能应用于所有行
lines flatMap lineToUserWithMail
//res29: List[User] = List(User(1,Email(alice,alice.com)), User(2,Email(bob,bob.com)), User(3,Email(carol,carol.com)))
或者,您可以通过添加unnapply方法在case classe上实现自定义Extractor。但对于那种情况,这不值得痛苦。
以下是unapply
的示例class Email(user:String, host:String)
object Email {
def unapply(s: String) : Option[(String,String)] = s.split("@") match {
case Array(user, host) => Some( (user,host) )
case _ => None
}
}
"bob@bob.com" match {
case Email(u,h) => println( s"$u , $h" )
}
// prints bob , bob.com
使用正则表达式解析CSV数据的警告。这并不像你想象的那么容易,我建议使用CSV-Reader作为http://supercsv.sourceforge.net/来处理一些开箱即用的令人讨厌的边缘情况。