อ่านไฟล์ทั้งหมดใน Scala?


312

วิธีที่ง่ายและเป็นที่ยอมรับในการอ่านไฟล์ทั้งหมดในหน่วยความจำใน Scala คืออะไร (เป็นการดีที่ควบคุมการเข้ารหัสอักขระ)

สิ่งที่ดีที่สุดที่ฉันสามารถทำได้คือ:

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

หรือฉันควรจะใช้หนึ่งในสำนวนเทพเจ้าอันยิ่งใหญ่ของJavaที่ดีที่สุดที่ (โดยไม่ต้องใช้ห้องสมุดภายนอก) น่าจะเป็น:

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 ไม่ได้แย่ขนาดนั้นถ้าคุณรู้จักเครื่องมือที่เหมาะสม นำเข้า org.apache.commons.io.FileUtils; FileUtils.readFileToString (ไฟล์ใหม่ ("file.txt", "UTF-8")
smartnut007

25
ความคิดเห็นนี้ไม่ตรงกับจุดของการออกแบบภาษา ภาษาใด ๆ ก็ตามที่มีฟังก์ชั่นไลบรารีอย่างง่ายสำหรับการดำเนินการที่คุณต้องการจึงเป็นสิ่งที่ดีเท่ากับไวยากรณ์การเรียกใช้ฟังก์ชัน ด้วยห้องสมุดที่ไม่มีที่สิ้นสุดและจดจำได้ 100% โปรแกรมทั้งหมดจะถูกนำไปใช้งานด้วยการเรียกใช้ฟังก์ชันเดียว ภาษาการเขียนโปรแกรมเป็นสิ่งที่ดีเมื่อต้องการส่วนประกอบ pre-fab น้อยลงที่มีอยู่แล้วเพื่อให้ได้ผลลัพธ์ที่เฉพาะเจาะจง
Chris Mountford

คำตอบ:


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

48
ฉันไปปาร์ตี้สายเกินไป แต่ฉันเกลียดคนที่ไม่รู้ว่าพวกเขาสามารถทำ "io.File (" / etc / passwd "). slurp" ในท้ายรถ
psp

28
@extempore หากคุณคิดว่าฉันไม่เห็นคุณค่าจริงๆฉันขอโทษจริงๆ ฉันซาบซึ้งอย่างยิ่งที่คุณสนับสนุนภาษาสกาลาและทุกครั้งที่คุณมองปัญหาที่ฉันนำมาเสนอแนะวิธีแก้ไขปัญหาที่ฉันมีหรืออธิบายบางอย่างให้ฉัน ฉันจะใช้โอกาสนี้เพื่อขอบคุณสำหรับการเปลี่ยน scala.io เป็นสิ่งที่ดีและคุ้มค่า ฉันจะเป็นแกนนำในการขอบคุณของฉันต่อจากนี้ไป แต่ฉันยังคงเกลียดชื่อขอโทษ
Daniel C. Sobral

49
"slurp" เป็นชื่อสำหรับอ่านไฟล์ทั้งหมดพร้อมกันใน Perl เป็นเวลาหลายปี Perl มีประเพณีการตั้งชื่อที่ไม่เป็นทางการมากกว่าภาษาตระกูล C ซึ่งบางคนอาจพบว่าน่ารังเกียจ แต่ในกรณีนี้ฉันคิดว่ามันเหมาะ: มันเป็นคำที่น่าเกลียดสำหรับการปฏิบัติที่น่าเกลียด เมื่อคุณ slurp () คุณรู้ว่าคุณกำลังทำอะไรซนเพราะคุณต้องพิมพ์มัน
Marcus Downing

15
File.read () จะเป็นชื่อที่ดีกว่าและสอดคล้องกับ Ruby และ Python นอกเหนือจากนี้
เบรนแดน OConnor

26
@extempore: คุณไม่สามารถหยุดคนไม่ให้ถูกรังเกียจได้ มันเป็นอย่างที่มันเป็น ไม่ควรรบกวนคุณว่าบางคนไม่ชอบทุกตัวเลือกที่คุณทำ นั่นคือชีวิตเพียงแค่คุณไม่สามารถโปรดทุกคน :)
อเล็กซ์ Baranosky

58

