Bài 8: Luồng vào ra

Lê Hồng Phương phuonglh@gmail.com Khoa Toán-Cơ-Tin học Trường Đại học Khoa học Tự nhiên Hà Nội

Nội dung

● Tổng quan về luồng vào ra

● Các kiểu luồng vào ra:

– Luồng byte

– Luồng kí tự

– Luồng có đệm

– Luồng dữ liệu

– Luồng đối tượng

2012-2013

Object-Oriented Programming: I/O Streams

2

Luồng vào ra

● Luồng vào ra (input/output streams – IO streams)

– Biểu diễn một nguồn vào và một đích ra

● Có nhiều kiểu nguồn và đích:

– tệp trên đĩa, thiết bị, chương trình, bộ nhớ

● Có nhiều kiểu dữ liệu khác nhau:

– các bytes

– các kiểu dữ liệu đơn giản

– các đối tượng

2012-2013

Object-Oriented Programming: I/O Streams

3

Luồng vào ra

● Định nghĩa tổng quát: mỗi luồng là một chuỗi dữ liệu.

● Mỗi chương trình thường sử dụng:

– Một luồng vào để đọc dữ liệu từ một nguồn, mỗi

lần đọc một đơn vị dữ liệu.

– Một luồng ra để ghi dữ liệu ra một đích, mỗi lần

ghi một đơn vị dữ liệu.

stream

Program

010111000111

2012-2013

Object-Oriented Programming: I/O Streams

4

Data source

Luồng vào ra

● Các chương trình sử dụng luồng byte để đọc/ghi các

byte (8 bits).

● Trong Java, có nhiều lớp luồng byte. Các lớp này đều

phái sinh từ hai lớp:

– InputStream

– OutputStream

● Khi đọc/ghi dữ liệu byte từ/vào tệp, ta sử dụng

– FileInputStream

– FileOutputStream

2012-2013

Object-Oriented Programming: I/O Streams

5

Luồng byte

● Viết chương trình sao chép một tệp, mô phỏng lệnh

– cp input.txt output.txt (Unix, Linux)

– copy input.txt output.txt (MS Windows)

● Sử dụng hai luồng byte gắn với hai tệp:

– Một luồng để đọc tệp input.txt

– Một luồng để ghi tệp output.txt

● Vòng lặp: mỗi lần đọc một byte của tệp input.txt và ghi nó vào tệp output.txt. Khi hết tệp thì hàm đọc trả về -1.

2012-2013

Object-Oriented Programming: I/O Streams

6

Luồng byte

public class CopyBytes {

public static void main(String[] args) throws IOException {

InputStream in = null; OutputStream out = null; try {

in = new FileInputStream("input.txt"); out = new FileOutputStream("output.txt"); int c; while ((c = in.read()) != -1) {

out.write(c);

Tại sao không có các khối catch? }

} finally {

Luôn phải đóng mọi luồng dữ liệu khi sử dụng xong. if (in != null) in.close(); if (out != null) out.close();

}

}

2012-2013

Object-Oriented Programming: I/O Streams

7

}

Luồng byte

● Tuy nhiên, luồng byte biểu diễn các thao tác vào/ra mức thấp, ta nên tránh sử dụng trực tiếp các luồng này.

● Vì các tệp input.txt, output.txt chứa các dữ liệu dạng

kí tự, cách tốt nhất là sử dụng các luồng kí tự.

● Nếu không nên dùng trực tiếp, tại sao lại giới thiệu

luồng byte?

– Vì luồng byte là cơ sở để xây dựng các kiểu luồng

khác.

2012-2013

Object-Oriented Programming: I/O Streams

8

Luồng kí tự

● Nền tảng Java lưu các giá trị kí tự sử dụng chuẩn

Unicode.

● Mọi lớp biểu diễn luồng kí tự đều phái sinh từ hai lớp

Reader và Writer.

● Khi đọc/ghi các tệp văn bản (luồng kí tự trong tệp), ta

sử dụng các lớp

– FileReader

– FileWriter

2012-2013

Object-Oriented Programming: I/O Streams

9

Luồng kí tự

public class CopyCharacters {

public static void main(String[] args) throws IOException {

Reader in = null; Writer out = null; try {

in = new FileReader("input.txt"); out = new FileWriter("output.txt"); int c; while ((c = in.read()) != -1) {

out.write(c);

}

} finally {

if (in != null) in.close(); if (out != null) out.close();

}

}

2012-2013

Object-Oriented Programming: I/O Streams

10

}

Luồng có đệm

● Luồng không đệm:

– Mỗi lệnh đọc, ghi được thực hiện trực tiếp bởi hệ điều

hành.

– Chương trình kém hiệu quả, do mỗi lệnh đều đòi hỏi

truy xuất ổ đĩa, truyền dữ liệu qua mạng hoặc các thao tác tốn kém khác.

● Luồng có đệm hiệu quả hơn nhiều:

– Đọc/ghi dữ liệu từ/vào một vùng nhớ (gọi là bộ đệm)

– Khi nào bộ đệm đầy thì mới gọi các lệnh đọc/ghi của hệ

điều hành

2012-2013

Object-Oriented Programming: I/O Streams

11

Luồng có đệm

● Các lớp hỗ trợ luồng có đệm:

– BufferedInputStream, BufferedOutputStream

– BufferedReader, BufferedWriter

● Kích hoạt việc đọc/ghi bộ đệm khi bộ đệm chưa đầy:

– Gọi phương thức flush()

● Để chuyển một luồng không đệm thành một luồng có

đệm, ta tạo một luồng có đệm (tương ứng):

BufferedReader bufferedReader = new

BufferedReader(new FileReader("input.txt"));

BufferedWriter bufferedWriter = new

2012-2013

Object-Oriented Programming: I/O Streams

12

BufferedWriter(new FileWriter("output.txt"));

Luồng dữ liệu

● Luồng dữ liệu hỗ trợ việc đọc, ghi các giá trị thuộc

các kiểu dữ liệu cơ sở:

– boolean, char, byte, short, int, long, float, double

– String

● Mọi luồng dữ liệu cài đặt một trong hai giao diện sau:

– DataInput, DataOutput

● Hai cài đặt được sử dụng nhiều là

– DataInputStream, DataOutputStream

2012-2013

Object-Oriented Programming: I/O Streams

13

Luồng dữ liệu

● Ví dụ, giả sử ta có 3 biến price, unit và desc lần lượt chứa các giá trị thuộc các kiểu double, int và String.

● Ghi các giá trị vào tệp:

Sử dụng luồng có đệm để ghi vào tệp dataFile.dat

DataOutputStream out; try {

out = new DataOutputStream(new BufferedOutputStream(

new FileOutputStream("dataFile.dat")));

Gọi các phương thức tương ứng của out out.writeDouble(price); out.writeInt(unit); out.writeUTF(desc);

} catch (FileNotFoundException e) {

e.printStackTrace(); } catch (IOException e) { e.printStackTrace();

2012-2013

Object-Oriented Programming: I/O Streams

14

}

Luồng dữ liệu

● Đọc dữ liệu từ tệp và gán giá trị cho các biến tương

ứng:

DataInputStream in; try {

in = new DataInputStream(new BufferedInputStream(

new FileInputStream("dataFile.dat")));

Gọi các phương thức tương ứng của in

double price = in.readDouble(); int unit = in.readInt(); String desc = in.readUTF(); } catch (FileNotFoundException e) {

e.printStackTrace(); } catch (IOException e) { e.printStackTrace();

2012-2013

Object-Oriented Programming: I/O Streams

15

}

Luồng đối tượng

● Luồng đối tượng hỗ trợ việc đọc, ghi các đối tượng.

● Nếu đối tượng thuộc một lớp cài đặt giao diện

Serializable thì ta có thể sử dụng luồng đối tượng để đọc, ghi đối tượng đó.

● Hai lớp hỗ trợ luồng đối tượng:

– ObjectInputStream, ObjectOutputStream

● Hai lớp này tương ứng cài đặt các giao diện

– ObjectInput và ObjectOutput

2012-2013

Object-Oriented Programming: I/O Streams

16

Luồng đối tượng

● ObjectInput và ObjectOutput tương ứng là các giao diện

con của các giao diện DataInput và DataOutput.

● Chính vì vậy, mọi phương thức vào, ra đối với các kiểu

dữ liệu cơ sở do DataInput, DataOutput hỗ trợ đều được cài đặt trong ObjectInput, ObjectOutput.

ObjectOutput oo = null; try {

oo = new ObjectOutputStream(new BufferedOutputStream(

new FileOutputStream("dates.dat"))); oo.writeObject(Calendar.getInstance());

} catch (FileNotFoundException e) {

e.printStackTrace(); } catch (IOException e) { e.printStackTrace();

2012-2013

Object-Oriented Programming: I/O Streams

17

}

Luồng đối tượng

ObjectInput oi = null; try {

oi = new ObjectInputStream(new BufferedInputStream(

new FileInputStream("dates.dat"))); Calendar date = (Calendar)oi.readObject();

} catch (FileNotFoundException e) {

e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); Phương thức readObject() luôn trả về kiểu Object nên ta phải ép thành kiểu thích hợp. } catch (ClassNotFoundException e) {

e.printStackTrace();

2012-2013

Object-Oriented Programming: I/O Streams

18

}