Do czytania przydatna jest abstrakcja Source. Jak mogę zapisać linie do pliku tekstowego?
Do czytania przydatna jest abstrakcja Source. Jak mogę zapisać linie do pliku tekstowego?
Odpowiedzi:
Edytuj 2019 (8 lat później), Scala-IO nie jest zbyt aktywna, jeśli w ogóle, Li Haoyi sugeruje własną bibliotekę lihaoyi/os-lib, którą przedstawia poniżej .
Czerwiec 2019, Xavier Guihot wspomina w swojej odpowiedzi o bibliotece Usingnarzędzie do automatycznego zarządzania zasobami.
Edycja (wrzesień 2011): odkąd Eduardo Costa pyta o Scala2.9 i od kiedy Rick-777 komentuje, że historia zmian scalax.IO praktycznie nie istnieje od połowy 2009 roku ...
Scala-IO zmieniło miejsce: zobacz repozytorium GitHub od Jesse Eichara (również na SO ):
Projekt parasolowy Scala IO składa się z kilku podprojektów dotyczących różnych aspektów i rozszerzeń IO.
Istnieją dwa główne składniki Scala IO:
- Core - Core zajmuje się głównie odczytywaniem i zapisywaniem danych do iz dowolnych źródeł i ujść. Kamień węgielny cechy są
Input,OutputiSeekablektóre stanowią rdzeń API.
Inne klasy znaczenie mająResource,ReadCharsiWriteChars.- Plik - plik jest
File(nazywanymPath) interfejsem API opartym na połączeniu systemu plików Java 7 NIO i interfejsów API SBT PathFinder.
PathiFileSystemsą głównymi punktami wejścia do Scala IO File API.
import scalax.io._
val output:Output = Resource.fromFile("someFile")
// Note: each write will open a new connection to file and
// each write is executed at the begining of the file,
// so in this case the last write will be the contents of the file.
// See Seekable for append and patching files
// Also See openOutput for performing several writes with a single connection
output.writeIntsAsBytes(1,2,3)
output.write("hello")(Codec.UTF8)
output.writeStrings(List("hello","world")," ")(Codec.UTF8)
Oryginalna odpowiedź (styczeń 2011), ze starym miejscem na scala-io:
Jeśli nie chcesz czekać na Scala2.9, możesz skorzystać z biblioteki scala-incubator / scala-io .
(jak wspomniano w sekcji „ Dlaczego Scala Source nie zamyka bazowego InputStream? ”)
Zobacz próbki
{ // several examples of writing data
import scalax.io.{
FileOps, Path, Codec, OpenOption}
// the codec must be defined either as a parameter of ops methods or as an implicit
implicit val codec = scalax.io.Codec.UTF8
val file: FileOps = Path ("file")
// write bytes
// By default the file write will replace
// an existing file with the new data
file.write (Array (1,2,3) map ( _.toByte))
// another option for write is openOptions which allows the caller
// to specify in detail how the write should take place
// the openOptions parameter takes a collections of OpenOptions objects
// which are filesystem specific in general but the standard options
// are defined in the OpenOption object
// in addition to the definition common collections are also defined
// WriteAppend for example is a List(Create, Append, Write)
file.write (List (1,2,3) map (_.toByte))
// write a string to the file
file.write("Hello my dear file")
// with all options (these are the default options explicitely declared)
file.write("Hello my dear file")(codec = Codec.UTF8)
// Convert several strings to the file
// same options apply as for write
file.writeStrings( "It costs" :: "one" :: "dollar" :: Nil)
// Now all options
file.writeStrings("It costs" :: "one" :: "dollar" :: Nil,
separator="||\n||")(codec = Codec.UTF8)
}
Jest to jedna z funkcji brakujących w standardowej Scali, która okazała się tak przydatna, że dodaję ją do mojej osobistej biblioteki. (Prawdopodobnie powinieneś też mieć osobistą bibliotekę.) Kod wygląda tak:
def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
val p = new java.io.PrintWriter(f)
try { op(p) } finally { p.close() }
}
i jest używany w ten sposób:
import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
data.foreach(p.println)
}
Source(domyślne kodowanie). Możesz oczywiście dodać np. enc: Option[String] = NoneParametr po, fjeśli uznasz to za powszechną potrzebę.
Podobna do odpowiedzi Rexa Kerra, ale bardziej ogólna. Najpierw używam funkcji pomocniczej:
/**
* Used for reading/writing to database, files, etc.
* Code From the book "Beginning Scala"
* http://www.amazon.com/Beginning-Scala-David-Pollak/dp/1430219890
*/
def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B =
try { f(param) } finally { param.close() }
Następnie używam tego jako:
def writeToFile(fileName:String, data:String) =
using (new FileWriter(fileName)) {
fileWriter => fileWriter.write(data)
}
i
def appendToFile(fileName:String, textData:String) =
using (new FileWriter(fileName, true)){
fileWriter => using (new PrintWriter(fileWriter)) {
printWriter => printWriter.println(textData)
}
}
itp.
Prosta odpowiedź:
import java.io.File
import java.io.PrintWriter
def writeToFile(p: String, s: String): Unit = {
val pw = new PrintWriter(new File(p))
try pw.write(s) finally pw.close()
}
importz których korzystasz?
Udzielenie innej odpowiedzi, ponieważ moje zmiany innych odpowiedzi zostały odrzucone.
To najbardziej zwięzła i prosta odpowiedź (podobna do odpowiedzi Garret Hall)
File("filename").writeAll("hello world")
Jest to podobne do Jus12, ale bez gadatliwości i z poprawnym stylem kodu
def using[A <: {def close(): Unit}, B](resource: A)(f: A => B): B =
try f(resource) finally resource.close()
def writeToFile(path: String, data: String): Unit =
using(new FileWriter(path))(_.write(data))
def appendToFile(path: String, data: String): Unit =
using(new PrintWriter(new FileWriter(path, true)))(_.println(data))
Zauważ, że NIE potrzebujesz nawiasów klamrowych dla try finallyani lambd i zwróć uwagę na użycie składni zastępczej. Zwróć także uwagę na lepsze nazewnictwo.
implementedwarunku wstępnego. Nie możesz użyć kodu, który nie został zaimplementowany. Chodzi mi o to, że musisz powiedzieć, jak go znaleźć, ponieważ nie jest on domyślnie dostępny i nie jest dobrze znany.
Oto zwięzły, jednowierszowy tekst wykorzystujący bibliotekę kompilatora Scala:
scala.tools.nsc.io.File("filename").writeAll("hello world")
Alternatywnie, jeśli chcesz użyć bibliotek Java, możesz to zrobić:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Jedna wkładka do zapisywania / odczytu do / z String, za pomocą java.nio.
import java.nio.file.{Paths, Files, StandardOpenOption}
import java.nio.charset.{StandardCharsets}
import scala.collection.JavaConverters._
def write(filePath:String, contents:String) = {
Files.write(Paths.get(filePath), contents.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE)
}
def read(filePath:String):String = {
Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8).asScala.mkString
}
To nie jest odpowiednie dla dużych plików, ale wystarczy.
Niektóre linki:
java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString
writeże skopiuje contentsdo nowej tablicy bajtów zamiast przesyłać ją strumieniowo do pliku, przez co w szczytowym momencie zużyje dwa razy więcej pamięci niż contentssam.
Niestety dla najlepszej odpowiedzi, Scala-IO nie żyje. Jeśli nie masz nic przeciwko korzystaniu z zależności od innej firmy, rozważ użycie mojej biblioteki OS-Lib . To sprawia, że praca z plikami, ścieżkami i systemem plików jest bardzo łatwa:
// 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")
Ma jednowierszowe do zapisywania do plików , dołączania do plików , nadpisywania plików i wielu innych użytecznych / typowych operacji
Mikro biblioteka, którą napisałem: https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
lub
file << "Hello" << "\n" << "World"
Począwszy Scala 2.13od biblioteki standardowej dostępne jest dedykowane narzędzie do zarządzania zasobami:Using .
Można go użyć w tym przypadku z zasobami takimi jak PrintWriterlub BufferedWriterktóre rozszerzają się AutoCloseable, aby zapisać do pliku i, bez względu na wszystko, później zamknąć zasób:
Na przykład w przypadku java.iointerfejsu API:
import scala.util.Using, java.io.{PrintWriter, File}
// val lines = List("hello", "world")
Using(new PrintWriter(new File("file.txt"))) {
writer => lines.foreach(writer.println)
}Lub z java.nioAPI:
import scala.util.Using, java.nio.file.{Files, Paths}, java.nio.charset.Charset
// val lines = List("hello", "world")
Using(Files.newBufferedWriter(Paths.get("file.txt"), Charset.forName("UTF-8"))) {
writer => lines.foreach(line => writer.write(line + "\n"))
}AKTUALIZACJA w dniu 2019 / wrzesień / 01:
finallyże połknął oryginał Exceptionrzucony przez, tryjeśli finallykod wyrzucił plikExceptionPo przejrzeniu wszystkich tych odpowiedzi, jak łatwo napisać plik w Scali, a niektóre z nich są całkiem fajne, miałem trzy problemy:
scala.util.Tryclosemetoda była wykonywana na każdym zasobie zależnym w odwrotnej kolejności - Uwaga: zamykanie zasobów zależnych w odwrotnej kolejności, SZCZEGÓLNIE W PRZYPADKU AWARII jest rzadko rozumianym wymogiem java.lang.AutoCloseablespecyfikacja, która często prowadzi do bardzo złośliwa i trudno znaleźć błędy i awarie w czasie runPrzed rozpoczęciem moim celem nie jest zwięzłość. Ma to na celu ułatwienie zrozumienia dla początkujących użytkowników Scala / FP, zazwyczaj tych pochodzących z języka Java. Na samym końcu połączę wszystkie bity razem, a następnie zwiększę zwięzłość.
Po pierwsze, usingmetoda musi zostać zaktualizowana, aby mogła być używana Try(ponownie, zwięzłość nie jest tutaj celem). Nazwa zostanie zmieniona na tryUsingAutoCloseable:
def tryUsingAutoCloseable[A <: AutoCloseable, R]
(instantiateAutoCloseable: () => A) //parameter list 1
(transfer: A => scala.util.Try[R]) //parameter list 2
: scala.util.Try[R] =
Try(instantiateAutoCloseable())
.flatMap(
autoCloseable => {
var optionExceptionTry: Option[Exception] = None
try
transfer(autoCloseable)
catch {
case exceptionTry: Exception =>
optionExceptionTry = Some(exceptionTry)
throw exceptionTry
}
finally
try
autoCloseable.close()
catch {
case exceptionFinally: Exception =>
optionExceptionTry match {
case Some(exceptionTry) =>
exceptionTry.addSuppressed(exceptionFinally)
case None =>
throw exceptionFinally
}
}
}
)
Początek powyższej tryUsingAutoCloseablemetody może być mylący, ponieważ wydaje się, że ma dwie listy parametrów zamiast zwyczajowej listy pojedynczych parametrów. Nazywa się to curry. I nie będę szczegółowo omawiać, jak działa curry ani gdzie występuje sporadycznie przydatne. Okazuje się, że dla tej konkretnej przestrzeni problemowej jest to odpowiednie narzędzie do pracy.
Następnie musimy stworzyć metodę, tryPrintToFilektóra utworzy (lub nadpisze istniejącą) Filei zapisze List[String]. Używa, FileWriterktóry jest zamknięty przez a, BufferedWriterktóry z kolei jest zamknięty w PrintWriter. Aby podnieść wydajność, BufferedWriterzdefiniowano domyślny rozmiar buforu znacznie większy niż domyślny dla ,defaultBufferSize i przypisano wartość 65536.
Oto kod (i znowu zwięzłość nie jest tutaj celem):
val defaultBufferSize: Int = 65536
def tryPrintToFile(
lines: List[String],
location: java.io.File,
bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
fileWriter =>
tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
bufferedWriter =>
tryUsingAutoCloseable(() => new java.io.PrintWriter(bufferedWriter)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
printWriter =>
scala.util.Try(
lines.foreach(line => printWriter.println(line))
)
}
}
}
}
Powyższa tryPrintToFilemetoda jest przydatna, ponieważ wymaga plikuList[String] dane wejściowe i wysyła je do pliku File. Utwórzmy teraz tryWriteToFilemetodę, która pobiera a Stringi zapisuje ją w pliku File.
Oto kod (i pozwolę ci odgadnąć priorytet zwięzłości tutaj):
def tryWriteToFile(
content: String,
location: java.io.File,
bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
fileWriter =>
tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
bufferedWriter =>
Try(bufferedWriter.write(content))
}
}
}
Na koniec warto mieć możliwość pobrania zawartości pliku Filejako pliku String. Chociaż scala.io.Sourcezapewnia wygodną metodę łatwego uzyskiwania zawartości pliku File, closemetoda ta musi być używana w Sourcecelu zwolnienia podstawowej maszyny JVM i uchwytów systemu plików. Jeśli tak się nie stanie, zasób nie zostanie zwolniony, dopóki JVM GC (moduł wyrzucania elementów bezużytecznych) nie wydaSource samej instancji. Nawet wtedy istnieje tylko słaba JVM gwarancja, że finalizemetoda zostanie wywołana przez GC do closezasobu. Oznacza to, że obowiązkiem klienta jest jawne wywołanie pliku . W tym celu potrzebujemy drugiej definicji metody using, która obsługuje .close metody, tak samo jak klient jest odpowiedzialny za oparcie się closena wystąpieniujava.lang.AutoCloseablescala.io.Source
Oto kod do tego (nadal nie jest zwięzły):
def tryUsingSource[S <: scala.io.Source, R]
(instantiateSource: () => S)
(transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
Try(instantiateSource())
.flatMap(
source => {
var optionExceptionTry: Option[Exception] = None
try
transfer(source)
catch {
case exceptionTry: Exception =>
optionExceptionTry = Some(exceptionTry)
throw exceptionTry
}
finally
try
source.close()
catch {
case exceptionFinally: Exception =>
optionExceptionTry match {
case Some(exceptionTry) =>
exceptionTry.addSuppressed(exceptionFinally)
case None =>
throw exceptionFinally
}
}
}
)
A oto przykład jego użycia w super prostym czytniku plików strumieniowych (obecnie używany do odczytu plików rozdzielanych tabulatorami z danych wyjściowych bazy danych):
def tryProcessSource(
file: java.io.File
, parseLine: (String, Int) => List[String] = (line, index) => List(line)
, filterLine: (List[String], Int) => Boolean = (values, index) => true
, retainValues: (List[String], Int) => List[String] = (values, index) => values
, isFirstLineNotHeader: Boolean = false
): scala.util.Try[List[List[String]]] =
tryUsingSource(scala.io.Source.fromFile(file)) {
source =>
scala.util.Try(
( for {
(line, index) <-
source.getLines().buffered.zipWithIndex
values =
parseLine(line, index)
if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
retainedValues =
retainValues(values, index)
} yield retainedValues
).toList //must explicitly use toList due to the source.close which will
//occur immediately following execution of this anonymous function
)
)
Zaktualizowana wersja powyższej funkcji został dostarczony jako odpowiedź na inny, ale powiązanego StackOverflow pytanie .
Teraz, łącząc to wszystko razem z wyodrębnionymi importami (co znacznie ułatwia wklejenie do arkusza Scala obecnego zarówno we wtyczce Eclipse ScalaIDE, jak i IntelliJ Scala, aby ułatwić zrzucanie danych wyjściowych na pulpit, aby można je było łatwiej zbadać za pomocą edytora tekstu), tak wygląda kod (ze zwiększoną zwięzłością):
import scala.io.Source
import scala.util.Try
import java.io.{BufferedWriter, FileWriter, File, PrintWriter}
val defaultBufferSize: Int = 65536
def tryUsingAutoCloseable[A <: AutoCloseable, R]
(instantiateAutoCloseable: () => A) //parameter list 1
(transfer: A => scala.util.Try[R]) //parameter list 2
: scala.util.Try[R] =
Try(instantiateAutoCloseable())
.flatMap(
autoCloseable => {
var optionExceptionTry: Option[Exception] = None
try
transfer(autoCloseable)
catch {
case exceptionTry: Exception =>
optionExceptionTry = Some(exceptionTry)
throw exceptionTry
}
finally
try
autoCloseable.close()
catch {
case exceptionFinally: Exception =>
optionExceptionTry match {
case Some(exceptionTry) =>
exceptionTry.addSuppressed(exceptionFinally)
case None =>
throw exceptionFinally
}
}
}
)
def tryUsingSource[S <: scala.io.Source, R]
(instantiateSource: () => S)
(transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
Try(instantiateSource())
.flatMap(
source => {
var optionExceptionTry: Option[Exception] = None
try
transfer(source)
catch {
case exceptionTry: Exception =>
optionExceptionTry = Some(exceptionTry)
throw exceptionTry
}
finally
try
source.close()
catch {
case exceptionFinally: Exception =>
optionExceptionTry match {
case Some(exceptionTry) =>
exceptionTry.addSuppressed(exceptionFinally)
case None =>
throw exceptionFinally
}
}
}
)
def tryPrintToFile(
lines: List[String],
location: File,
bufferSize: Int = defaultBufferSize
): Try[Unit] =
tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
tryUsingAutoCloseable(() => new PrintWriter(bufferedWriter)) { printWriter =>
Try(lines.foreach(line => printWriter.println(line)))
}
}
}
def tryWriteToFile(
content: String,
location: File,
bufferSize: Int = defaultBufferSize
): Try[Unit] =
tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
Try(bufferedWriter.write(content))
}
}
def tryProcessSource(
file: File,
parseLine: (String, Int) => List[String] = (line, index) => List(line),
filterLine: (List[String], Int) => Boolean = (values, index) => true,
retainValues: (List[String], Int) => List[String] = (values, index) => values,
isFirstLineNotHeader: Boolean = false
): Try[List[List[String]]] =
tryUsingSource(() => Source.fromFile(file)) { source =>
Try(
( for {
(line, index) <- source.getLines().buffered.zipWithIndex
values = parseLine(line, index)
if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
retainedValues = retainValues(values, index)
} yield retainedValues
).toList
)
}
Jako nowicjusz w Scala / FP spędziłem wiele godzin (głównie w frustracji drapiącej głowę), zdobywając powyższą wiedzę i rozwiązania. Mam nadzieję, że pomoże to innym nowicjuszom w Scala / FP szybciej uporać się z tym garbem uczenia się.
try-catch-finally. Wciąż kochaj swoją pasję.
Oto przykład zapisu niektórych wierszy do pliku przy użyciu scalaz-stream .
import scalaz._
import scalaz.stream._
def writeLinesToFile(lines: Seq[String], file: String): Task[Unit] =
Process(lines: _*) // Process that enumerates the lines
.flatMap(Process(_, "\n")) // Add a newline after each line
.pipe(text.utf8Encode) // Encode as UTF-8
.to(io.fileChunkW(fileName)) // Buffered write to the file
.runLog[Task, Unit] // Get this computation as a Task
.map(_ => ()) // Discard the result
writeLinesToFile(Seq("one", "two"), "file.txt").run
Aby przewyższyć samthebest i współpracowników przed nim, poprawiłem nazewnictwo i zwięzłość:
def using[A <: {def close() : Unit}, B](resource: A)(f: A => B): B =
try f(resource) finally resource.close()
def writeStringToFile(file: File, data: String, appending: Boolean = false) =
using(new FileWriter(file, appending))(_.write(data))
Eitherdo obsługi błędówdef write(destinationFile: Path, fileContent: String): Either[Exception, Path] =
write(destinationFile, fileContent.getBytes(StandardCharsets.UTF_8))
def write(destinationFile: Path, fileContent: Array[Byte]): Either[Exception, Path] =
try {
Files.createDirectories(destinationFile.getParent)
// Return the path to the destinationFile if the write is successful
Right(Files.write(destinationFile, fileContent))
} catch {
case exception: Exception => Left(exception)
}
val filePath = Paths.get("./testDir/file.txt")
write(filePath , "A test") match {
case Right(pathToWrittenFile) => println(s"Successfully wrote to $pathToWrittenFile")
case Left(exception) => println(s"Could not write to $filePath. Exception: $exception")
}
Aktualizacja 2019:
Podsumowanie - Java NIO (lub NIO.2 dla asynchronii) jest nadal najbardziej wszechstronnym rozwiązaniem do przetwarzania plików obsługiwanym w Scali. Poniższy kod tworzy i zapisuje tekst w nowym pliku:
import java.io.{BufferedOutputStream, OutputStream}
import java.nio.file.{Files, Paths}
val testFile1 = Paths.get("yourNewFile.txt")
val s1 = "text to insert in file".getBytes()
val out1: OutputStream = new BufferedOutputStream(
Files.newOutputStream(testFile1))
try {
out1.write(s1, 0, s1.length)
} catch {
case _ => println("Exception thrown during file writing")
} finally {
out1.close()
}
Pathobiekt o wybranej nazwie plikuOutputStream writefunkcji strumienia wyjściowegoPodobnie jak w tej odpowiedzi , oto przykład z fs2(wersja 1.0.4):
import cats.effect._
import fs2._
import fs2.io
import java.nio.file._
import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import cats.syntax.functor._
object ScalaApp extends IOApp {
def write[T[_]](p: Path, s: String)
(implicit F: ConcurrentEffect[T], cs: ContextShift[T]): T[Unit] = {
Stream(s)
.covary[T]
.through(text.utf8Encode)
.through(
io.file.writeAll(
p,
scala.concurrent.ExecutionContext.global,
Seq(StandardOpenOption.CREATE)
)
)
.compile
.drain
}
def run(args: List[String]): IO[ExitCode] = {
implicit val executionContext: ExecutionContext =
scala.concurrent.ExecutionContext.Implicits.global
implicit val contextShift: ContextShift[IO] =
IO.contextShift(executionContext)
val outputFile: Path = Paths.get("output.txt")
write[IO](outputFile, "Hello world\n").as(ExitCode.Success)
}
}
Jeśli mimo wszystko masz w swoim projekcie strumienie Akka, zapewnia on jednowierszowy:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Akka docs> Przesyłanie strumieniowe pliku IO