เพียงเพื่อขยายโซลูชันของ Daniel คุณสามารถย่อสิ่งต่าง ๆ ให้สั้นลงอย่างมากโดยการแทรกการนำเข้าต่อไปนี้ลงในไฟล์ใด ๆ ที่ต้องมีการจัดการไฟล์:

import scala.io.Source._

ด้วยสิ่งนี้คุณสามารถทำได้:

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

Stringผมจะมีความระมัดระวังในการอ่านไฟล์ทั้งหมดลงในหนึ่งเดียว มันเป็นนิสัยที่แย่มากซึ่งจะกัดคุณเร็วและหนักกว่าที่คุณคิด วิธีการส่งกลับค่าของประเภทgetLines Iterator[String]เป็นเคอร์เซอร์ขี้เกียจที่มีประสิทธิภาพในไฟล์ช่วยให้คุณสามารถตรวจสอบข้อมูลที่คุณต้องการโดยไม่ต้องเสี่ยงกับหน่วยความจำเหลือเฟือ

โอ้และเพื่อตอบคำถามโดยนัยของคุณเกี่ยวกับSource: ใช่เป็นห้องสมุด I / O ที่เป็นที่ยอมรับ รหัสส่วนใหญ่จบลงด้วยการใช้งานjava.ioเนื่องจากอินเตอร์เฟสระดับต่ำกว่าและเข้ากันได้ดีกับเฟรมเวิร์กที่มีอยู่ แต่รหัสใด ๆ ที่มีตัวเลือกควรใช้Sourceโดยเฉพาะอย่างยิ่งสำหรับการจัดการไฟล์อย่างง่าย


ตกลง. มีเรื่องราวสำหรับความประทับใจเชิงลบของฉันเกี่ยวกับแหล่งที่มา: ครั้งหนึ่งฉันเคยอยู่ในสถานการณ์ที่แตกต่างจากตอนนี้ที่ฉันมีไฟล์ขนาดใหญ่มากซึ่งไม่เหมาะกับความทรงจำ การใช้ซอร์สทำให้โปรแกรมขัดข้อง มันกลับกลายเป็นว่าพยายามอ่านข้อความทั้งหมดในครั้งเดียว
เบรนแดน OConnor

7
ซอร์สไม่ควรอ่านไฟล์ทั้งหมดลงในหน่วยความจำ ถ้าคุณใช้ toList หลังจาก getLines หรือวิธีอื่นที่จะสร้างคอลเลกชันคุณจะได้ทุกอย่างเข้าสู่หน่วยความจำ ตอนนี้ Source คือแฮ็คที่ตั้งใจจะทำงานให้เสร็จไม่ใช่ห้องสมุดที่คิดอย่างรอบคอบ มันจะได้รับการปรับปรุงใน Scala 2.8 แต่มีโอกาสอย่างแน่นอนที่ชุมชน Scala จะสามารถใช้งานได้ในการกำหนด I / O API ที่ดี
Daniel C. Sobral

36
// 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

9
ดูความคิดเห็นของฉันในคำตอบของ Daniel C. Sobral - การใช้งานนี้จะไม่ปิดอินสแตนซ์ต้นทางดังนั้น Scala อาจเก็บล็อกไฟล์ไว้
djb

26

(แก้ไข: สิ่งนี้ใช้ไม่ได้ในสกาลา 2.9 และอาจไม่ใช่ 2.8 ก็ได้)

ใช้ลำตัว:

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

14
" slurp" พวกเรามีชื่อที่ชัดเจนและเข้าใจง่ายอย่างแท้จริงหรือไม่? ปัญหาslurpก็คือว่ามันอาจทำให้รู้สึกถึงความเป็นจริงสำหรับคนที่มีภาษาอังกฤษเป็นภาษาแรกเป็นอย่างน้อย แต่คุณจะไม่คิดว่ามันจะเริ่มต้นด้วย!
Daniel C. Sobral

5
เพิ่งสะดุดกับคำถาม / คำตอบนี้ Fileไม่ได้อยู่ใน 2.8.0 อีกต่อไปใช่ไหม?
huynhjl

