Trong bài học trước tôi đã giới thiệu về luồng vào ra nhị phân (input-output binary stream), trong bài này chúng ta tiếp tục tìm hiểu về luồng vào ra ký tự (input-output character stream) trong Java. Như bạn đã biết uồng nhị phân (binary stream), mỗi một lần đọc/ghi một byte (Tương đương với 8 bit), trong khi đó luồng ký tự (character stream) mỗi lần đọc/ghi một ký tự, tùy thuộc vào kiểu mã hóa (encoding) ( UTF-8, UTF-16,..) mà ký tự đó tương đương với 1, 2 hoặc 3 byte.
Trong Java, có nhiều lớp hỗ trợ các thao tác với luồng ký tự và các lớp này được dẫn đầu bởi 2 class Reader và Writer:
- java.io.Reader: được sử dụng để đọc dữ liệu từ một nguồn (source).
- java.io.Writer: được sử dụng để ghi dữ liệu đến đích (destination).
Nội dung
- 1 Đọc chuỗi từ console
- 2 Sử dụng lớp FileReader và FileWriter
- 3 Chuyển một luồng nhị phân thành luồng ký tự
- 4 Sử dụng FilterInputStream và FilterOutputStream
- 5 Sử dụng lớp BufferedReader và BufferedWriter
- 6 Sử dụng lớp CharArrayReader và CharArrayWriter
- 7 Ví dụ ghi một dữ liệu chung ra nhiều file
- 8 Sử dụng lớp StringReader và StringWriter
- 9 Ghi file với lớp PrintStream
- 10 Ghi file với lớp PrintWriter
- 11 Sử dụng lớp PushbackInputStream
- 12 Sử dụng lớp PushbackReader
- 13 Sử dụng lớp PipedReader và PipedWriter
Đọc chuỗi từ console
Ví dụ nhập một chuỗi từ bàn phím và hiển thị chuỗi ra màn hình
package com.gpcoder.characterstream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class ReadConsole { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while (true) { System.out.print("Enter your name: "); String name = br.readLine(); if (name.equalsIgnoreCase("Exit")) { System.out.println("Finished!"); break; } System.out.println("Hello " + name); } } }
Kết quả thực thi chương trình trên:
Enter your name: gpcoder.com Hello gpcoder.com Enter your name: exit Finished!
Sử dụng lớp FileReader và FileWriter
Ví dụ ghi file sử dụng lớp FileWriter
import java.io.FileWriter; public interface FileWriterExample { public static void main(String args[]) { try { FileWriter fw = new FileWriter("data/test.txt"); fw.write("gpcoder.com"); fw.close(); } catch (Exception e) { System.out.println(e); } System.out.println("Success..."); } }
Thực thi chương trình trên, một file test.txt được tạo ra trong thư mục data với nội dung gpcoder.com
Ví dụ đọc file sử dụng lớp FileReader
package com.gpcoder.characterstream; import java.io.FileReader; public class FileReaderExample { public static void main(String args[]) throws Exception { FileReader fr = new FileReader("data/test.txt"); int i; while ((i = fr.read()) != -1) { System.out.print((char) i); } fr.close(); } }
Kết quả thực thi chương trình trên:
gpcoder.com
Chuyển một luồng nhị phân thành luồng ký tự
Ví dụ chuyển từ OutputStream sang Writer
package com.gpcoder.characterstream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; public class OutputStreamWriterExample { public static void main(String[] args) throws IOException { // Tạo một OutputStream (luồng đầu ra) để ghi dữ liệu vào file. OutputStream out = new FileOutputStream("data/test.txt"); // Tạo một Character Stream (luồng ghi ký tự) với mã hóa (encoding) là UTF-8. Writer writer = new OutputStreamWriter(out, "UTF-8"); String s = "Lập trình Java"; writer.write(s); writer.close(); } }
Ví dụ chuyển từ InputStream sang Reader
package com.gpcoder.characterstream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; public class InputStreamReaderExample { public static void main(String[] args) throws IOException { // Tạo một binary Stream (luồng nhị phân), để đọc file. InputStream in = new FileInputStream("data/test.txt"); // Tạo một Character stream (luồng ký tự) với mã hóa (encoding) là UTF-8. Reader reader = new InputStreamReader(in, "UTF-8"); int i = 0; // Đọc lần lượt từng ký tự. while ((i = reader.read()) != -1) { // Ép kiểu (cast) thành một ký tự và in ra màn hình. System.out.println((char) i + " " + i); } reader.close(); } }
Sử dụng FilterInputStream và FilterOutputStream
Lớp FilterOutputStream trong java extends lớp OutputStream. Nó cung cấp các lớp con khác nhau như BufferedOutputStream và DataOutputStream để cung cấp các chức năng bổ sung. Vì vậy, nó ít được sử dụng riêng lẻ.
Lớp FilterInputStream trong java extends lớp InputStream. Nó cung cấp các lớp con khác nhau như BufferedInputStream và DataInputStream để cung cấp chức năng bổ sung. Vì vậy, nó ít được sử dụng riêng lẻ.
Ví dụ ghi file sử dụng lớp FilterOutputStream
package com.gpcoder.characterstream; import java.io.File; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; public class FilterOutputStreamExample { public static void main(String[] args) throws IOException { FileOutputStream file = null; FilterOutputStream filter = null; try { file = new FileOutputStream(new File("data/test.txt")); filter = new FilterOutputStream(file); String s = "gpcoder.com"; byte b[] = s.getBytes(); filter.write(b); filter.flush(); System.out.println("Success..."); } finally { filter.close(); file.close(); } } }
Ví dụ đọc file sử dụng lớp FilterInputStream
package com.gpcoder.characterstream; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; public class FilterInputStreamExample { public static void main(String[] args) throws IOException { FileInputStream file = null; FilterInputStream filter = null; try { file = new FileInputStream(new File("data/test.txt")); filter = new BufferedInputStream(file); int k = 0; while ((k = filter.read()) != -1) { System.out.print((char) k); } } catch (IOException ex) { ex.printStackTrace(); } file.close(); filter.close(); } }
Sử dụng lớp BufferedReader và BufferedWriter
Lớp BufferedWriter trong java được sử dụng để cung cấp bộ đệm cho các các thể hiện của lớp Writer. Nó giúp hiệu suất nhanh. Nó thừa kế lớp Writer. Các ký tự đệm được sử dụng để cung cấp việc ghi dữ liệu hiệu quả với các mảng đơn, các ký tự và chuỗi.
Lớp BufferedReader trong java được sử dụng để đọc văn bản từ một input stream dựa trên các ký tự (character stream). Nó có thể được sử dụng để đọc dữ liệu theo dòng (line by line) bằng phương thức readLine(). Nó giúp hiệu suất nhanh. Nó kế thừa lớp Reader.
Ví dụ ghi file sử dụng BufferedWriter
package com.gpcoder.characterstream; import java.io.BufferedWriter; import java.io.FileWriter; public class BufferedWriterExample { public static void main(String[] args) throws Exception { FileWriter writer = new FileWriter("data/test.txt"); BufferedWriter buffer = new BufferedWriter(writer); buffer.write("gpcoder.com"); buffer.close(); System.out.println("Success..."); } }
Ví dụ đọc file sử dụng BufferedReader
package com.gpcoder.characterstream; import java.io.BufferedReader; import java.io.FileReader; public class BufferedReaderExample { public static void main(String args[]) throws Exception { FileReader fr = new FileReader("data/test.txt"); BufferedReader br = new BufferedReader(fr); int i; while ((i = br.read()) != -1) { System.out.print((char) i); } br.close(); fr.close(); } }
Sử dụng lớp CharArrayReader và CharArrayWriter
CharArrayReader gồm có hai từ: CharArray và Reader. Lớp CharArrayReader trong java được sử dụng để đọc mảng ký tự như là một trình đọc (Reader). Nó kế thừa lớp Reader.
Lớp CharArrayWriter trong java có thể được sử dụng để ghi dữ liệu chung cho nhiều file. Lớp này thừa kế lớp Writer. Bộ đệm của nó tự động phát triển khi dữ liệu được ghi vào stream này. Gọi phương thức close() đối với đối tượng này không có hiệu lực.
Ví dụ đọc một ký tự sử dụng lớp Java CharArrayReader
package com.gpcoder.characterstream; import java.io.CharArrayReader; public class CharArrayReaderExample { public static void main(String[] ag) throws Exception { char[] ary = { 'g', 'p', 'c', 'o', 'd', 'e', 'r', '.', 'c', 'o', 'm' }; CharArrayReader reader = new CharArrayReader(ary); int k = 0; // Read until the end of a file while ((k = reader.read()) != -1) { char ch = (char) k; System.out.print(ch + " : "); System.out.println(k); } } }
Kết quả thực thi chương trình trên:
g : 103 p : 112 c : 99 o : 111 d : 100 e : 101 r : 114 . : 46 c : 99 o : 111 m : 109
Ví dụ ghi một dữ liệu chung ra nhiều file
package com.gpcoder.characterstream; import java.io.CharArrayWriter; import java.io.FileWriter; public class CharArrayWriterExample { public static void main(String args[]) throws Exception { CharArrayWriter out = new CharArrayWriter(); out.write("gpcoder.com"); FileWriter f1 = new FileWriter("data/f1.txt"); FileWriter f2 = new FileWriter("data/f2.txt"); FileWriter f3 = new FileWriter("data/f3.txt"); FileWriter f4 = new FileWriter("data/f4.txt"); out.writeTo(f1); out.writeTo(f2); out.writeTo(f3); out.writeTo(f4); f1.close(); f2.close(); f3.close(); f4.close(); System.out.println("Success..."); } }
Thực thi chương trình trên, 4 file f1.txt, f2.txt, f3.txt, f4.txt được tạo ra trong thư mục data và có cùng nội dung gpcoder.com
Sử dụng lớp StringReader và StringWriter
Lớp StringWriter trong java là một charater stream thu thập dữ liệu từ bộ đệm chuỗi, có thể được sử dụng để xây dựng một chuỗi. Lớp StringWriter kế thừa lớp Writer. Trong lớp StringWriter, các tài nguyên hệ thống như các network socket và file không được sử dụng, do đó việc đóng StringWriter là không cần thiết.
Lớp StringReader trong java là một character stream với chuỗi như một nguồn dữ liêu. Nó lấy một chuỗi đầu vào và thay đổi nó vào character stream. Nó kế thừa lớp Reader. Trong lớp StringReader, các tài nguyên hệ thống như các network socket và các file không được sử dụng, do đó việc đóng StringReader là không cần thiết.
Ví dụ StringWriter sử dụng BufferedReader để đọc file từ luồng (stream)
package com.gpcoder.characterstream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; public class StringWriterExample { public static void main(String[] args) throws IOException { char[] arr = new char[512]; StringWriter writer = new StringWriter(); FileInputStream input = null; BufferedReader buffer = null; input = new FileInputStream("data/test.txt"); buffer = new BufferedReader(new InputStreamReader(input, "UTF-8")); int x; while ((x = buffer.read(arr)) != -1) { writer.write(arr, 0, x); } System.out.println(writer.toString()); writer.close(); buffer.close(); } }
Ví dụ StringReader đọc chuỗi như luồng ký tự (stream)
package com.gpcoder.characterstream; import java.io.StringReader; public class StringReaderExample { public static void main(String[] args) throws Exception { String srg = "gpcoder.com"; StringReader reader = new StringReader(srg); int k = 0; while ((k = reader.read()) != -1) { System.out.print((char) k); } } }
Ghi file với lớp PrintStream
Lớp PrintStream trong java cung cấp các phương thức để ghi dữ liệu vào một stream khác. Lớp PrintStream tự động làm sạch dữ liệu vì vậy không cần gọi phương thức flush(). Hơn nữa, các phương thức của nó không ném ngoại lệ IOException.
package com.gpcoder.characterstream; import java.io.FileOutputStream; import java.io.PrintStream; public class PrintStreamExample { public static void main(String args[]) throws Exception { FileOutputStream fout = new FileOutputStream("data/test.txt"); PrintStream pout = new PrintStream(fout); pout.println(2017); pout.println("gpcoder.com"); pout.println("Java I/O Tutorials"); pout.close(); fout.close(); System.out.println("Success..."); } }
Ghi file với lớp PrintWriter
Lớp PrintWriter trong java là bản cài đặt của lớp Writer. Nó được sử dụng để ghi các định dạng đại diện của các đối tượng vào stream hướng văn bản.
package com.gpcoder.characterstream; import java.io.File; import java.io.PrintWriter; public class PrintWriterExample { public static void main(String[] args) throws Exception { // Data to write on Console using PrintWriter PrintWriter writer = new PrintWriter(System.out); writer.write("Data to write on Console using PrintWriter"); writer.flush(); writer.close(); // Data to write in File using PrintWriter PrintWriter writer1 = null; writer1 = new PrintWriter(new File("data/test.txt")); writer1.write("Data to write in File using PrintWriter"); writer1.flush(); writer1.close(); } }
Thực thi chương trình trên, một dòng chữ Data to write on Console using PrintWriter trong Console và một file test.txt được tạo ra trong thư mục data của project với nội dung Data to write in File using PrintWriter.
Sử dụng lớp PushbackInputStream
Lớp PushbackInputStream trong java ghi đè các phương thức của lớp InputStream và cung cấp thêm chức năng mở rộng cho một input stream khác. Nó có thể unread một byte đã được đọc và đẩy trở lại một byte.
package com.gpcoder.characterstream; import java.io.ByteArrayInputStream; import java.io.PushbackInputStream; public class PushbackInputStreamExample { public static void main(String[] args) throws Exception { String srg = "1##2#34###12"; byte[] byteArr = srg.getBytes(); ByteArrayInputStream array = new ByteArrayInputStream(byteArr); PushbackInputStream push = new PushbackInputStream(array); int i; while ((i = push.read()) != -1) { // Tìm thấy ký tự '#' if (i == '#') { int j; // Đọc tiếp một ký tự nữa if ((j = push.read()) == '#') { System.out.print("**"); } else { // Đẩy trở lại (Pushes back) ký tự này lên luồng. // Giống như lùi con trỏ trở lại 1 vị trí. push.unread(j); System.out.print((char) i); } } else { System.out.print((char) i); } } } }
Kết quả thực thi chương trình trên:
1**2#34**#12
Sử dụng lớp PushbackReader
Lớp PushbackReader trong java ghi đè các phương thức của lớp FilterReader và cung cấp thêm các chức năng mở rộng. Nó được sử dụng để đọc một luồng ký tự và có thể đẩy trở lại một ký tự vào stream.
package com.gpcoder.characterstream; import java.io.CharArrayReader; import java.io.PushbackReader; public class PushbackReaderExample { public static void main(String[] args) throws Exception { char ary[] = { '1', '-', '-', '2', '-', '3', '4', '-', '-', '-', '5', '6' }; CharArrayReader reader = new CharArrayReader(ary); PushbackReader push = new PushbackReader(reader); int i; while ((i = push.read()) != -1) { // Tìm thấy ký tự '-' if (i == '-') { int j; // Đọc tiếp một ký tự nữa if ((j = push.read()) == '-') { System.out.print("#*"); } else { // Đẩy trở lại (Pushes back) ký tự này lên luồng. // Giống như lùi con trỏ trở lại 1 vị trí. push.unread(j); System.out.print((char) i); } } else { System.out.print((char) i); } } } }
Kết quả thực thi chương trình trên:
1#*2-34#*-56
Sử dụng lớp PipedReader và PipedWriter
Đặt ra một tình huống bạn có 2 luồng một luồng đầu vào và một luồng đầu ra. Chẳng hạn luồng dữ liệu đầu vào X đọc một file, lấy thông tin từ luồng này ghi vào luồng dữ liệu Y đầu ra là một file khác. Hai luồng X và Y trong tình huống này là tách riêng nhau. Vì vậy trong ứng dụng bạn phải có 3 thao tác:
- Tạo luồng dữ liệu đọc X
- Tạo luồng ghi dữ liệu Y
- Đọc từ X ghi vào Y
Hai thao tác đầu phải có, nhưng bạn muốn bỏ đi thao tác thứ 3, nghĩa là có một cái gì đó liên hệ ngầm với nhau giữa 2 luồng ,để sao cho những ký tự xuất hiện trên luồng đầu đọc X lập tức luồng đầu ra Y biết được và đọc luôn các ký tự đó vào luồng của mình. Đó được gọi là liên hệ đường ngầm giữa 2 luồng vào và ra.
Điều này còn thực sự có ý nghĩa hơn khi biết rằng (với hiệu ứng đường ngầm này) khi luồng đầu ra khi đã đọc hết các ký tự trên luồng đầu vào nó tự động chờ đợi các ký tự nào đó xuất hiện trên luồng đầu vào và lại đọc hết vào luồng đầu ra.
Thật vậy hiệu ứng đường ngầm này chỉ sử dụng hiệu quả trong một vài tình huống không phải tất cả , và khi sử dụng nó thường được sử dụng đi đôi với xử lý đa luồng (Mulit-thread).
Ví dụ sử dụng PipedReader
package com.gpcoder.characterstream; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; import java.io.Reader; public class PipeReaderExample { private Reader pipedReader; public static void main(String[] args) throws IOException, InterruptedException { new PipeReaderExample().test(); } private void test() throws IOException, InterruptedException { // Tạo một 'pipedWriter', PipedWriter pipedWriter = new PipedWriter(); // Dữ liệu ghi vào 'pipedWriter' // sẽ tự động xuất hiện tại 'pipedReader'. pipedReader = new PipedReader(pipedWriter); new ThreadRead().start(); char[] chs = new char[] { 'a', 'a', 'b', 'c', 'e' }; // Ghi dữ liệu vào 'pipedWriter'. for (char ch : chs) { pipedWriter.write(ch); Thread.sleep(1000); } pipedWriter.close(); } // Một Thread đọc dữ liệu xuất hiện trên 'pipedReader'. class ThreadRead extends Thread { @Override public void run() { try { int data = 0; while ((data = pipedReader.read()) != -1) { System.out.println((char) data); } } catch (Exception e) { e.printStackTrace(); } finally { closeQuietly(pipedReader); } } } private void closeQuietly(Reader is) { if (is != null) { try { is.close(); } catch (IOException e) { } } } }
Kết quả thực thi chương trình trên:
a a b c e
Tài liệu tham khảo:
- https://www.javatpoint.com/java-io
- https://docs.oracle.com/javase/tutorial/essential/io/index.html
- http://o7planning.org/vi/10173/huong-dan-su-dung-luong-vao-ra-ky-tu-trong-java