在scala中,将整个文件读取到内存的简单规范方法是什么?(理想情况下,控制字符编码。)
我能想到的最好办法是:
1
| scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_) |
或者我应该使用Java的一个可怕的成语,其中最好的(不使用外部库)似乎是:
1 2 3
| import java. util. Scanner
import java. io. File
new Scanner (new File ("file.txt")). useDelimiter("\\Z"). next() |
号
从阅读邮件列表讨论中,我不清楚scala.i o.source甚至应该是规范的I/O库。我不明白它的目的到底是什么。
…我想要一些简单易记的东西。例如,在这些语言中,很难忘记成语……
1 2 3
| Ruby open("file.txt").read
Ruby File.read("file.txt")
Python open("file.txt").read() |
- 如果你知道正确的工具,Java就没那么糟糕了。导入org.apache.commons.io.fileutils;fileutils.readfiletoString(新文件("file.txt","utf-8")
- 这个评论忽略了语言设计的要点。因此,对于要执行的操作,任何具有简单库函数的语言都与其函数调用语法一样好。给定一个无限的100%内存库,所有程序都将通过一个函数调用来实现。当一种编程语言需要更少的预制组件来实现特定的结果时,它是很好的。
1
| val lines = scala. io. Source. fromFile("file.txt"). mkString |
顺便说一下,"scala."不是真正必要的,因为它总是在范围内,当然,您可以完全或部分导入IO的内容,并避免预先准备"IO"。
但是,上述操作会使文件保持打开状态。为了避免出现问题,您应该这样关闭它:
1 2
| val source = scala. io. Source. fromFile("file.txt")
val lines = try source. mkString finally source. close() |
号
上面代码的另一个问题是,由于它的实现性质,它的速度非常慢。对于较大的文件,应使用:
1 2
| source.getLines mkString"
" |
- 啊,绝对比缩小(+)。
- 我参加聚会太晚了,但我不想让人不知道他们可以在后备箱里做"io.file"("/etc/passwd").slurp"。
- 我不希望scala 2.8有一个名为"slurp"的方法,但我似乎还是坚持使用它。
- 我本来可以谈这个名字的,但由于你的"极度厌恶",我会尽力保持原样。谢谢你特有的不感谢。
- @即席,如果你真的认为我不感谢,我真的很抱歉。我非常感谢您对scala语言的支持,每次您亲自研究我提出的问题、为我提出的问题提出解决方案或向我解释一些事情时,我都会非常感激。那么,我要借此机会感谢你把scala.io变成了一个体面而有价值的东西。从现在起,我会更加直言不讳地表示感谢,但我仍然讨厌这个名字,对不起。
- 我不想多说声谢谢,我只想让你和其他人抑制那种强烈的厌恶,而只是提出建议。另外要记住,我是唯一一个在大多数标准库和编译器上工作的人,而且只有这么多小时。
- 多年来,"slurp"一直是在Perl中同时读取整个文件的名称。Perl的命名传统比C语言家族更为发自内心、更为非正式,有些人可能会对此感到厌恶,但在这种情况下,我认为它是合适的:对于一个丑陋的实践来说,这是一个丑陋的词。当你发出slurp()时,你知道你在做一些淘气的事情,因为你只需要输入。
- "提出建议"-源代码应该检测文件的编码(在合理的范围内),并正确读取。unicode的bom是标准的,还有其他的度量标准,足以猜测给定文件前100个字节的编码。我不必发明什么聪明的东西来检测一个恰好是ucs-2的文件。是的,这件事发生在我身上。
- 我很肯定我见过这样的代码,但我现在肯定在后备箱上找不到。
- (当时我没有使用scala…)
- file.read()将是一个更好的名称,并且与ruby和python一致。
- @即席:你不能阻止人们厌恶。就这样。有些人不喜欢你做的每一个选择,这不应该让你烦恼。这就是生活,你不能取悦所有人:)
- scala 2.8没有fromPath方法。fromFile仍在使用中,并且仍然接受字符串中的文件名。
- @霍萨姆是的,他们在最后恢复了这一点。固定的。
- 赞成使用slurp的人永远不会忘记。
- 注意,只调用fromfile.getlines将实例化一个源实例,但不会关闭它。这意味着scala运行时可以在文件系统中保留该文件的锁,从而防止打开该文件进行写入、重命名、删除等操作,只要scala源代码处于异常状态并持有该锁。下面将读取文件并关闭文件。def linesfrom(文件名:字符串):list[string]=val source=scala.io.source.fromfile(文件名)val text=source.getlines source.close()text
- @DJB这个锁是Windows的问题,但我同意你的观点。它将以任何速率泄漏文件描述符,这可能是一个严重的问题。我已经相应地编辑了这个问题。
- 顺便说一下,光秃秃的mkString(没有getLines)固定在2.11中。
- @雷克斯克尔,你是说速度方面?酷!我得看看。他们是否在迭代器级别重写了它?
- @danielc.sobral-这就是"他们"所做的:github.com/scala/scala/pull/2929
- @danielc.sobral mkstring部分是一个更完整的独立于平台的实现吗:source.getlines mkstring system.lineseparator?
- @本,我不这么认为。一旦你把它作为EDCOX1的6读,你就在使用一个内部Java表示,在这个表示中,EDCOX1×7是一个新的行。如果再次输出,输出方法可能正确理解新行。只有当您要输出它,然后输出方法需要依赖于系统的新行时,这样做才有用。
- @很高兴认识丹妮尔。所以,除非有人计划把它吐到其他地方,比如stdo或其他文件,否则没有任何后果。谢谢您。
- @本是的。即使是吐出来,
也可能是正确的。
- 我学的越多,就越觉得这是个笑话。啜食!-)
为了扩展Daniel的解决方案,您可以将以下导入插入任何需要文件操作的文件中,从而极大地缩短时间:
有了这个,您现在可以:
1
| val lines = fromFile ("file.txt"). getLines |
。
我会小心地把整个文件读到一个单独的String中。这是一个非常坏的习惯,它会比你想象的更快更难咬你。getLines方法返回Iterator[String]类型的值。它实际上是文件中的一个懒惰的光标,允许您只检查所需的数据,而不冒内存过剩的风险。
哦,为了回答你关于Source的隐含问题:是的,它是规范I/O库。大多数代码最终使用java.io,因为它的低层接口和与现有框架的兼容性更好,但是任何有选择的代码都应该使用Source,特别是对于简单的文件操作。
- 好啊。有一个故事是关于我对资料来源的负面印象的:我以前的处境和现在不同,我有一个非常大的文件,不适合记忆。使用source导致程序崩溃;结果发现它试图同时读取整个程序。
- SOURCE不应该将整个文件读取到内存中。如果在getlines之后使用tolist,或者使用其他方法生成一个集合,那么就可以将所有内容都保存到内存中。现在,源代码是一个黑客,目的是完成任务,而不是一个经过仔细考虑的库。它将在scala 2.8中得到改进,但是对于scala社区来说,在定义一个好的I/O API方面肯定有机会变得活跃起来。
1 2
| // for file with utf-8 encoding
val lines = scala. io. Source. fromFile("file.txt", "utf-8"). getLines. mkString |
。
- 将"getlines"添加到原始答案将删除所有新行。应该是"source.fromfile("file.txt","utf-8").mkstring"。
- 在DanielC.Sobral的回答中也可以看到我的评论——这种使用不会关闭源实例,因此scala可能会在文件上保留一个锁。
(编辑:这在scala 2.9中不起作用,也可能不是2.8)
使用后备箱:
1 2 3 4 5 6
| scala> io.File("/etc/passwd").slurp
res0: String =
##
# User Database
#
... etc |
。
- "江户十一〔一〕号"?我们真的抛弃了显而易见的、直观的名字吗?slurp的问题是,它可能在事后对一个以英语为第一语言的人来说是有意义的,至少,但你永远不会想到它是从一开始的!
- 只是偶然发现了这个问题/答案。File已经不在2.8.0版本了,是吗?
- 您仍然可以从scala.tools.nsc.io.file中潜入它,尽管我假设将来位置可能会改变,所以使用它的风险由您自己承担。哦,让我插句话,说我有多讨厌"荡妇"这个名字。
- 咕噜声听起来不错。:)我没想到会出现这种情况,但我也没想到屏幕的输出会被命名为"打印"。slurp太棒了!:)太棒了?我找不到。(
- 在scala-2.10.0中,包名是scala.reflect.io.file,关于这个"文件"有一个问题。即席,为什么这个文件被标记为"实验性的"?安全吗?它是否释放了文件系统的锁?
- 在Clojure,它也叫slurp。
- 我认为,slurp有着悠久的历史,它起源于Perl。
1 2 3 4
| import java. nio. charset. StandardCharsets. _
import java. nio. file. {Files, Paths }
new String (Files. readAllBytes(Paths. get("file.txt")), UTF _8 ) |
控制字符编码,没有要清理的资源。此外,还可能进行了优化(例如,Files.readAllBytes分配适合文件大小的字节数组)。
我听说source.fromfile有问题。就个人而言,我在用Source.fromFile打开大文件时遇到问题,不得不求助于Java输入流。
另一个有趣的解决方案是使用scalax。下面是一些注释良好的代码示例,这些代码使用managedResource打开日志文件,使用scalax帮助器打开文件:http://pastie.org/pastes/420714
在scala.io.source上使用getlines()将丢弃用于行终止符的字符(、
、
等)。
下面应该为字符保留IT字符,并且不执行过多的字符串连接(性能问题):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def fileToString (file : File, encoding : String ) = {
val inStream = new FileInputStream (file )
val outStream = new ByteArrayOutputStream
try {
var reading = true
while ( reading ) {
inStream. read() match {
case - 1 => reading = false
case c => outStream. write(c )
}
}
outStream. flush()
}
finally {
inStream. close()
}
new String (outStream. toByteArray(), encoding )
} |
就像Java一样,使用公共图书馆:
1
| FileUtils.readFileToString(file, StandardCharsets.UTF_8) |
号
此外,这里的许多答案都忘记了字符集。最好总是明确地提供它,否则总有一天会遇到麻烦。
还有一个:https://github.com/pathikrit/better files流和编解码器
在不将内容加载到内存中的情况下,可以使用多种方法来slurp文件:
1 2 3 4
| val bytes : Iterator [Byte ] = file. bytes
val chars : Iterator [Char ] = file. chars
val lines : Iterator [String ] = file. lines
val source : scala. io. BufferedSource = file. content |
您也可以为进行读/写的任何操作提供自己的编解码器(如果不提供,则假定为scala.io.codec.default):
1 2 3 4 5 6 7
| val content : String = file. contentAsString // default codec
// custom codec:
import scala. io. Codec
file. contentAsString(Codec. ISO8859)
//or
import scala. io. Codec. string2codec
file. write("hello world")(codec ="US-ASCII") |
号
为了模拟打开和读取文件的Ruby语法(并传递语义),考虑这个隐式类(scala 2.10和更高版本)。
号
这样的话,
号
正如少数人提到的,由于连接泄漏,最好避免使用scala.io.source。
在新的孵化器项目(即ScalaIO)被合并之前,Scalax和JavaJavaLIBs可能是最好的选择。
您还可以使用scala io中的路径来读取和处理文件。
。
现在,您可以使用以下命令获取文件路径:
1 2
| val filePath = Path ("path_of_file_to_b_read", '/')
val lines = file. lines(includeTerminator = true) |
号
您还可以包括终止符,但默认情况下它设置为false。
为了更快地读取/上载(大)文件,请考虑增加bufferSize(Source.DefaultBufSize设置为2048的大小,例如:
1 2
| val file = new java. io. File("myFilename")
io. Source. fromFile(file, bufferSize = Source. DefaultBufSize * 2) |
号
注意source.scala。有关进一步的讨论,请参阅scala快速文本文件读取和上载到内存。
显而易见的问题是"为什么要读取整个文件?"如果文件变得非常大,这显然不是一个可扩展的解决方案。scala.io.Source从getLines方法返回了Iterator[String]方法,非常有用和简洁。
使用下面的Java IO实用程序来生成一个隐式转换并将EDCOX1的4个EDE、EDCOX1的5个或EDCOX1的6个ED转换成EDCOX1的7个,这不是一个很大的工作。我认为缺乏可伸缩性意味着它们不将此添加到标准API中是正确的。
- 真的吗?在常规的基础上,您实际读取了多少文件,这些文件在内存中存在实际问题?我曾经处理过的绝大多数程序中的绝大多数文件都很容易小到可以放入内存中。坦率地说,大数据文件是例外,如果要读/写它们,您应该认识到这一点,并相应地进行编程。
- 牛轭湖,我不同意。很多情况下,小文件的大小在将来不会增加。
- 我同意他们是例外——但我认为这就是为什么在JDK或scala SDK中都没有将整个文件读取到内存中的原因。这是一个三行实用方法,你自己写:克服它
如果您不介意第三方依赖,您应该考虑使用我的OS lib库。这使得读/写文件和使用文件系统非常方便:
1 2 3 4 5 6 7 8 9 10 11 12
| // Make sure working directory exists and is empty
val wd = os. pwd/ "out"/ "splash"
os. remove. all(wd )
os. makeDir. all(wd )
// Read/write files
os. write(wd/ "file.txt", "hello")
os. read(wd/ "file.txt") ==>"hello"
// Perform filesystem operations
os. copy(wd/ "file.txt", wd/ "copied.txt")
os. list(wd ) ==> Seq (wd/ "copied.txt", wd/ "file.txt") |
。
使用单行帮助程序读取字节、读取块、读取行和许多其他有用/常见操作
您不需要解析每一行,然后再次连接它们…
1
| Source.fromFile(path)(Codec.UTF8).mkString |
我喜欢用这个:
1 2 3 4 5 6 7 8 9
| import scala. io. {BufferedSource, Codec, Source }
import scala. util. Try
def readFileUtf8 (path : String ): Try [String ] = Try {
val source : BufferedSource = Source. fromFile(path )(Codec. UTF8)
val content = source. mkString
source. close()
content
} |
。
- 如果val content = source.mkString中发生错误,应该关闭流。
1 2 3 4 5 6 7 8
| import scala. io. source
object ReadLine {
def main (args :Array [String ]){
if (args. length>0){
for (line <- Source. fromLine(args (0)). getLine())
println (line )
}
} |
。
在参数中,您可以给出文件路径,它将返回所有行
- 另一个答案不能提供什么?
- 没有看到其他答案…我想我可以在这里发表文章…希望这不会伤害任何人。)
- 你真的应该读它们。大多数都很有信息量。即使是8岁的孩子也有相关信息。
打印每一行,就像使用JavaBuffeRead Read Erry线,并打印:
1
| scala.io.Source.fromFile("test.txt" ).foreach{ print } |
。
等效:
1
| scala.io.Source.fromFile("test.txt" ).foreach( x => print(x)) |