4
slurp ฟังดูดี :) ฉันไม่คาดหวัง แต่ฉันไม่ได้คาดหวังเอาท์พุทไปยังหน้าจอว่าชื่อ 'พิมพ์' เช่นกัน slurpมันยอดเยี่ยมมาก! :) ยอดเยี่ยมใช่ไหม ฉันไม่พบมัน ; (
ไม่ทราบผู้ใช้

5
ใน scala-2.10.0 ชื่อแพคเกจคือ scala.reflect.io.File และมีคำถามเกี่ยวกับ "ไฟล์" นี้ ก่อนหน้าเหตุใดไฟล์นี้จึงถูกทำเครื่องหมายว่า "ทดลอง" ปลอดภัยไหม มันเป็นการล็อคระบบไฟล์หรือไม่?
VasiliNovikov

4
slurp มีประวัติอันยาวนานสำหรับจุดประสงค์นี้มีต้นกำเนิดมาจาก 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)

ควบคุมการเข้ารหัสอักขระและไม่มีทรัพยากรในการล้างข้อมูล นอกจากนี้อาจปรับให้เหมาะสม (เช่นการFiles.readAllBytesจัดสรรอาร์เรย์ไบต์ที่เหมาะสมกับขนาดของไฟล์)


7

ฉันได้รับแจ้งว่า Source.fromFile นั้นเป็นปัญหา โดยส่วนตัวฉันมีปัญหาในการเปิดไฟล์ขนาดใหญ่ด้วย Source.fromFile และต้องหันไปใช้ Java InputStreams

อีกวิธีที่น่าสนใจคือการใช้ scalax นี่คือตัวอย่างของโค้ดที่มีการคอมเม้นต์ที่เปิดไฟล์บันทึกโดยใช้ ManagedResource เพื่อเปิดไฟล์ด้วย scalax helpers: http://pastie.org/pastes/420714


6

การใช้ 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)
}

6

อีกหนึ่งอย่าง: https://github.com/pathikrit/better-files#streams-and-codecs

วิธีต่างๆในการ slurp ไฟล์โดยไม่โหลดเนื้อหาลงในหน่วยความจำ:

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 หากคุณไม่ได้เตรียมไว้):

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 โดยใช้ไลบรารี CommonsIO:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

นอกจากนี้คำตอบมากมายที่นี่ลืม Charset มันจะดีกว่าที่จะให้มันอย่างชัดเจนเสมอหรือจะตีหนึ่งวัน


4

สำหรับการเลียนแบบรูทไวยากรณ์ (และถ่ายทอดความหมาย) ของการเปิดและอ่านไฟล์ให้พิจารณาคลาส implicit นี้ (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

3

ในฐานะที่เป็นคนไม่กี่คนที่กล่าวถึงscala.io.Source ที่ดีที่สุดที่จะหลีกเลี่ยงเนื่องจากการรั่วไหลของการเชื่อมต่อ

scalax และ libs java ที่บริสุทธิ์อย่างคงที่เช่นคอมมอนส์ - ไอโอเป็นตัวเลือกที่ดีที่สุดจนกว่าโครงการศูนย์บ่มเพาะใหม่ (เช่น scala-io) จะถูกรวมเข้าด้วยกัน


3

คุณยังสามารถใช้เส้นทางจาก scala io เพื่ออ่านและประมวลผลไฟล์

import scalax.file.Path

ตอนนี้คุณสามารถรับพา ธ ไฟล์โดยใช้สิ่งนี้: -

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

นอกจากนี้คุณยังสามารถรวมเทอร์มิเนเตอร์ แต่โดยค่าเริ่มต้นมันถูกตั้งค่าเป็นเท็จ


3

สำหรับการอ่าน / อัปโหลดไฟล์ (ใหญ่) ที่เร็วขึ้นโดยรวมลองพิจารณาเพิ่มขนาดของbufferSize( Source.DefaultBufSizeตั้งค่าเป็น2048) ตัวอย่างเช่น

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

หมายเหตุSource.scala สำหรับการอภิปรายต่อไปดูScala รวดเร็วอ่านไฟล์ข้อความและอัปโหลดไปยังหน่วยความจำ


3

คุณไม่จำเป็นต้องวิเคราะห์คำทุกบรรทัดแล้วต่อกันอีกครั้ง ...

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
}

