Scala'daki dosyanın tamamı okunuyor mu?


312

Scala'da bir dosyanın tamamını belleğe okumanın basit ve standart bir yolu nedir? (İdeal olarak, karakter kodlaması üzerinde kontrol ile.)

En iyi ben gelebilir:

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

ya da en iyi (harici bir kütüphane kullanmadan) gibi görünen Java'nın tanrısal korkunç deyimlerinden birini kullanmam gerekiyor mu?

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

Posta listesi tartışmalarını okurken, scala.io.Source'un kanonik I / O kütüphanesi olduğu bile açık değil. Amaçlanan amacının tam olarak ne olduğunu anlamıyorum.

... çok basit ve hatırlaması kolay bir şey istiyorum. Örneğin, bu dillerde deyimi unutmak çok zor ...

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

12
Doğru araçları biliyorsanız Java o kadar da kötü değildir. import org.apache.commons.io.FileUtils; FileUtils.readFileToString (yeni Dosya ("file.txt", "UTF-8")
smartnut007

25
Bu yorum, dil tasarımının konusunu özlüyor. Bu nedenle, tam olarak gerçekleştirmek istediğiniz işlem için basit bir kütüphane işlevi olan herhangi bir dil, işlev çağırma sözdizimi kadar iyidir. Sonsuz ve% 100 hafızaya alınmış bir kütüphane verildiğinde, tüm programlar tek bir işlev çağrısı ile uygulanacaktır. Bir programlama dili, belirli bir sonuç elde etmek için önceden var olan daha az fab bileşenine ihtiyaç duyduğunda iyidir.
Chris Mountford

Yanıtlar:


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

Bu arada, " scala." her zaman zaten kapsamda olduğu için gerçekten gerekli değildir ve elbette, io'nun içeriğini tamamen veya kısmen içe aktarabilir ve "io" nun önüne geçmekten kaçınabilirsiniz. çok.

Ancak yukarıdakiler dosyayı açık bırakır. Sorunlardan kaçınmak için şu şekilde kapatmalısınız:

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

Yukarıdaki kodla ilgili bir başka sorun, uygulama niteliği nedeniyle korkunç yavaş olmasıdır. Daha büyük dosyalar için aşağıdakiler kullanılmalıdır:

source.getLines mkString "\n"

48
Partiye çok geç kaldım, ama insanların bagajda "io.File (" / etc / passwd "). Slurp" yapabileceklerini bilmemelerinden nefret ediyorum.
psp

28
@extempore Gerçekten minnettar olmadığımı düşünüyorsanız, gerçekten üzgünüm. Scala diline desteğinizi derinden takdir ediyorum ve her seferinde kişisel olarak ortaya koyduğum bir konuya baktığımda, yaşadığım bir soruna bir çözüm önerdim veya bana bir şeyler anlattım. O zaman scala.io'yu iyi ve değerli bir şeye dönüştürdüğünüz için teşekkür etme fırsatını bulacağım. Bundan sonra teşekkürlerimde daha vokal olacağım, ama yine de isimden nefret ediyorum, üzgünüm.
Daniel C.Sobral

49
"slurp", Perl'de yıllardır bir dosyanın tamamını okumak için kullanılan isim olmuştur. Perl, bazılarının tatsız bulabileceği C dil ailesinden daha viseral ve gayri resmi bir adlandırma geleneğine sahiptir, ancak bu durumda uygun olduğunu düşünüyorum: çirkin bir uygulama için çirkin bir kelime. Slurp () yaptığınızda, yaramaz bir şey yaptığınızı biliyorsunuz çünkü sadece bunu yazmak zorundaydınız.
Marcus Downing

15
File.read () daha güzel bir isim olurdu ve Ruby ve Python ile uyumludur.
Brendan OConnor

26
@extempore: İnsanların tiksinmesini engelleyemezsiniz. Sadece o yol var. Bazı insanların yaptığınız her seçeneği beğenmemesi sizi rahatsız etmemelidir. Bu sadece hayat, herkesi memnun edemezsin :)
Alex Baranosky

58

Sadece Daniel'in çözümünü genişletmek için, dosya aktarımı gerektiren herhangi bir dosyaya aşağıdaki içe aktarmayı ekleyerek işleri büyük ölçüde kısaltabilirsiniz:

import scala.io.Source._

Bununla artık şunları yapabilirsiniz:

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

Bütün bir dosyayı tek bir dosyaya okurken dikkatli olurdum String. Bu çok kötü bir alışkanlıktır, sizi düşündüğünüzden daha erken ve daha sert ısırır. getLinesYöntem türü bir değer verir Iterator[String]. Dosyaya etkili bir şekilde tembel bir imleç, sadece bellek glutunu riske atmadan ihtiyacınız olan verileri incelemenize izin verir.

