Làm việc với File (Đọc và Ghi File I/O)
Chào mừng bạn đã quay trở lại series "Chinh Phục Java"! Ở bài trước, chúng ta đã học cách xử lý các ngoại lệ (exceptions) để chương trình hoạt động ổn định hơn. Hôm nay, chúng ta sẽ học cách làm cho chương trình có thể giao tiếp với hệ thống lưu trữ của máy tính. Cụ thể là cách **đọc dữ liệu từ một file** và **ghi dữ liệu ra một file**.
Kỹ năng làm việc với file, hay còn gọi là **File I/O (Input/Output)**, là một trong những kỹ năng cơ bản và quan trọng nhất. Nó cho phép ứng dụng của bạn có thể lưu trữ dữ liệu một cách bền vững. Ví dụ như lưu cấu hình, ghi log, hay xuất báo cáo. Hãy cùng tìm hiểu cách thực hiện các thao tác này trong Java.
1. Giới thiệu về `java.io` và `java.nio`
Java cung cấp hai gói (package) chính để làm việc với I/O. Gói `java.io` là gói truyền thống, ra đời từ những phiên bản đầu của Java. Nó hoạt động dựa trên luồng (stream), xử lý dữ liệu theo từng byte hoặc từng ký tự. Các lớp như `FileInputStream` và `FileReader` thuộc gói này. Cách tiếp cận này đơn giản và dễ hiểu cho các tác vụ cơ bản.
Sau này, Java giới thiệu gói `java.nio` (New I/O). Gói này cung cấp một cách tiếp cận hiện đại và hiệu quả hơn. Nó hoạt động dựa trên bộ đệm (buffer) và kênh (channel). `java.nio` thường cho hiệu năng tốt hơn. Trong bài này, chúng ta sẽ kết hợp những gì tốt nhất của cả hai. Chúng ta sẽ dùng các lớp `Reader`/`Writer` từ `java.io` được "bọc" lại để tăng hiệu năng.
2. Đọc File văn bản: Sử dụng `BufferedReader`
Cách hiệu quả và phổ biến nhất để đọc một file văn bản theo từng dòng là kết hợp `FileReader` và `BufferedReader`. `FileReader` đọc file theo từng ký tự. `BufferedReader` sẽ "bọc" `FileReader` lại. Nó đọc một khối lớn ký tự vào bộ đệm (buffer). Điều này giúp giảm số lần phải truy cập vào ổ đĩa. Nó làm cho việc đọc file nhanh hơn đáng kể.
Việc đọc file có thể gây ra `IOException`. Vì vậy, chúng ta phải đặt code xử lý file vào trong một khối `try-catch`. Cú pháp `try-with-resources` được giới thiệu từ Java 7. Nó giúp tự động đóng file sau khi sử dụng xong. Đây là cách làm được khuyến nghị.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileExample {
public static void main(String[] args) {
// Giả sử chúng ta có một file "data.txt" trong cùng thư mục
String filePath = "data.txt";
// Sử dụng try-with-resources để đảm bảo file được đóng tự động
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
System.out.println("Nội dung của file:");
// Đọc file theo từng dòng cho đến khi hết file (readLine() trả về null)
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Đã xảy ra lỗi khi đọc file: " + e.getMessage());
}
}
}
Bạn hãy tạo một file `data.txt` với vài dòng chữ. Sau đó, đặt nó cùng cấp với thư mục `src` trong project của bạn để chạy thử ví dụ trên.
3. Ghi File văn bản: Sử dụng `BufferedWriter`
Tương tự như việc đọc file, cách hiệu quả nhất để ghi ra một file văn bản là kết hợp `FileWriter` và `BufferedWriter`. `FileWriter` ghi từng ký tự. `BufferedWriter` sẽ bọc `FileWriter` lại. Nó lưu dữ liệu vào một bộ đệm. Chỉ khi bộ đệm đầy hoặc bạn ra lệnh, nó mới thực sự ghi toàn bộ khối dữ liệu đó xuống ổ đĩa. Điều này cũng giúp tăng hiệu năng ghi file.
Chúng ta cũng sẽ sử dụng cú pháp `try-with-resources`. Nó đảm bảo rằng file sẽ được đóng đúng cách sau khi ghi xong. Việc này rất quan trọng để dữ liệu không bị mất. Hãy xem ví dụ dưới đây.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
String filePath = "output.txt";
String content = "Đây là dòng đầu tiên.\nĐây là dòng thứ hai.";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write(content);
System.out.println("Đã ghi nội dung vào file output.txt thành công!");
} catch (IOException e) {
System.err.println("Đã xảy ra lỗi khi ghi file: " + e.getMessage());
}
}
}
Sau khi chạy code, một file mới tên là `output.txt` sẽ được tạo ra. File này sẽ chứa nội dung mà bạn đã định nghĩa. Nếu file đã tồn tại, nó sẽ bị **ghi đè** theo mặc định. Nếu bạn muốn **ghi tiếp vào cuối file** thay vì ghi đè, hãy dùng `new FileWriter(filePath, true)`.
4. Làm việc với Đường dẫn: Lớp `Path` và `Files`
Gói `java.nio` giới thiệu các lớp hiện đại hơn để làm việc với file và thư mục. Đó là `Path` và `Files`. Lớp `Path` đại diện cho một đường dẫn trong hệ thống file. Lớp `Files` là một lớp tiện ích. Nó cung cấp các phương thức tĩnh để thực hiện các thao tác phổ biến. Ví dụ như kiểm tra file tồn tại, tạo thư mục, xóa file...
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathFilesExample {
public static void main(String[] args) {
Path path = Paths.get("data.txt");
System.out.println("File tồn tại: " + Files.exists(path));
System.out.println("Là file thông thường: " + Files.isRegularFile(path));
System.out.println("Có thể đọc: " + Files.isReadable(path));
System.out.println("Đường dẫn tuyệt đối: " + path.toAbsolutePath());
}
}
Kết luận
Bạn vừa học được cách để ứng dụng Java của mình tương tác với hệ thống file. Kỹ năng đọc và ghi file là nền tảng cho rất nhiều tác vụ thực tế. Hãy nhớ sử dụng `BufferedReader` và `BufferedWriter` để có hiệu năng tốt nhất. Đồng thời, hãy luôn dùng `try-with-resources` để quản lý tài nguyên một cách an toàn. Ở bài tiếp theo, chúng ta sẽ khám phá các tính năng hiện đại được giới thiệu từ Java 8, như Lambda và Stream API.
0 Bình luận
Vui lòng đăng nhập để gửi bình luận.