我编写了一个简短的Scala程序来读取一个大文件,处理它并将结果存储在另一个文件中。该文件包含大约60000行数字,我需要从每个第三行提取第一个数字。最后我将这些数字保存到另一个文件中。虽然数字,但我一直把它们视为字符串。
这是Scala代码:
import scala.io.Source
import java.io.BufferedWriter
import java.io.FileWriter
object Analyze {
def main(args: Array[String]) {
val fname = "input.txt"
val counters = Source.fromFile(fname).mkString.split("\\n").grouped(3)
.map(_(2).split("\\s+")(0))
val f = new BufferedWriter(new FileWriter("output1.txt"))
f.write(counters.reduceLeft(_ + "\n" + _))
f.close()
}
}
我非常喜欢Scala强大的一个内衬的功能。上面代码中的单行读取文件中的整个文本,将其拆分为行,将行分组为3行组,然后从每个组中获取第3行,将其拆分并获取第一个数字。
这是等效的python脚本:
fname = 'input.txt'
with file(fname) as f:
lines = f.read().splitlines()
linegroups = [lines[i:i+3] for i in range(0, len(lines), 3)]
nums = [linegroup[2].split()[0] for linegroup in linegroups]
with file('output2.txt', 'w') as f:
f.write('\n'.join(nums))
Python不具备这样的一个内容。在上面的脚本中,第一行代码将文件读入行列表,下一行将行分组为3组,下一行创建一个列表,其中包含每个组的每个最后一行的第一个数字。它与Scala代码非常相似,只是它的运行速度要快得多。
python脚本在我的笔记本电脑上运行不到一秒钟,而Scala程序运行15秒!我注释掉了将结果保存到文件的代码,持续时间降到了5秒,这仍然太慢了。另外我不明白为什么将数字保存到文件需要这么长时间。当我处理更大的文件时,python脚本运行了几秒钟,而Scala程序的运行时间是几分钟,我无法用它来分析我的文件。
我很感激你对这个问题的建议。 感谢
答案 0 :(得分:9)
我冒昧地清理代码,这应该通过避免初始的mkString来更有效地运行,不需要正则表达式来执行空白分割,而不是在写出结果之前预先聚合结果。我还使用了更好的自我记录方法:
val fname = "input.txt"
val lines = (Source fromFile fname).getLines
val counters =
(lines grouped 3 withPartial false) map { _.last takeWhile (!_.isWhitespace) }
val f = new BufferedWriter(new FileWriter("output1.txt"))
f.write(counters mkString "\n")
f.close()
警告,未经测试的代码
这在很大程度上是无关紧要的,具体取决于您的分析方式。如果您在指标中包含JVM启动时间,则所有投注均已关闭 - 没有任何代码优化可以帮助您。
我通常也建议在你计时之前运行例程几百次预热JVM,但面对文件I / O这不太实际。
答案 1 :(得分:5)
我将Kevin提供的版本定时进行了少量编辑(由于python版本也没有处理填充,因此删除了withPartial):
import scala.io.Source
import java.io.BufferedWriter
import java.io.FileWriter
object A extends App {
val fname = "input.txt"
val lines = (Source fromFile fname).getLines
val counters =
(lines grouped 3) map { _.last takeWhile (!_.isWhitespace) }
val f = new BufferedWriter(new FileWriter("output1.txt"))
f.write(counters mkString "\n")
f.close()
}
这里有60,000行时间:
$ time scala -cp classes A
real 0m2.823s
$ time /usr/bin/python A.py
real 0m0.437s
拥有900,000行:
$ time scala -cp classes A
real 0m5.226s
$ time /usr/bin/python A.py
real 0m3.319s
拥有2,700,000行:
$ time scala -cp classes A
real 0m9.516s
$ time /usr/bin/python A.py
real 0m10.635s
之后,scala版本优于python版本。所以似乎有些长时间是由于JVM初始化和JIT编译时间。
答案 2 :(得分:1)
我认为 notan3xit 是正确的,它是由.mkString
引起的,它急切地将整个文件加载到字符串中。我不太了解Python,但我认为范围和分界线是懒惰的。
您可以查看here以获取有关在Scala中逐行读取文件的更多信息。
答案 3 :(得分:0)
除了@ notan3xit的答案之外,您还可以将计数器写入文件而不先连接它们:
val f = new BufferedWriter(new FileWriter("output1.txt"))
f.write(counters.head.toString)
counters.tail.foreach(c => f.write("\n" + c.toString))
f.close()
虽然你可以在Python中做同样的事情......
答案 4 :(得分:0)
尝试使用此代码写入文件:
val f = new java.io.PrintWriter(new java.io.File("output1.txt"))
f.write(counters.reduce(_ + "\n" + _))
f.close()
快得多。