Oh, ve zımni sorunuzu cevaplamak için Source: evet, kanonik I / O kütüphanesi. Çoğu kod java.io, daha düşük seviyeli arayüzü ve mevcut çerçevelerle daha iyi uyumluluğu nedeniyle kullanılır , ancak Sourceözellikle basit dosya manipülasyonu için seçeneği olan herhangi bir kod kullanılmalıdır .


TAMAM. Kaynak hakkındaki olumsuz izlenimim için bir hikaye var: Bir zamanlar şu andan farklı bir durumdaydım, belleğe sığmayan çok büyük bir dosyam vardı. Source kullanmak programın çökmesine neden oldu; her şeyi bir anda okumaya çalıştığı ortaya çıktı.
Brendan OConnor

7
Kaynağın tüm dosyayı belleğe okuması gerekmez. GetLines'den sonra toList veya bir koleksiyon oluşturacak başka bir yöntem kullanırsanız, her şeyi belleğe alırsınız. Şimdi, Kaynak dikkatle düşünülmüş bir kütüphane değil, işi tamamlamaya yönelik bir hack'tir . Scala 2.8'de geliştirilecek, ancak Scala topluluğunun iyi bir I / O API'sini tanımlamada aktif hale gelmesi için kesinlikle bir fırsat var.
Daniel C. Sobral

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

6
Orijinal cevaba "getLines" eklenmesi tüm yeni satırları silecektir. "Source.fromFile (" file.txt "," utf-8 "). MkString" olmalıdır.
Joe23

9
Ayrıca Daniel C. Sobral'ın cevabındaki yorumuma bakın - bu kullanım Kaynak örneğini kapatmaz, bu nedenle Scala dosyada bir kilit tutabilir.
djb

26

(DÜZENLEME: Bu scala 2.9'da çalışmaz ve belki de 2.8'de çalışmaz)

Bagaj kullanın:

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

14
" slurp"? Gerçekten bariz, sezgisel bir isim mi bıraktık? Sorun slurpşu ki, en azından ilk dil olarak İngilizce olan birine, mantıklı gelebilir, ama asla bununla başlamayı düşünemezsiniz!
Daniel C.Sobral

5
Sadece bu soru / cevap tökezledi. Fileartık 2.8.0'da değil, değil mi?
huynhjl

4
slurp kulağa hoş geliyor. :) Bunu beklemiyordum ama ekrana çıktıların da 'print' olarak adlandırılmasını beklemiyordum. slurpharika! :) Harikaydı? Ben bulamıyorum. ; (
kullanıcı bilinmiyor

5
scala-2.10.0 da paket adı scala.reflect.io.File Ve bu "Dosya" hakkında bir soru. extempore, bu dosya neden "deneysel" olarak işaretlenmiş? Güvenli mi? Dosya sistemine kilit açıyor mu?
VasiliNovikov

4
slurp bu amaçla uzun bir geçmişe sahip, sanırım, perl
Chris Mountford

18
import java.nio.charset.StandardCharsets._
import java.nio.file.{Files, Paths}

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

Karakter kodlaması üzerinde kontrol ve temizlemek için kaynak yok. Ayrıca, muhtemelen optimize edilmiştir (örneğin Files.readAllBytesdosyanın boyutuna uygun bir bayt dizisi tahsis etmek).


7

Bana Source.fromFile sorunlu olduğu söylendi. Şahsen, Source.fromFile ile büyük dosyaları açmakta sorun yaşadım ve Java InputStreams başvurmak zorunda kaldım.

Bir başka ilginç çözüm skalaks kullanmaktır. Aşağıda, scalax yardımcılarıyla bir dosyayı açmak için ManagedResource'u kullanarak bir günlük dosyasını açan iyi yorumlanmış bazı kodlara bir örnek verilmiştir: http://pastie.org/pastes/420714


6

Scala.io.Source üzerinde getLines () kullanılması, satır sonlandırıcıları için hangi karakterlerin kullanıldığını (\ n, \ r, \ r \ n, vb.) Atar.

Aşağıdakiler karakteri karakter için korumalı ve aşırı dize birleştirme (performans sorunları) yapmamalıdır:

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

6

Bir tane daha: https://github.com/pathikrit/better-files#streams-and-codecs

İçeriği belleğe yüklemeden bir dosyayı karıştırmanın çeşitli yolları:

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 

Okuma / yazma işlemi yapan her şey için kendi kodekinizi de sağlayabilirsiniz (bir tane sağlamazsanız scala.io.Codec.default varsayar):

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

5

Java'da olduğu gibi CommonsIO kütüphanesini kullanarak:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

Ayrıca, burada birçok cevap Charset'i unutuyor. Her zaman açıkça sağlamak daha iyidir, yoksa bir gün vuracaktır.


4

Bir dosyanın açılması ve okunması için Ruby sözdizimini taklit etmek (ve anlambilimi iletmek) için, bu örtülü sınıfı (Scala 2.10 ve üstü),

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

Böylece,

open("file.txt").read

3

birkaç kişi gibi scala.io.Source bağlantı sızıntıları nedeniyle kaçınılması en iyisidir.

Muhtemelen scalax ve commons-io gibi saf java kütüphaneleri, yeni inkübatör projesi (yani scala-io) birleştirilene kadar en iyi seçenektir.


3

ayrıca dosyaları okumak ve işlemek için Skala Yolunu da kullanabilirsiniz.

import scalax.file.Path

Şimdi bunu kullanarak dosya yolu alabilirsiniz: -

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

Sonlandırıcıları da dahil edebilirsiniz, ancak varsayılan olarak false değerine ayarlanır.


3

(Büyük) bir dosyanın daha hızlı okunması / yüklenmesi için örneğin bufferSize(olarak Source.DefaultBufSizeayarlanmış 2048) boyutunu aşağıdaki gibi artırmayı düşünün ,

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

Not Source.scala . Daha fazla tartışma için bkz. Scala hızlı metin dosyası okuma ve belleğe yükleme .


3

Her satırı ayrıştırıp tekrar birleştirmeniz gerekmez ...

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

Bunu kullanmayı tercih ediyorum:

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
}

