2009-08-16 84 views
277

在Scala中將整個文件讀入內存的簡單和規範的方法是什麼? (理想的情況下,具有過字符編碼控制)在Scala中讀取整個文件?

最好我可以想出的是:

scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_) 

或我應該使用的Java's god-awful idioms酮,(不使用外部庫,最好的)似乎是:

import java.util.Scanner 
import java.io.File 
new Scanner(new File("file.txt")).useDelimiter("\\Z").next() 

從閱讀郵件列表討論,目前還不清楚,我認爲scala.io.Source甚至被認爲是規範的I/O庫。我完全不明白它的預期目的是什麼。

...我想要一些簡單易記的東西。例如,在這些語言也很難忘記的成語......

Ruby open("file.txt").read 
Ruby File.read("file.txt") 
Python open("file.txt").read() 
+12

Java的心不是那麼糟糕,如果你知道正確的工具。 import org.apache.commons.io.FileUtils; FileUtils.readFileToString(new File(「file.txt」,「UTF-8」) – smartnut007 2011-06-18 00:01:04

+19

這個評論忽略了語言設計的重點,任何語言都可以使用簡單的庫函數來完成你想要執行的操作,作爲其函數調用語法 給定一個無限且100%記憶的庫,所有程序都將通過一個函數調用來實現 編程語言是好的,因爲它需要更少的預製組件來實現 – 2014-02-09 05:45:04

回答

378
val lines = scala.io.Source.fromFile("file.txt").mkString 

順便說一句,「scala.」是不是真的有必要,因爲它總是在範圍上不管怎樣,你可以,當然,完全或部分地導入io的內容,並避免必須添加「io」。太。

但是,上面的文件將文件打開。爲了避免出現問題,你應該關閉它是這樣的:

val source = scala.io.Source.fromFile("file.txt") 
val lines = try source.mkString finally source.close() 

與上面的代碼中的另一個問題是,它是可怕的慢,因爲它實現本質。對於大文件應該使用:「你爲什麼要在整個文件中讀取」

source.getLines mkString "\n" 
+0

啊,肯定比betterLeft(_ + _)更好 – 2009-08-16 15:06:55

+41

我對派對來說太遲了,但我討厭別人不知道他們可以做「io.File(」/ etc/passwd 「).slurp」in trunk。 – extempore 2009-08-26 03:48:13

+6

我討厭Scala 2.8有一個名爲「'slurp'」的方法,但似乎我卡在了w無論如何。 – 2009-08-26 13:34:18

34
// for file with utf-8 encoding 
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString 
+6

將「getLines」添加到原始答案將刪除所有換行符。應該是「Source.fromFile(」file.txt「,」utf-8「)。mkString」。 – Joe23 2010-12-16 10:54:23

+9

另請參閱我在Daniel C. Sobral的回答中的評論 - 此用法不會關閉Source實例,因此Scala可能會保留該文件的鎖定。 – djb 2011-08-01 02:51:39

1

最明顯的問題是這顯然不是一個可擴展的解決方案,如果你的文件變得非常大。 scala.io.SourcegetLines方法給你回Iterator[String],這是非常有用和簡潔。

這不是太大的工作拿出使用底層Java IO公用事業一個File,一個ReaderInputStreamString轉換的隱式轉換。我認爲可擴展性的缺乏意味着他們是正確的,不會將其添加到標準API中。

+11

認真嗎?你經常讀到多少個文件,這些文件在內存中存在真正的問題?絕大多數我曾經處理過的程序中的絕大多數文件都足夠小,以適應內存。坦率地說,大數據文件是例外情況,如果您要閱讀/編寫大數據文件,您應該認識到並編寫相應的程序。 – Christopher 2009-08-17 15:24:01

+8

oxbow_lakes,我不同意。很多情況下,小型文件的大小在未來不會增長。 – 2009-08-18 16:37:32

+4

我同意他們是例外 - 但我認爲這就是爲什麼讀取整個文件到內存不在JDK或Scala SDK中。這是一個3行實用程序方法供您自己編寫:克服它 – 2009-08-18 17:16:20

50

只是對丹尼爾的解決方案擴展,可以極大地插入以下導入到任何文件,該文件要求文件操作縮短東西:

import scala.io.Source._ 

有了這個,你現在可以做的:

val lines = fromFile("file.txt").getLines 

我會小心閱讀整個文件到一個String。這是一種非常不好的習慣,會比你想象的更快,更難咬你。 getLines方法返回類型爲Iterator[String]的值。它實際上是一個懶惰的光標到文件中,允許您檢查所需的數據而不會冒內存過剩的風險。

哦,並回答你的隱含問題Source:是的,它是規範的I/O庫。大多數代碼由於其較低級別的接口以及與現有框架的更好兼容性而最終使用java.io,但任何有選擇權的代碼都應該使用Source,特別是對於簡單文件操作。

+0

好的。我對Source的負面印象有一個故事:我曾經處於與現在不同的情況,那裏有一個非常大的文件,不適合記憶。使用Source導致程序崩潰;原來它試圖一次讀完所有的東西。 – 2009-08-18 16:38:57

+6

源不應該將整個文件讀入內存。如果你在getLines之後使用toList,或者其他一些會產生集合的方法,那麼你將所有東西都放到內存中。現在,Source是一個* hack *,意在完成工作,而不是經過深思熟慮的庫。它將在Scala 2.8中得到改進,但Scala社區絕對有機會積極地定義一個好的I/O API。 – 2009-08-18 21:08:27

6

我被告知Source.fromFile有問題。就我個人而言,我在用Source.fromFile打開大文件時遇到了問題,不得不求助於Java InputStreams。

另一個有趣的解決方案是使用scalax。下面是在使用ManagedResource爲打開與scalax助手一個文件打開一個日誌文件中的一些很好的註釋代碼示例:http://pastie.org/pastes/420714

+1

+ +1提及scalax – 2009-08-20 20:15:37

24

(編輯:這並不在斯卡拉2.9工作,也許不是2.8或者)

使用後備箱:

scala> io.File("/etc/passwd").slurp 
res0: String = 
## 
# User Database 
# 
... etc 
+13

「'slurp'」?我們真的放棄了明顯,直觀的名字嗎? 「slurp」的問題在於,事後對於以英語作爲第一語言的人來說可能是有意義的,至少,但你永遠不會想到它開始! – 2009-08-26 13:32:23

+4

只是偶然發現了這個問題/答案。 'File'不再在2.8.0中,不是嗎? – huynhjl 2010-02-21 05:38:11

+3

您仍然可以從scala.tools.nsc.io.File中偷取它,但我認爲該位置可能會在未來發生變化,因此請自擔風險。 ;-)哦,讓我來說說我多麼討厭「slurp」這個名字。 – Steve 2010-08-03 15:01:38