คุณควรปิดสตรีม - หากเกิดข้อผิดพลาดในval content = source.mkString
Andrzej Jozwik

+1 Codecสำหรับ ฉันได้รับการทดสอบล้มเหลวsbt testเนื่องจากไม่สามารถตั้งค่าได้ในขณะที่คำสั่งทดสอบของ Intellij ผ่านการทดสอบทั้งหมด และคุณสามารถใช้def usingจากนี้ได้
Mikhail

3

หากคุณไม่ทราบการพึ่งพาบุคคลที่สามคุณควรพิจารณาใช้ของฉันห้องสมุด OS-Lib ทำให้การอ่าน / เขียนไฟล์และทำงานกับระบบไฟล์สะดวกมาก:

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

มีผู้ช่วยเหลือหนึ่งบรรทัดสำหรับการอ่านไบต์ , อ่านชิ้น , อ่านบรรทัดและการดำเนินงานทั่วไปอื่น ๆ อีกมากมายที่มีประโยชน์ /


2

คำถามที่ชัดเจนคือ"ทำไมคุณต้องการอ่านไฟล์ทั้งหมด?" เห็นได้ชัดว่านี่ไม่ใช่โซลูชันที่ปรับขนาดได้หากไฟล์ของคุณมีขนาดใหญ่มาก scala.io.Sourceช่วยให้คุณกลับมาIterator[String]จากgetLinesวิธีการที่เป็นประโยชน์อย่างมากและรัดกุม

มันไม่มากของงานที่จะเกิดขึ้นกับการแปลงโดยปริยายใช้ Java พื้นฐานสาธารณูปโภค IO การแปลงFileเป็นReaderหรือไปInputStream Stringฉันคิดว่าการขาดความสามารถในการปรับขยายได้หมายความว่าพวกเขาถูกต้องไม่เพิ่มลงใน API มาตรฐาน


12
อย่างจริงจัง? มีกี่ไฟล์ที่คุณอ่านเป็นประจำที่มีปัญหาจริงในหน่วยความจำ? ไฟล์ส่วนใหญ่ในโปรแกรมส่วนใหญ่ที่ฉันเคยจัดการมีขนาดเล็กพอที่จะใส่ลงในหน่วยความจำ ตรงไปตรงมาไฟล์ข้อมูลขนาดใหญ่เป็นข้อยกเว้นและคุณควรตระหนักว่าและโปรแกรมตามนั้นถ้าคุณกำลังจะอ่าน / เขียนพวกเขา
คริส

8
oxbow_lakes ฉันไม่เห็นด้วย มีหลายสถานการณ์ที่เกี่ยวข้องกับไฟล์ขนาดเล็กซึ่งขนาดจะไม่เติบโตในอนาคต
Brendan OConnor

4
ฉันยอมรับว่าพวกเขาเป็นข้อยกเว้น - แต่ฉันคิดว่านั่นเป็นสาเหตุที่การอ่านไฟล์ทั้งหมดลงในหน่วยความจำไม่ได้อยู่ใน JDK หรือ Scala SDK เป็นวิธีอรรถประโยชน์ 3 บรรทัดสำหรับคุณที่จะเขียนด้วยตัวคุณเอง: เอาชนะมันได้
oxbow_lakes

1

พิมพ์ทุกบรรทัดเช่นใช้ Java BufferedReader อ่าน ervery บรรทัดและพิมพ์:

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

เทียบเท่า:

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

ในข้อโต้แย้งที่คุณสามารถให้เส้นทางของไฟล์และมันจะกลับมาทุกบรรทัด


3
ข้อเสนอนี้อะไรที่คำตอบอื่น ๆ ไม่ได้?
jwvh

ไม่ได้เห็นคำตอบอื่น ๆ ... เพียงแค่คิดว่าฉันสามารถมีส่วนร่วมที่นี่โพสต์ดังนั้น ... หวังว่าจะไม่เป็นอันตรายต่อทุกคน :)
Apurw

1
คุณควรอ่านพวกเขาจริงๆ ส่วนใหญ่มีข้อมูลค่อนข้าง แม้แต่คนที่อายุ 8 ปีก็ยังมีข้อมูลที่เกี่ยวข้อง
jwvh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.