Java - เขียนลงในไฟล์

1. ภาพรวม

ในบทช่วยสอนนี้เราจะสำรวจวิธีต่างๆในการเขียนไฟล์โดยใช้ Java เราจะใช้ประโยชน์จากBufferedWriter , PrintWriter , FileOutputStream , DataOutputStream , RandomAccessFile , FileChannelและคลาสยูทิลิตี้Java 7 Files

นอกจากนี้เราจะดูการล็อกไฟล์ในขณะที่เขียนและพูดคุยประเด็นสุดท้ายเกี่ยวกับการเขียนลงไฟล์

บทช่วยสอนนี้เป็นส่วนหนึ่งของชุด Java“ Back to Basics” ที่ Baeldung

2. เขียนด้วยBufferedWriter

เริ่มต้นง่ายๆและใช้BufferedWriterเพื่อเขียนStringไปยังไฟล์ใหม่ :

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

ผลลัพธ์ในไฟล์จะเป็น:

Hello

จากนั้นเราสามารถต่อท้ายStringเข้ากับไฟล์ที่มีอยู่ :

@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(); }

จากนั้นไฟล์จะเป็น:

Hello World

3. เขียนด้วยPrintWriter

ต่อไปมาดูว่าเราสามารถใช้PrintWriterเพื่อเขียนข้อความที่จัดรูปแบบไปยังไฟล์ได้อย่างไร :

@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(); }

ไฟล์ผลลัพธ์จะประกอบด้วย:

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

สังเกตว่าเราไม่เพียง แต่เขียนสตริงดิบลงในไฟล์ แต่ยังรวมถึงข้อความที่จัดรูปแบบด้วยเมธอดprintfด้วย

เราสามารถสร้างนักเขียนใช้FileWriter , BufferedWriterหรือแม้กระทั่งSystem.out

4. เขียนด้วยFileOutputStream

ตอนนี้เรามาดูกันว่าเราสามารถใช้FileOutputStreamเพื่อเขียนข้อมูลไบนารีลงในไฟล์ได้อย่างไร

รหัสต่อไปนี้แปลงสตริงเป็นไบต์และเขียนไบต์ไปยังไฟล์โดยใช้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(); }

ผลลัพธ์ในไฟล์จะเป็น:

Hello

5. เขียนด้วยDataOutputStream

ต่อไปมาดูวิธีที่เราสามารถใช้DataOutputStreamเพื่อเขียนStringลงในไฟล์:

@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. เขียนด้วยRandomAccessFile

ตอนนี้เรามาดูวิธีการเขียนและแก้ไขภายในไฟล์ที่มีอยู่แทนที่จะเขียนลงในไฟล์ใหม่ทั้งหมดหรือต่อท้ายไฟล์ที่มีอยู่ ใส่เพียง: เราต้องการการเข้าถึงโดยสุ่ม

RandomAccessFileช่วยให้เราสามารถเขียนในตำแหน่งเฉพาะในไฟล์ที่กำหนดให้ออฟเซ็ต - จากจุดเริ่มต้นของไฟล์เป็นไบต์

รหัสนี้เขียนค่าจำนวนเต็มโดยมีค่าชดเชยจากจุดเริ่มต้นของไฟล์:

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

หากเราต้องการอ่านint ที่เก็บไว้ในตำแหน่งใดตำแหน่งหนึ่งเราสามารถใช้วิธีนี้:

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

ในการทดสอบฟังก์ชันของเราให้เขียนจำนวนเต็มแก้ไขและอ่านกลับในที่สุด:

@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. เขียนด้วยFileChannel

หากเรากำลังจัดการกับไฟล์ขนาดใหญ่FileChannelอาจเร็วกว่า IO มาตรฐาน รหัสต่อไปนี้เขียนStringไปยังไฟล์โดยใช้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. เขียนด้วยไฟล์คลาส

Java 7 แนะนำวิธีการใหม่ของการทำงานกับระบบแฟ้มพร้อมกับระดับยูทิลิตี้ใหม่: ไฟล์

เมื่อใช้คลาสไฟล์เราสามารถสร้างย้ายคัดลอกและลบไฟล์และไดเร็กทอรี นอกจากนี้ยังสามารถใช้ในการอ่านและเขียนลงในไฟล์:

@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. เขียนลงในไฟล์ชั่วคราว

ตอนนี้เรามาลองเขียนลงในไฟล์ชั่วคราว รหัสต่อไปนี้สร้างไฟล์ชั่วคราวและเขียนสตริงลงไป:

@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(); }

อย่างที่เราเห็นมันเป็นเพียงการสร้างไฟล์ชั่วคราวที่น่าสนใจและแตกต่าง หลังจากนั้นการเขียนไฟล์จะเหมือนเดิม

10. ล็อคไฟล์ก่อนเขียน

สุดท้ายเมื่อเขียนไปยังไฟล์บางครั้งเราต้องตรวจสอบให้แน่ใจเป็นพิเศษว่าไม่มีใครเขียนไฟล์นั้นพร้อมกัน โดยพื้นฐานแล้วเราต้องสามารถล็อกไฟล์นั้นขณะเขียนได้

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.

การใช้งานตัวอย่างและข้อมูลโค้ดเหล่านี้สามารถพบได้บน GitHub