我有一个平面文本文件,其格式如下。
98430John Smith Y
98431Mary Jones N
98432Michael Johnson Y
我一直在使用Java打开文件,并使用substring和FileInputStream拆分文件。
FileInputStream fis = new FileInputStream(fin);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
while ((line = br.readLine()) != null) {
String csvString = str.substring(5)+","str.substring(6, 18);
}
br.close();
我现在想做同样的事情,但是在Scala中。我知道我可以通过导入Java库来实现这一点,但我只是想知道,是否有更优雅,纯粹的Scala方法来实现这一目标?
答案 0 :(得分:1)
您可以使用scala.io.Source来阅读文件。
示例代码(无异常处理!):
import scala.io.Source
// field lengths
val fieldLengths = List(5, 12, 12, 1)
// from field lengths to start pos: List(0, 5, 17, 29, 30)
val startPos = fieldLengths.foldLeft(List(0)){(acc, l) =>
l + acc.head :: acc
}.reverse
// fields: List[List[String]] = List(List(98430, John, Smith, Y), List(98431, Mary, Jones, N),
val fields = Source.fromFile("/tmp/test.txt").getLines map { line =>
startPos.zip(fieldLengths).map{ case (start, length) =>
line.substring(start, start + length).trim
}
}
// csv: String =
// 98430,John,Smith,Y
// 98431,Mary,Jones,N
// 98432,Michael,Johnson,Y
val csv = fields.map(_.mkString(",")).mkString("\n")
答案 1 :(得分:1)
处理i / o可能永远不会优雅,因为您始终必须考虑i / o异常的发生。一旦你必须处理异常,你就不再具有好的功能(在数学意义上,即没有状态和确定性结果)。有一种情况可以优雅地处理异常行为:如果异常是结果的一部分(例如,如果您正在编写测试引擎),则可以将结果指定为类型Try[Something]
。 所以java.io或java.nio是可以选择的。
考虑到可测试性,将i / o访问与转换分开。首先读取输入文件(如果它至少适合内存),然后将其转换为csv-string。
您的输入格式可以很容易地表达为正则表达式,您可以根据该表达式匹配输入行。假设ID始终是整数,则正则表达式可能如下所示:(\d{5})(.{12})(.{12})([Y,N])
。
给定输入行上的迭代器,您可以使用fold和regexp-matcher将输入转换为csv-string:
object ToCSV {
val InputFormat = "(\\d{5})(.{12})(.{12})([Y,N])".r
def main(args: Array[String]): Unit = {
// Assume the input to be read from file using a BufferedReader
val input =
"""98430John Smith Y
|98431Mary Jones N
|98432Michael Johnson Y""".stripMargin
val inputLines = input.lines
val csvString =
(inputLines foldLeft "") {
case (accumulator, InputFormat(id, firstName, lastName, yesOrNo)) =>
s"$accumulator$id,${firstName.trim},${lastName.trim},$yesOrNo\n"
}
print(csvString)
}
}
与正则表达式匹配的好处是你的字符串看起来像一个元组。在代码中,"#####firstName lastName X"
与case class InputFormat(id, firstName, lastName, yesOrNo)
的实例无法区分。
编辑:实际上,如果将正则表达式更改为.trim
,则可以删除对(\d{5})(\S{1,12})\s*(\S{1,12})\s*([Y,N])
的调用,但是第一个名称和姓氏不能包含空格。