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 Using
narzę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
,Output
iSeekable
które stanowią rdzeń API.
Inne klasy znaczenie mająResource
,ReadChars
iWriteChars
.- Plik - plik jest
File
(nazywanymPath
) interfejsem API opartym na połączeniu systemu plików Java 7 NIO i interfejsów API SBT PathFinder.
Path
iFileSystem
są 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] = None
Parametr po, f
jeś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()
}
import
z 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 finally
ani lambd i zwróć uwagę na użycie składni zastępczej. Zwróć także uwagę na lepsze nazewnictwo.
implemented
warunku 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 contents
do 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ż contents
sam.
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.13
od 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 PrintWriter
lub BufferedWriter
któ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.io
interfejsu 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.nio
API:
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ł Exception
rzucony przez, try
jeśli finally
kod wyrzucił plikException
Po 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.Try
close
metoda 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.AutoCloseable
specyfikacja, 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, using
metoda 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 tryUsingAutoCloseable
metody 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ę, tryPrintToFile
która utworzy (lub nadpisze istniejącą) File
i zapisze List[String]
. Używa, FileWriter
który jest zamknięty przez a, BufferedWriter
który z kolei jest zamknięty w PrintWriter
. Aby podnieść wydajność, BufferedWriter
zdefiniowano 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 tryPrintToFile
metoda jest przydatna, ponieważ wymaga plikuList[String]
dane wejściowe i wysyła je do pliku File
. Utwórzmy teraz tryWriteToFile
metodę, która pobiera a String
i 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 File
jako pliku String
. Chociaż scala.io.Source
zapewnia wygodną metodę łatwego uzyskiwania zawartości pliku File
, close
metoda ta musi być używana w Source
celu 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 finalize
metoda zostanie wywołana przez GC do close
zasobu. 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ę close
na wystąpieniujava.lang.AutoCloseable
scala.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))
Either
do 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()
}
Path
obiekt o wybranej nazwie plikuOutputStream
write
funkcji 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