2

作爲少數人提到scala.io.Source是最好避免由於連接泄漏。

直到新的孵化器項目(即scala-io)被合併後,像commons-io這樣的可能scalax和純Java庫纔是最好的選擇。

5

使用getLines()上使用了哪些字符,行終結scala.io.Source丟棄(\ n,\ r,\ r \ n等)

下應該保留它的字符換字符,並沒有做過多的字符串連接(性能問題):

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

打印每行,如採用Java的BufferedReader讀ervery線,並打印:

scala.io.Source.fromFile("test.txt").foreach{ print } 

相當於:

scala.io.Source.fromFile("test.txt").foreach(x => print(x)) 
2

您還可以使用Scala io的Path來讀取和處理文件。

import scalax.file.Path 

現在你可以使用這個獲取文件的路徑: -

val filePath = Path("path_of_file_to_b_read", '/') 
val lines = file.lines(includeTerminator = true) 

您還可以終止,但默認情況下它被設置爲false ..

2

對於更快的整體讀取/上傳(大)文件,考慮增大尺寸bufferSizeSource.DefaultBufSize設爲2048),例如如下,

val file = new java.io.File("myFilename") 
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2) 

注意Source.scala。有關進一步的討論,請參閱Scala fast text file read and upload to memory

4

就像在Java中,使用CommonsIO庫:

FileUtils.readFileToString(file, StandardCharsets.UTF_8) 

而且,很多答案忘在這裏字符集。最好總是明確地提供它,否則它會在一天內出現。

2

爲了模擬打開和讀取文件的Ruby語法(並傳達語義),請考慮這個隱式類(Scala 2。10和上部),

import java.io.File 

def open(filename: String) = new File(filename) 

implicit class RichFile(val file: File) extends AnyVal { 
    def read = io.Source.fromFile(file).getLines.mkString("\n") 
} 

以這種方式,

open("file.txt").read 
2

一個更:https://github.com/pathikrit/better-files#streams-and-codecs

各種方式來發出聲音文件而不內容加載到存儲器:

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。默認值,如果不提供一個):

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") 
0

你並不需要解析每一個線,然後再次將它們連接起來......

Source.fromFile(path)(Codec.UTF8).mkString 

我更喜歡使用這樣的:

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 
} 
10
import java.nio.charset.StandardCharsets._ 
import java.nio.file.{Files, Paths} 

new String(Files.readAllBytes(Paths.get("file.txt")), UTF_8) 

控制字符編碼,沒有資源清理。此外,還進行了優化,因爲Files.readAllBytes分配了一個與文件大小相匹配的字節數組。

0
import scala.io.source 
object ReadLine{ 
def main(args:Array[String]){ 
if (args.length>0){ 
for (line <- Source.fromLine(args(0)).getLine()) 
println(line) 
} 
} 

在爭論你可以給文件的路徑,它將返回所有行

+0

這是什麼提供其他答案沒有? – jwvh 2017-07-23 21:58:57

+0

還沒有看到其他答案......只是以爲我可以在這裏發佈這麼貼......希望這不會傷害任何人:) – Apurw 2017-07-24 14:52:33

+0

你真的應該閱讀它們。大多數信息豐富。即使是8歲的人也有相關的信息。 – jwvh 2017-07-24 16:37:19