Akışı kapatmalısınız - eğer hata oluşursaval content = source.mkString
Andrzej Jozwik

İçin +1 Codec. sbt testIntellij'in test komutu tüm testleri geçerken test başarısız oldu çünkü ayarlayamıyorum. Ve kullanabilirsiniz def usinggelen bu
Mikhail Ionkin

3

Üçüncü taraf bir bağımlılığın sakıncası yoksa, OS-Lib kütüphanemi kullanmayı düşünmelisiniz . Bu, dosyaları okuma / yazma ve dosya sistemiyle çalışmayı çok kolaylaştırır:

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

Tek satırlık için yardımcıları ile bayt okuma , parçalar okuyarak , satırları okuyan ve diğer birçok yararlı / ortak operasyonlar


2

Açık olan soru "neden dosyanın tamamını okumak istiyorsun?" Dosyalarınız çok büyük olursa, bu açıkça ölçeklenebilir bir çözüm değildir. scala.io.SourceBir geri verir Iterator[String]den getLinesçok faydalı ve özlü olan yöntemde,.

Bu bir dönüştürmek için altta yatan java IO yardımcı programlarını kullanarak bir örtük dönüştürme ile gelip bir iş sayılmaz File, bir Readerya da InputStreambir karşı String. Ölçeklenebilirlik eksikliğinin, bunu standart API'ye eklememek için doğru oldukları anlamına geldiğini düşünüyorum.


12
Ciddi anlamda? Belleğe uyan gerçek sorunları olan düzenli olarak kaç dosyayı gerçekten okuyorsunuz? Şimdiye kadar ele aldığım programların büyük çoğunluğundaki dosyaların büyük çoğunluğu belleğe sığacak kadar küçüktür. Açıkçası, büyük veri dosyaları istisnadır ve bunu fark etmeli ve eğer bunları okuyacak / yazacaksanız buna göre programlamalısınız.
Christopher

8
oxbow_lakes, katılmıyorum. Gelecekte boyutu büyümeyecek küçük dosyaları içeren birçok durum vardır.
Brendan OConnor

4
İstisna olduklarını kabul ediyorum - ama bence bu yüzden belleğe dosya okuma, JDK veya Scala SDK'da değil. Kendiniz yazmanız için 3 satırlık bir yardımcı yöntem: üstesinden gel
oxbow_lakes

1

Java BufferedReader'ı kullanmak gibi her satırı yazdırın ve her satırı okuyun:

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

eşdeğer:

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

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

argümanlarda dosya yolu verebilir ve tüm satırları döndürür


3
Bu diğer cevabın sunmadığı şey nedir?
jwvh

Başka cevaplar görmedim ... sadece burada katkıda bulunabileceğini düşündüm bu kadar yayınlanmış ... umarım bu kimseye zarar vermez :)
Apurw

1
Onları gerçekten okumalısın. Çoğu oldukça bilgilendiricidir. 8 yaşında olanlar bile ilgili bilgilere sahiptir.
jwvh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.