Java - zapis do pliku

1. Przegląd

W tym samouczku poznamy różne sposoby zapisu do pliku przy użyciu języka Java. Będziemy wykorzystywać BufferedWriter , PrintWriter , FileOutputStream , DataOutputStream , RandomAccessFile , FileChannel, a Java 7 Files klasie użytkowej.

Przyjrzymy się również blokowaniu pliku podczas pisania i omówimy ostateczne wnioski dotyczące zapisywania do pliku.

Ten samouczek jest częścią serii Java „Back to Basics” tutaj w Baeldung.

2. Napisz za pomocą BufferedWriter

Zacznijmy od prostych rzeczy i użyj BufferedWriter, aby zapisać String do nowego pliku :

public void whenWriteStringUsingBufferedWritter_thenCorrect() throws IOException { String str = "Hello"; BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); writer.write(str); writer.close(); }

Wynik w pliku będzie wyglądał następująco:

Hello

Następnie możemy dołączyć ciąg znaków do istniejącego pliku :

@Test public void whenAppendStringUsingBufferedWritter_thenOldContentShouldExistToo() throws IOException { String str = "World"; BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, true)); writer.append(' '); writer.append(str); writer.close(); }

Plik będzie miał wtedy postać:

Hello World

3. Napisz za pomocą PrintWriter

Następnie zobaczmy, jak możemy użyć PrintWriter do zapisania sformatowanego tekstu do pliku :

@Test public void givenWritingStringToFile_whenUsingPrintWriter_thenCorrect() throws IOException { FileWriter fileWriter = new FileWriter(fileName); PrintWriter printWriter = new PrintWriter(fileWriter); printWriter.print("Some String"); printWriter.printf("Product name is %s and its price is %d $", "iPhone", 1000); printWriter.close(); }

Wynikowy plik będzie zawierał:

Some String Product name is iPhone and its price is 1000$

Zwróć uwagę, że nie tylko zapisujemy nieprzetworzony ciąg do pliku, ale także trochę sformatowanego tekstu za pomocą metody printf .

Możemy stworzyć moduł zapisujący za pomocą FileWriter , BufferedWriter lub nawet System.out .

4. Napisz za pomocą FileOutputStream

Zobaczmy teraz, jak możemy użyć FileOutputStream do zapisania danych binarnych do pliku.

Poniższy kod konwertuje String na bajty i zapisuje bajty do pliku przy użyciu FileOutputStream :

@Test public void givenWritingStringToFile_whenUsingFileOutputStream_thenCorrect() throws IOException { String str = "Hello"; FileOutputStream outputStream = new FileOutputStream(fileName); byte[] strToBytes = str.getBytes(); outputStream.write(strToBytes); outputStream.close(); }

Wynik w pliku to oczywiście:

Hello

5. Napisz za pomocą DataOutputStream

Następnie, rzućmy okiem na to, jak możemy wykorzystać DataOutputStream napisać ciąg do pliku:

@Test public void givenWritingToFile_whenUsingDataOutputStream_thenCorrect() throws IOException { String value = "Hello"; FileOutputStream fos = new FileOutputStream(fileName); DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos)); outStream.writeUTF(value); outStream.close(); // verify the results String result; FileInputStream fis = new FileInputStream(fileName); DataInputStream reader = new DataInputStream(fis); result = reader.readUTF(); reader.close(); assertEquals(value, result); }

6. Napisz za pomocą RandomAccessFile

Zilustrujmy teraz, jak pisać i edytować w istniejącym pliku, zamiast pisać do zupełnie nowego pliku lub dołączać do istniejącego. Mówiąc najprościej: potrzebujemy dostępu swobodnego.

RandomAccessFile umożliwia nam zapisywanie w określonej pozycji w pliku z podanym przesunięciem - od początku pliku - w bajtach.

Ten kod zapisuje wartość całkowitą z przesunięciem podanym od początku pliku:

private void writeToPosition(String filename, int data, long position) throws IOException { RandomAccessFile writer = new RandomAccessFile(filename, "rw"); writer.seek(position); writer.writeInt(data); writer.close(); }

Jeśli chcemy odczytać int przechowywany w określonej lokalizacji , możemy skorzystać z tej metody:

private int readFromPosition(String filename, long position) throws IOException { int result = 0; RandomAccessFile reader = new RandomAccessFile(filename, "r"); reader.seek(position); result = reader.readInt(); reader.close(); return result; }

Aby przetestować nasze funkcje, napiszmy liczbę całkowitą, wyedytujmy ją i na koniec przeczytajmy ją z powrotem:

@Test public void whenWritingToSpecificPositionInFile_thenCorrect() throws IOException { int data1 = 2014; int data2 = 1500; writeToPosition(fileName, data1, 4); assertEquals(data1, readFromPosition(fileName, 4)); writeToPosition(fileName2, data2, 4); assertEquals(data2, readFromPosition(fileName, 4)); }

7. Pisz za pomocą FileChannel

Jeśli mamy do czynienia z dużymi plikami, FileChannel może być szybszy niż standardowe IO. Poniższy kod zapisuje String do pliku przy użyciu FileChannel :

@Test public void givenWritingToFile_whenUsingFileChannel_thenCorrect() throws IOException { RandomAccessFile stream = new RandomAccessFile(fileName, "rw"); FileChannel channel = stream.getChannel(); String value = "Hello"; byte[] strBytes = value.getBytes(); ByteBuffer buffer = ByteBuffer.allocate(strBytes.length); buffer.put(strBytes); buffer.flip(); channel.write(buffer); stream.close(); channel.close(); // verify RandomAccessFile reader = new RandomAccessFile(fileName, "r"); assertEquals(value, reader.readLine()); reader.close(); }

8. Napisz z klasą plików

Java 7 introduces a new way of working with the filesystem, along with a new utility class: Files.

Using the Files class, we can create, move, copy, and delete files and directories. It can also be used to read and write to a file:

@Test public void givenUsingJava7_whenWritingToFile_thenCorrect() throws IOException { String str = "Hello"; Path path = Paths.get(fileName); byte[] strToBytes = str.getBytes(); Files.write(path, strToBytes); String read = Files.readAllLines(path).get(0); assertEquals(str, read); }

9. Write to a Temporary File

Now let's try to write to a temporary file. The following code creates a temporary file and writes a String to it:

@Test public void whenWriteToTmpFile_thenCorrect() throws IOException { String toWrite = "Hello"; File tmpFile = File.createTempFile("test", ".tmp"); FileWriter writer = new FileWriter(tmpFile); writer.write(toWrite); writer.close(); BufferedReader reader = new BufferedReader(new FileReader(tmpFile)); assertEquals(toWrite, reader.readLine()); reader.close(); }

As we can see, it's just the creation of the temporary file that is interesting and different. After that point, writing to the file is the same.

10. Lock File Before Writing

Finally, when writing to a file, we sometimes need to make extra sure that no one else is writing to that file at the same time. Basically, we need to be able to lock that file while writing.

Let's make use of FileChannel to try locking the file before writing to it:

@Test public void whenTryToLockFile_thenItShouldBeLocked() throws IOException { RandomAccessFile stream = new RandomAccessFile(fileName, "rw"); FileChannel channel = stream.getChannel(); FileLock lock = null; try { lock = channel.tryLock(); } catch (final OverlappingFileLockException e) { stream.close(); channel.close(); } stream.writeChars("test lock"); lock.release(); stream.close(); channel.close(); }

Note that if the file is already locked when we try to acquire the lock, an OverlappingFileLockException will be thrown.

11. Notes

After exploring so many methods of writing to a file, let's discuss some important notes:

  • If we try to read from a file that doesn't exist, a FileNotFoundException will be thrown.
  • If we try to write to a file that doesn't exist, the file will be created first and no exception will be thrown.
  • It is very important to close the stream after using it, as it is not closed implicitly, to release any resources associated with it.
  • In output stream, the close() method calls flush() before releasing the resources, which forces any buffered bytes to be written to the stream.

Looking at the common usage practices, we can see, for example, that PrintWriter is used to write formatted text, FileOutputStream to write binary data, DataOutputStream to write primitive data types, RandomAccessFile to write to a specific position, and FileChannel to write faster in larger files. Some of the APIs of these classes do allow more, but this is a good place to start.

12. Conclusion

This article illustrated the many options of writing data to a file using Java.

Implementację wszystkich tych przykładów i fragmentów kodu można znaleźć na GitHub.