File I/O in JAVA


VÀO/RA (I/O)

Giới thiệu

Các dữ liệu mà một chương trình ngôn ngữ Java sử dụng phải đến từ một nơi nào đó. Nó thường hay đến từ một số nguồn dữ liệu bên ngoài. Có rất nhiều loại nguồn dữ liệu khác nhau, bao gồm cả cơ sở dữ liệu, chuyển giao byte trực tiếp qua một ổ cắm (socket) và các tệp tin. Ngôn ngữ Java sẽ mang lại cho bạn rất nhiều công cụ để bạn có thể nhận được các thông tin từ các nguồn bên ngoài. Các công cụ này phần lớn nằm trong gói java.io.

Trong tất cả các nguồn dữ liệu sẵn có, các tệp tin là phổ biến nhất và thường thuận tiện nhất. Việc hiểu biết cách sử dụng các API có sẵn của ngôn ngữ Java để tương tác với các tệp tin là một kỹ năng cơ bản của lập trình viên.

Nhìn chung, ngôn ngữ Java cung cấp cho bạn một lớp bao gói (File) cho kiểu tệp tin trong hệ điều hành của bạn. Để đọc tệp tin đó, bạn phải sử dụng các luồng (streams) phân tích cú pháp các byte đầu vào thành các kiểu của ngôn ngữ Java. Trong phần này, chúng tôi sẽ nói về tất cả các đối tượng mà bạn sẽ thường sử dụng để đọc các tệp tin.

Các tệp tin (File)

Lớp File định nghĩa một nguồn tài nguyên trên hệ thống tệp tin của bạn. Nó gây phiền toái, đặc biệt là cho việc kiểm thử, nhưng đó là thực tế mà các lập trình viên Java phải đối phó với nó.

Đây là cách bạn khởi tạo một cá thể File:

File aFile = new File("temp.txt");

Đoạn mã này tạo ra một cá thể File với đường dẫn temp.txt trong thư mục hiện tại. Chúng ta có thể tạo một File với chuỗi ký tự đường dẫn bất kỳ như mong muốn, miễn là nó hợp lệ. Lưu ý rằng khi có đối tượng File này không có nghĩa là tệp tin mà nó đại diện thực sự tồn tại trên hệ thống tệp tin ở vị trí dự kiến. Đối tượng của chúng ta chỉ đại diện cho một tệp tin thực tế có thể có hoặc có thể không có ở đó. Nếu tệp tin liên quan không tồn tại, chúng ta sẽ không phát hiện ra là có một vấn đề cho đến khi chúng ta cố đọc hay viết vào nó. Đó là một chút khó chịu, nhưng nó có ý nghĩa. Ví dụ, chúng ta có thể hỏi xem File của ta có tồn tại hay không:

aFile.exists();

Nếu nó không tồn tại, chúng ta có thể tạo nó:

aFile.createNewFile();

Khi sử dụng các phương thức khác trên File, chúng ta cũng có thể xóa các tệp tin, tạo các thư mục, xác định xem một tài nguyên trong hệ thống tệp tin là một thư mục hay là một tệp tin, v.v.. Tuy nhiên, hoạt động sẽ thực sự xảy ra, khi chúng ta ghi vào và đọc ra từ tệp tin. Để làm điều đó, chúng ta cần phải hiểu thêm một chút về các luồng.

Các luồng

Chúng ta có thể truy cập các tệp tin trên hệ thống tệp tin bằng cách sử dụng các luồng (streams). Ở mức độ thấp nhất, các luồng cho phép một chương trình nhận các byte từ một nguồn và/hoặc để gửi kết quả đến một đích đến. Một số luồng xử lý tất cả các loại ký tự 16-bit, các ký tự (đó là các luồng kiểu Reader và Writer). Các luồng khác xử lý chỉ các byte 8-bit (đó là các luồng kiểuInputStream và OutputStream). Trong các hệ thống thứ bậc này có một số luồng với hương vị khác (tất cả nằm trong góijava.io). Ở mức độ trừu tượng cao nhất, có các luồng ký tự và các luồng byte.

Các luồng byte đọc (InputStream và các lớp con của nó) và viết (OutputStream và các lớp con của nó) các byte 8-bit. Nói cách khác, các luồng byte có thể được coi là một kiểu luồng thô hơn. Kết quả là, rất dễ dàng để hiểu lý do tại sao hướng dẫn Java.sun.com về các lớp ngôn ngữ Java chủ yếu nói rằng các luồng byte thường được sử dụng cho các dữ liệu nhị phân, chẳng hạn như các hình ảnh. Đây là một danh sách lựa chọn các luồng byte:

Các luồng Cách sử dụng
FileInputStream
FileOutputStream
Đọc các byte từ một tệp tin và ghi các byte vào một tệp tin.
ByteArrayInputStream
ByteArrayOutputStream
Đọc các byte từ một mảng trong bộ nhớ và ghi các byte vào một mảng trong bộ nhớ.

Các luồng ký tự đọc (Reader và các lớp con của nó) và viết (Writer và các lớp con của nó) các ký tự 16-bit. Các lớp con hoặc đọc hoặc viết từ/đến các bộ nhớ (sinks) dữ liệu hoặc xử lý các byte được chuyển qua nó. Đây là một danh sách chọn lọc của các luồng ký tự:

Các luồng Cách sử dụng
StringReader
StringWriter
Các luồng này đọc và viết các ký tự từ/đến các String trong bộ nhớ.
InputStreamReader
InputStreamWriter(and subclassesFileReader
FileWriter)
Cầu nối giữa các luồng byte và các luồng ký tự. Reader flavors read bytes from a byte stream and convert them to characters. The Writer chuyển đổi các ký tự thành các byte để đặt chúng vào các luồng byte.
BufferedReader andBufferedWriter Là bộ đệm dữ liệu trong khi đọc hoặc viết một luồng khác, cho phép các hoạt động đọc và ghi có hiệu quả hơn. Bạn gói một luồng khác trong một luồng có bộ đệm.

Các luồng là một chủ đề lớn và chúng ta không thể trình bày toàn bộ chúng ở đây. Thay vào đó, chúng ta sẽ tập trung vào các luồng được khuyến cáo dùng để đọc và viết các tệp tin. Trong hầu hết trường hợp, đây sẽ là các luồng ký tự, nhưng chúng tôi sẽ sử dụng cả các luồng ký tự và các luồng byte để minh họa các khả năng này.

Đọc và viết các tệp tin

Có một số cách để đọc ra từ và viết vào một File. Người ta có thể cho rằng cách tiếp cận đơn giản nhất diễn ra như sau:

  • Tạo một FileOutputStream trên File để viết vào nó.
  • Tạo một FileInputStream trên File để đọc từ nó.
  • Gọi read() để đọc từ File và write() để viết vào File.
  • Đóng các luồng, dọn dẹp sạch sẽ nếu cần thiết.

Các mã có thể trông giống như sau:

try {
	File source = new File("input.txt");
	File sink = new File("output.txt");

	FileInputStream in = new FileInputStream(source);
	FileOutputStream out = new FileOutputStream(sink);
	int c;

	while ((c = in.read()) != -1)
	   out.write(c);

	in.close();
	out.close();			
} catch (Exception e) {
	e.printStackTrace();
}

Ở đây chúng ta tạo ra hai đối tượng File: một FileInputStream để đọc từ tệp tin nguồn và một FileOutputStream để viết tớiFile kết quả. (Lưu ý: Ví dụ này đã được điều chỉnh từ ví dụ CopyBytes.java trong Java.sun.com;. Chúng ta sau đó đọc vào từng byte của dữ liệu đầu vào và ghi nó vào kết quả đầu ra. Sau khi đã xong, chúng ta đóng các luồng. Có lẽ là khôn ngoan khi đặt một lời gọi close() vào trong khối cuỗi cùng (finally). Tuy nhiên, trình biên dịch của ngôn ngữ Java sẽ còn yêu cầu bạn phải nắm bắt nhiều trường hợp ngoại lệ khác có thể xảy ra, điều này có nghĩa là cần một mệnh đề catch khác nữa nằm trong khối finally của bạn. Có chắc chắn đáng làm không? Có thể.

Vậy là bây giờ chúng ta có một cách tiếp cận cơ bản để đọc và viết. Nhưng sự lựa chọn khôn ngoan hơn, và về một vài khía cạnh, sự lựa chọn dễ dàng hơn, là sử dụng một số luồng khác mà chúng ta sẽ thảo luận trong phần tiếp theo.

Các luồng có bộ đệm

Có một số cách để đọc ra và ghi vào một File, nhưng cách tiếp cận điển hình và thuận tiện nhất diễn ra như sau:

  • Tạo một FileWriter trên File.
  • Gói FileWriter trong một BufferedWriter.
  • Gọi write() trên BufferedWriter mỗi khi cần thiết để viết các nội dung của File, thường ở cuối mỗi dòng cần viết ký tự kết thúc dòng (đó là, \n).
  • Gọi flush() trên BufferedWriter để làm rỗng nó.
  • Đóng BufferedWriter, dọn dẹp sạch nếu cần thiết.

Các mã có thể trông giống như sau:

try {
	FileWriter writer = new FileWriter(aFile);
	BufferedWriter buffered = new BufferedWriter(writer);
	buffered.write("A line of text.\n");
	buffered.flush();
} catch (IOException e1) {
	e1.printStackTrace();
}

Ở đây, chúng ta tạo ra một FileWriter trên aFile, sau đó chúng ta gói nó trong một BufferedWriter. Thao tác viết có bộ đệm hiệu quả hơn là đơn giản viết mỗi lần một byte. Khi chúng ta đã thực hiện viết từng dòng (mà chúng ta tự kết thúc bằng \n), chúng ta gọi flush() trên BufferedWriter. Nếu chúng ta không làm như vậy, chúng ta sẽ không nhìn thấy bất kỳ dữ liệu nào trong tệp tin đích, bất chấp mọi công sức cố gắng viết tệp tin này.

Khi chúng ta có dữ liệu trong tệp tin, chúng ta có thể đọc nó bằng các mã tương tự đơn giản:

String line = null;
StringBuffer lines = new StringBuffer();
try {
	FileReader reader = new FileReader(aFile);
	BufferedReader bufferedReader = new BufferedReader(reader);
	while ( (line = bufferedReader.readLine()) != null) {
		lines.append(line);
		lines.append("\n");
	}
} catch (IOException e1) {
	e1.printStackTrace();
}
System.out.println(lines.toString());

Chúng ta tạo ra một FileReader, sau đó gói nó trong một BufferedReader. Điều đó cho phép chúng ta sử dụng phương thức thuận tiện readLine(). Chúng ta đọc từng dòng cho đến khi không có gì còn lại, viết thêm mỗi dòng vào cuối StringBuffer. của chúng ta. Khi đọc từ một tệp tin, một IOException có thể xảy ra, do đó chúng ta bao quanh tất cả logic đọc tệp tin của chúng ta bằng một khối try/catch.

http://www.ibm.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: