Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 24 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
24
Dung lượng
729,5 KB
Nội dung
Sưu tầm bởi: www.daihoc.com.vn 59 Chương 3 Cácluồngvàora 1. Khái niệm về luồng trong Java Khi lập bất kỳ chương trình nào trong một ngôn ngữ nào thì vấn đề vàora dữ liệu giữa chương trình và nguồn dữ liệu cũng như đích dữ liệu là vấn đề mà người lập trình cần phải quan tâm. Làm thế nào để ta có thể truyền dữ liệu cho một chương trình Java. Có hai cách hiệu quả để thực hiện điều này: Thông qua một tài nguyên tuần tự nào đó như file hoặc qua một máy tính khác. Thông qua giao diện người máy. Mục đích của chương này là xem xét cách truyền dữ liệu cho một chương trình thông qua một máy tính khác hay tập tin. 1.1. Khái niệm luồng (stream) Theo nghĩa đen luồng là một đường ống nước. Về mặt thuật ngữ chuyên ngành ta có thể hiểu “Các luồng là các dãy dữ liệu có sắp thứ tự”. Xét trên quan điểm của chương trình và nguồn dữ liệu (Data Soure) ta có thể phân loại luồng thành hai loại: Luồng xuất (output stream) và luồng nhập (input stream). Để trực quan hơn chúng ta xem hình vẽ dướ đây: Hình 3.1 Như vậy nếu chúng ta cần lấy dữ liệu từ nguồn vào chương trình thì cần phải sử dụng luồng nhập. Ngược lại, nếu ta cần ghi dữ liệu từ chương trình ra nguồn dữ liệu thì ta cần phải sử dụng luồng xuất. Ta có thể thấy rằng có rất nhiều luồng dữ liệ, chẳng hạn như từ một tệp tin, từ các thiết bị xuất và nhập chuẩn, từ liên kết mạng. Như vậy một chương trình có thể truy xuất tới nhiiều nguồn dữ liệu. Program Data Source InputStream OutputStream Sưu tầm bởi: www.daihoc.com.vn 60 Hình 3.2 2. Luồng xuất nhập chuẩn System.out: Luồng xuất chuẩn thường được sử dụng để hiển thị kết quả đầu ra trên màn hình. System.in: Luồng nhập chuẩn thường đến từ bàn phím và được sử dụng để hiện các ký tự. System.err: Luồng lỗi chuẩn. Cácluồng trên còn được gọi là cácluồng hệ thống. Mặc dù cácluồng này rất có ích khi lập trình nhưng chúng không đủ mạnh khi giải quyết các vấn đề vàora quan trọng khác. Trong các mục tiếp theo ta sẽ tìm hiểu sâu một số luồng trong gói java.io 3. Luồnng nhị phân 3.1. Lớp InputStream Lớp trừu tượng InputStream khai báo các phương thức để đọc dữ liệu đầu vào từ một nguồn cụ thể. Lớp InputStream là lớp cơ sở của hầu hết cácluồng nhập trong gói java.io, và nó hỗ trợ các phương thức sau: Các phương thức: public InpuStream() InputStream chỉ hỗ trợ constructor không tham số. public abstract int read() throws IOException Phương thức cơ bản của lớp InputStream là phương thức read(). Phương thức này đọc một byte dữ liệu từ luồng nhập và trả về một số kiểu nguyên int có giá trị nằm trong khoảng từ 0 đến 255. Giá trị trả về là -1 khi kết thúc luồng. Phương thức read() chờ và phong tỏa các đoạn mã sau nó cho tới khi một byte dữ liệu được đọc. Việc nhập và xuất diễn ra với tốc độ chậm, vì vậy nếu chương trình của ta thực hiện một công việc khác quan trọng thì tốt nhất là đặt các lệnh nhập xuất vào một tuyến đoạn riêng của nó. Phương thức read() là phương thức trừu tượng bởi vì các lớp con cần thay đổi để thích ích với môi trường cụ thể. public int read(byte[] b) throws IOException Phương thức này đọc một dãy các byte dữ liệu liên tục từ một nguồn của luồng nhập và lưu vào mảng b. public int read(byte[] b, int offs, int len) throws IOException Phương thức này đọc một dãy các byte dữ liệu và lưu vào mảng b, vị trí bắt đầu lưu dữ liệu là offs và lưu len byte dữ liệu Thiết bị Console Tệp tin Mạng InputStream OutputStream Chương trình ứng dụng Sưu tầm bởi: www.daihoc.com.vn 61 public int available() throws IOException Phương thức này cho biết còn bao nhiêu byte dữ liệu trong luồng. public long skip(long count) throws IOException Phương thức skip(long count) bỏ qua long byte dữ liệu public synchronized void mark(int readLimit) Phương thức này được sử dụng để dánh dấu vị trí hiện thời trong luồng public void reset() throws IOException Phương thức này xác định lại vị trí luồng là vị trí đánh dấu lần gần đây nhất. public boolean markSupported() Phương thức này trả về giá trị true nếu luồng này hỗ trợ đánh dấu và false nếu nó không hỗ trợ đánh dấu. public void close() throws IOException Khi đã làm việc xong với một luồng, ta cần đóng lại luồng đó. Điều này cho phép hệ điều hành giải phóng các tài nguyên gắn với luồng. 3.2. Lớp OutputStream Lớp trừu tượng OutputStream khai báo các phương thức để ghi dữ liệu ra luồng. Chúng bao gồm các phương thức sau đây: public OuputStream() Phương thức OutputStream hỗ trợ constructor không tham số public abstract void write(int b)throws IOException Phương thức này ghi một byte không dấu có giá trị trong khoảng từ 0 đến 255. Nếu ta truyền vào một số có giá trị lớn hơn 255 hoặc nhỏ hơn 0, nó sẽ thực hiện phép tính b =b mod 256 trước khi ghi giá trị vào luồng. public void write(byte[] b)throws IOException Phương thức này ghi dữ liệu từ luồngvào toàn bộ mảng b. public void write(byte[] b, int off, int len) throws IOException Phương thức này chỉ ghi một đoạn con của mảng dữ liệu bắt đầu từ vị trí offs và tiếp tục cho tới khi ghi hết len byte. public void close() Phương thức này đóng một luồng. Phương thức này được gọi để giải phóng các tài nguyên gắn với luồng. public void flush() Cácluồng xuất nhập khác được thừa kế từ cácluồng trừu tượng InputStream và OutputStream. Đối với mỗi kiểu dữ liệu và nguồn dữ liệu chúng ta có thể có các kiểu luồng xuất và nhập riêng, chẳng hạn DataInputStream, DataOutputStream, FileInputStream, FileOutputStream,… Sau đây chúng ta sẽ lần lượt xem xét từng kiểu luồng cụ thể. Sưu tầm bởi: www.daihoc.com.vn 62 3.3. Cácluồng xuất nhập mảng byte Để xây dựng một xâu ký tự biểu diễn dữ liệu có thể đọc được hoặc giải mã dữ liệu, người ta xem các mảng byte như là nguồn của cácluồng nhập hoặc đích của cácluồng xuất. Cácluồng byte cung cấp các khả năng này. Hình 3.3 3.3.1. Luồng nhập mảng byte Lớp ByteArrayInputStream sử dụng một mảng byte như là một nguồn dữ liệu đầu vào. Nó có hai constructor: public ByteArrayInputStream(byte[] buf) Tạo ra một đối tượng ByteArrayInputStream từ một mảng xác định. Mảng đầu vào được sử dụng một cách trực tiếp. Khi kết thúc buf nghĩa là kết thúc nhập từ luồng. public ByteArrayInputStream(byte[] buf, int offset, int length) Tạo ra một đối tượng ByteArrayInputStream từ một mảng xác định, chỉ sử dụng một phần của mảng buf từ buf[offset] đến buff[offset+length-1] hoặc kết thúc mảng. ByteArrayInputStream tạo ra một luồng nhập từ một vùng đệm trong bộ nhớ được biểu diễn bằng một mảng byte. Lớp này không hỗ trợ bất kỳ phương thức mới nào, nó nạp chồng các phương thức read(), skip(), available(), và reset() của lớp cha InputStream. Ví dụ: Tạo một mảng gồm 100 byte rồi gắn vào mảng này một luồng ByteArrayInputStream để lấy dữ liệu ra. import java.io.*; public class LuongNhapMang { public static void main(String[] args) { byte[] b = new byte[100]; for(byte i=0;i<b.length;i++) b[i]=i; try{ InputStream is = new ByteArrayInputStream(b); for(byte i=0;i<b.length;i++) ByteArrayInputStream ByteArrayOutputStream Sưu tầm bởi: www.daihoc.com.vn 63 System.out.print(is.read()+" "); } catch(IOException e) { System.err.println(e); } } } Kết quả thực hiện chương trình C:\MyJava\Baitap>java LuongNhapMang 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 Chú ý: Mỗi lần đọc luồng bằng phương thức read(), một byte dữ liệu không còn trong luồng, nhưng vẫn tồn tại trong mảng. 3.3.1. Luồng nhập mảng byte ByteArrayOutputStream tạo ra một luồng xuất trên một mảng byte. Nó cũng cung cấp các khả năng bổ sung. Các constructor: public ByteArrayOutputStream() Tạo ra một đối tượng ByteArrayOutputStream với kích thước mặc định public ByteArrayOutputStream(int size) Tạo ra một đối tượng ByteArrayOutputStream với kích thước xác định ban đầu. Các phương thức mới của lớp ByteArrayOutputStream: public synchronized byte[] toByteArray(): Phương thức này trả về một bản sao dữ liệu của luồng và lưu dữ liệu vào một mảng và có thể sửa đổi dữ liệu trong mảng này mà không cần thay đổi các byte của luồng xuất. public size() Trả về kích thước hiện thời của vùng đệm public String toString(int hiByte) Tạo một đối tượng String mới từ nội dung của luồng xuất mảng byte public String toString() Phương thức chuyển đổi một luồng byte thành một đối tượng String Ví dụ: Viết chương trình tạo lập một luồng xuất mảng (ByteArrayOutputStream) 100 byte. Ghi vàoluồng xuất mảng 100 phần tử từ 0 đến 99. Đổ dữ liệu từ luồng xuất mảng vào mảng b. In dữ liệu từ mảng b ra màn hình. import java.io.*; class LuongXuatMang { Sưu tầm bởi: www.daihoc.com.vn 64 public static void main(String[] args) { try{ //Tao mot luong xuat mang 100 byte ByteArrayOutputStream os = new ByteArrayOutputStream(100); //Ghi du lieu vaoluong for(byte i=0;i<100;i++) os.write(i); //Doc du lieu tu luongvao mang byte[] b = os.toByteArray(); for(byte i=0;i<100;i++) System.out.print(b[i]+" "); os.close(); } catch(IOException e) { System.err.println(e); } } } Kết quả thực hiện chương trình: C:\MyJava\Baitap>java LuongXuatMang 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 3.4. Luồng xuất nhập tập tin Phần lớn việc nhập và xuất dữ liệu trong các ứng dụng là đọc và ghi dữ liệu từ các tệp tin và ghi vào dữ liệu vào tệp tin. Hai luồng trong java.io thực hiện việc xuất nhập tệp tin là FileInputStream và FileOutputStream. Mỗi kiểu luồng có ba constructor. Một constructor nhận một đối tượng String làm tên của tệp tin. Một constructor nhận một đối tượng File để tham chiếu đến tệp tin. Một constructor nhận đối tượng FileDescriptor làm tham số. FileDescriptor biểu diễn một giá trị phụ thuộc vào hệ thống mô tả một tệp đang mở. Đối với luồng xuất nhập tập tin ta hình dung như sau: chương trình Java là nơi tiêu thụ dữ liệu, tập tin là nơi cung cấp dữ liệu. Để đọc dữ liệu từ tập tin vào bộ nhớ ta sử dụng luồng nhập tập tin FileInputStream. Để ghi dữ liệu từ bộ nhớ vào tập tin ta sử dụng luồng xuất tập tin FileOutputStream. Sưu tầm bởi: www.daihoc.com.vn 65 Hình 4.5 Ví dụ import java.io.*; public class FileIOExam { public static void main(String[] args) { //Tao mot file de ghi try{ OutputStream os = new FileOutputStream(args[0]); String s = "Thu nghiem voi luong xuat nhap tap tin"; for(int i=0;i<s.length();i++) os.write(s.charAt(i)); os.close(); //Mo de doc InputStream is = new FileInputStream(args[0]); int len = is.available(); System.out.println("Luong nhap co "+len+ " bytes"); byte b[] = new byte[len]; int sobyte = is.read(b,0,len); System.out.println(sobyte+ " la so bytes da doc"); System.out.println(new String(b)); is.close(); } catch(IOException e) { System.err.println(e); } } } Kết quả thực hiện chương trình FILE FileInputStream FileOutputStream Sưu tầm bởi: www.daihoc.com.vn 66 C:\MyJava\Baitap>java FileIOExam abc.txt Luong nhap co 38 bytes 38 la so bytes da doc Thu nghiem voi luong xuat nhap tap tin 3.5. Truy nhập tệp ngẫu nhiên RandomAccessFile cho phép ta truy nhập trực tiếp vàocác tệp, nghĩa là có thể đọc, ghi các byte ở bất kỳ vị trí nào đó trong tệp. Các phương thức tạo luồng truy nhập tệp ngẫu nhiên RandomAccessFile(String name, String mode) throws IOException RandomAccessFile(File file, String mode) throws IOException Tệp được xác định bởi tên hoặc đối tượng File. Tham số mode cho phép xác định mở file để đọc hay ghi. -“r”: Dùng để đọc. -“rw”: Dùng để ghi. Các phương thức khác long getFilePointer() throws IOException : Trả về vị trí của con trỏ tệp. long length() throws IOException: cho biết số byte hay độ dài của tệp. void seek(long offset) throws IOException: Chuyển con trỏ tệp đi offset vị trí kể từ đầu tệp. void close() throws IOException: Khi không cần truy nhập tệp nữa thì đóng lại. Ví dụ: import java.io.*; public class RandomAccessDemo { static String filename="dayso.dat"; final static int INT_SIZE=4; //Tao file de ghi public void createFile() throws IOException { File datFile = new File(filename); RandomAccessFile out_file = new RandomAccessFile(datFile,"rw"); for(int i=0;i<10;i++)out_file.writeInt(i*i); out_file.close(); } //Mo file de doc public void readFile() throws IOException { File datFile = new File(filename); Sưu tầm bởi: www.daihoc.com.vn 67 RandomAccessFile inp_file= new RandomAccessFile(datFile,"r"); System.out.println("Cac so doc tu file:"); long len = inp_file.length(); for(int i=INT_SIZE;i<len;i+=2*INT_SIZE) { inp_file.seek(i); System.out.println(inp_file.readInt()); } inp_file.close(); } //Mo file de ghi public void extendFile() throws IOException { RandomAccessFile out_file = new RandomAccessFile(filename,"rw"); for(int i=10;i<20;i++) out_file.writeInt(i*i); out_file.close(); } public static void main(String[] args) { try{ RandomAccessDemo rnd = new RandomAccessDemo(); rnd.createFile(); rnd.readFile(); rnd.extendFile(); rnd.readFile(); } catch(IOException e) { System.err.println(e); } } } Sưu tầm bởi: www.daihoc.com.vn 68 3.5. Luồng PrintStream Luồng PrintStream được sử dụng mỗi khi cần sử dụng các phương thức print và println trong chương trình. Lớp PrintStream là lớp con của lớp InputStream, vì vậy ta có thể sử dụng luồng này để lọc các byte. Nó cung cấp các phương thức print và println cho các kiểu dữ liệu sau: char int float Object boolean char[ ] long double String Ngoài ra phương thức println không tham số được sử dụng để kết thúc một dòng. PrintStream hỗ trợ hai constuctor. Constructor thứ nhất nhận tham số là một luồng. Constructor thứ hai có thêm tham số điều khiển việc đẩy dữ liệu ra khỏi luồng. Ngoài ra còn một số kiểu luồng xuất và nhập khác như DataInputStream, DataOutputStream,…tùy thuộc vào từng tình huống cụ thể mà chúng ta có những lựa chọn cho phù hợp. 4. Luồng ký tự Luồng ký tự cung cấp một cách thức để quản lý việc vàora với các ký tự. Cácluồng này sử dụng tập ký tự Unicode và vì thế có thể quốc tế hóa. Trong một số trường hợp làm việc với cácluồng ký tự hiệu quả hơn luồng byte. Cácluồng ký tự chuẩn vay mượn từ rất nhiều các lớp luồng hướng byte, bao gồm luồng lọc, luồng đệm, và cácluồng tệp tin, và tất cả cácluồng được dẫn xuất từ các lớp cha Reader và Writer. Ngoài ra, có hai lớp đóng vai trò cầu nối giữa cácluồng byte và cácluồng ký tự. Hai lớp này kết hợp các hàm chuyển đổi các ký tự thành các byte và ngược lại theo một kiểu mã hóa đã được xác định. Điều này cho phép một nguồn dữ liệu ASCII được chuyển đổi dễ dàng thành một luồng ký tự Unicode và tương tự cho dữ liệu Unicode được ghi một cách dễ dàng vào một tệp tin theo chuẩn mã hóa cục bộ, cho dù nó là chuẩn 8-bit, UTF-8, hoặc 16 bit. Hình 4.6 Hình 4.7 4.1. Sự tương ứng giữa luồng byte và luồng ký tự Bảng dưới đây chỉ ra sự tương ứng giữa luồng byte và luồng ký tự Luồng byte Luồng ký tự OuputStream Writer InputStream Reader FileOutputStream FileWriter FileInputStream FileReader ByteArrayInputStream CharArrayReader InputStream InputStreamReader 72 105 33 8-bit 16-bit “Hi!” OutputStream OutputStreamWriter “Hi!” 16-bit 255 254 0 105 0 33 0 [...]... tìm hiểu các khái niệm căn bản về vàora bằng cách sử dụng cácluồng trong Java Cũng trong chương này cácluồng hướng byte và cácluồng hướng ký tự trong Java đã được giới thiệu Khái niệm vàora mới bằng cách sử dụng các kênh (channel) và vùng đệm (buffer) cũng được giới thiệu trong chương này Ở các chương tiếp theo các bạn sẽ thấy hầu hết các chương trình lập trình mạng đều vào ra dữ liệu bằng cách sử... ta làm việc với nhiều kênh Selector được hỗ trợ bởi các lớp trong gói java.io.channels Các selector ứng dụng nhiều nhất với các kênh dựa trên luồng 6.5 Sử dụng hệ thống vàora mới Đơn vị dữ liệu vàora phổ biến nhất là tệp tin, trong phần này ta sẽ xem cách thức để truy xuất tới các tệp tin trên đĩa bằng cách sử dụng hệ thống vàora mới Do hầu hết các thao tác trên tệp là mức byte nên kiểu vùng đệm... Constructor này tạo ra một đối tượng FileWriter để ghi thông tin vào một tệp tin cụ thể, sử dụng cách mã hóa ký tự mặc định Biến boolean append xác định cách thức ghi vào tệp tin: ghi mới hay bổ sung thêm nội dụng vào tệp hiện có Các phương thức Lớp FileWriter cung cấp tất cả các phương thức thường dùng của lớp Writer Việc ghi các ký tự vào một luồng FileWriter tạo racác ký tự được chuyển thành các byte tương... FileDescriptor.in, luồng này gắn với bàn phím Ta cũng gắn FileWriter với FileDescriptor.out, luồng này gắn với màn hình Trong vòng lặp, dữ liệu được đọc từ bàn phím vào vùng đệm b[], chuyển đổi các ký tự này thành chữ viết hoa sau đó ghi dữ liệu raluồng xuất 5 Luồng đệm Cácluồng InputStream và OuptutStream là cácluồng thô Chúng đọc và ghi các byte theo từng nhóm Việc xác định các byte này có ý nghĩa... tương ứng với cách mã hóa cụ thể và các byte này được ghi vào tệp tin gắn với luồng này Ngoại lệ IOException sẽ được đưa ra bởi các phương thức của lớp FileWriter nếu gặp một lỗi trong quá trình ghi tệp tin, hoặc không tạo ra được đối tượng FileWriter thì nó đưa ra ngoại lệ IOException 4.8 Lớp FileReader Lớp Reader này cung cấp một interface luồng ký tự để đọc các tệp văn bản bằng cách sử dụng cách mã hóa... sẽ là ByteBuffer 6.5.1 Đọc tệp Có một số cách để đọc dữ liệu từ một tệp tin bằng cách sử dụng hệ thống vàora mới Chúng ta sẽ xem xét hai cách Cách thứ nhất đọc một tệp tin bằng cách ánh xạ nó vào một buffer và sau đó thực hiện một thao tác đọc Cách thứ hai để đọc một tệp tin là tự động hóa quá trình đọc Cách 1: Bước 1: Mở một tệp tin để đọc bằng cách sử dụng luồng FileInputStream Bước 2: Nhận một... diễn một liên kết mở tới một thiết bị vàora mới, như tệp tin hoặc một socket Để sử dụng hệ thống vàora mới, ta phải nhận một kênh truyền tới một thiết bị vào ra và một vùng đệm để lưu trữ dữ liệu Sau đó ta có thể thực hiện thao tác trên vùng đệm để vào và ra dữ liệu 6.2 Buffer (Các vùng đệm) Các vùng đệm được định nghĩa trong gói java.io Tất cả các vùng đệm là các lớp con của lớp con Buffer, lớp này... hoặc một luồng khác void write(String str) throws IOException Phương thức này ghi một xâu ký tự str raluồng 4.4 Lớp Reader Reader là lớp cha của tất cả cácluồng nhập ký tự Nó cung cấp các phương thức tương tự như luồng InputStream, nhưng chủ yếu phục vụ cho việc đọc các ký tự protetected Reader() protected Reader(Object lock) 4.4.1 Các phương thức Các phương thức của lớp Reader giống như các phương... nguyên hay các số dấu phẩy động theo chuẩn IEEE 754 hay là các ký tự Unicode-điều này hoàn toàn phụ thuộc vào người lập trình Tuy nhiên, có những khuôn dạng dữ liệu rất phổ biến đã được cài đặt trong các thư viện lớp Java cung cấp một số lớp lọc để ta gắn cácluồng dữ liệu thô với chúng nhằm mục đích chuyển đổi qua lại giữa các byte và các khuôn dạng dữ liệu khác Cácluồng lọc cũng có hai loại là :luồng. .. Selector CharSet xác định cách ánh xạ các byte thành các ký tự Ta có thể mã hóa một xâu ký tự bằng cách sử dụng một bộ mã hóa và cũng có thể giải mã một dãy các byte thành các ký 76 Sưu t m b i: www.daihoc.com.vn tự bằng cách sử dụng bộ giải mã Charset, encoder và decoder được hỗ trợ bởi gói java.nio.charset Selector hỗ trợ vàora ghép kênh, không phong tỏa, dựa trên phím Ngoài ra, selector còn cho phép . với các luồng ký tự hiệu quả hơn luồng byte. Các luồng ký tự chuẩn vay mượn từ rất nhiều các lớp luồng hướng byte, bao gồm luồng lọc, luồng đệm, và các luồng. người ta xem các mảng byte như là nguồn của các luồng nhập hoặc đích của các luồng xuất. Các luồng byte cung cấp các khả năng này. Hình 3.3 3.3.1. Luồng nhập