openplanning

Hướng dẫn sử dụng luồng vào ra ký tự trong Java

  1. Giới thiệu
  2. Sự khác biệt giữa luồng nhị phân và luồng ký tự
  3. Tổng quan về luồng vào ra ký tự
  4. Class java.io.Reader
  5. Class java.io.Writer
  6. Làm thế nào để chuyển một luồng nhị phân thành luồng ký tự?
  7. Class java.io.BufferedReader
  8. Class java.io.BufferedWriter
  9. Class java.io.FilterReader
  10. Class java.io.FilterWriter
  11. Class java.util.PushbackReader
  12. Class java.io.PrintWriter
  13. Class java.io.CharArrayReader
  14. Class java.io.CharArrayWriter
  15. Class java.io.PipedReader
  16. Class java.io.PipedWriter

1. Giới thiệu

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), bạn cần phải hiểu về nó trước khi tìm hiểu về luồng vào ra ký tự (input-output character stream), bạn có thể xem tại đây:

2. Sự khác biệt giữa luồng nhị phân và luồng ký tự

Luồ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. Chúng ta hãy xem một hình ảnh minh họa sau:
UTF-16:
Đây là một đoạn văn bản tiếng Nhật, nếu nó được lưu tại File với mã hóa UTF-16 (UTF-16 encoding), lúc đó các byte dưới ổ cứng sẽ giống hình minh họa:
  • Hai byte đầu tiên (254,255) chỉ có ý nghĩa thông báo rằng, nó là bắt đầu của một chuỗi có mã hóa UTF-16.
  • Các ký tự tiếp theo đều được mã hóa bởi 2 byte.
    • Ví dụ như ký tự 'J' được mã hóa bởi 2 byte (0 và 74)
    • Ký tự 'P' được mã hóa bởi 2 byte (0 và 80)
    • ....
  • Khi đọc từ file lên theo mã hóa UTF-16, nó sẽ bỏ 2 byte đầu tiên, và đọc 2 byte liên tiếp ghép lại thành một ký tự.
UTF-8:
Cũng dòng văn bản tiếng Nhật nói trên nếu được ghi xuống file với mã hóa UTF-8 lúc đó sẽ khác đi, hãy xem các byte được lưu xuống ổ cứng:
  • Đối với các ký tự ASCII, nó sẽ chỉ dùng 1 byte để lưu trữ.
    • Ví dụ, nó dùng 1 byte để lưu trữ ký tự 'J' (74).
    • Dùng 1 byte để lưu trữ ký tự 'P' (80).
  • Các ký tự khác nó có thể dùng 2 byte hoặc 3 byte để lưu.
  • Quy tắc để đọc 1 ký tự, dựa vào một bảng mã UTF-8 (UTF-8 Table).
    • Đọc byte đầu tiên, nếu <= 127, thì đó là 1 ký tự ASCII.
    • Ngược lại nếu > 127, thì nó cần đọc tiếp byte thứ 2, và kiểm tra xem 2 byte đó có ghép được thành 1 ký tự dựa vào bảng mã UTF-8 hay không.
    • Nếu 2 byte đầu tiên không tương ứng với một ký tự, nó đọc tiếp byte thứ 3 và ghép thành 1 ký tự.
    • UTF-8 dùng tối đa 3 byte để lưu trữ một ký tự.

Như vậy khi bạn lưu một văn bản với mã hóa (encoding) gì, cần phải đọc ra với mã hóa tương ứng, nếu không việc đọc ra sẽ cho kết quả sai.

3. Tổng quan về luồng vào ra ký tự

Sơ đồ phân cấp các lớp:

4. Class java.io.Reader

Reader là một lớp trừu tượng, nó là lớp cơ sở cho các luồng đọc ký tự.
Tạo một file test_reader.txt để bắt đầu một ví dụ với Reader:
HelloReader.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class HelloReader {

	public static void main(String[] args) throws IOException {
		// Tạo một Reader (Luồng ký tự), để đọc một file.
		// Với mã hóa (encoding) mặc định.
		Reader r = new FileReader("test_reader.txt");
		int i = -1;

		// Đọc lần lượt từng ký tự trong luồng.
		while ((i = r.read()) != -1) {
			// Ép về (cast) thành kiểu ký tự.
			System.out.println((char) i);
		}
		r.close();
	}

}
Kết quả chạy ví dụ:
Ví dụ tiếp theo sẽ đọc nhiều ký tự trong một lần đọc. Cách này làm tăng hiệu năng chương trình so với cách đọc lần lượt từng ký tự.
HelloReader2.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

// Ví dụ này sẽ đọc nhiều ký tự trong một lần.
public class HelloReader2 {

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

		// Tạo một đối tượng Reader đọc một file.
		// Với mã hóa (encoding) mặc định. 
		Reader r = new FileReader("test_reader.txt");
		// Tạo một mảng tạm.
		char[] temp = new char[10];
		int i = -1;

		// Phương thức read(char[]):
		// Đọc nhiều ký tự trong một lần, và gán vào các phần tử của mảng. 
		// Trả về số ký tự đọc được.
		// Trả về -1 nếu luồng (stream) kết thúc.
		while ((i = r.read(temp)) != -1) {
			String s = new String(temp, 0, i);
			System.out.println(s);
		}
		r.close(); 
	}

}

5. Class java.io.Writer

Writer là một lớp trừu tượng, nó là lớp cơ sở cho các luồng ghi ký tự.
HelloWriter.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class HelloWriter {

	public static void main(String[] args) throws IOException {
		File dir = new File("C:/test");

		// Tạo thư mục 'C:/test', nếu nó chưa tồn tại.
		dir.mkdirs();

		// Tạo một đối tượng Writer, để ghi dữ liệu vào file.
		// Với mã hóa (encoding) mặc định.
		Writer w = new FileWriter("C:/test/test_writer.txt");

		// Mảng các ký tự.
		char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', // 
				' ', 'w', 'r', 'i', 't', 'e', 'r' };

		// Ghi lần lượt các ký tự vào luồng (stream).
		for (int i = 0; i < chars.length; i++) {
			char ch = chars[i];
			int j = (int) ch;
			// 
			w.write(j);
		}
		// Đóng luồng (Close stream),
		w.close();
	}
}
Kết quả chạy ví dụ:
Ví dụ tiếp theo, ghi nhiều ký tự vào luồng cùng một lúc. Cụ thể là ghi một mảng các ký tự vào luồng. Việc làm này nâng cao hiệu năng chương trình so với việc ghi lần lượt từng ký tự.
HelloWriter2.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class HelloWriter2 {

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

		File dir = new File("C:/test");
		// Tạo thư mục 'C:/test' nếu nó chưa tồn tại.
		dir.mkdirs();

		// Tạo một đối tượng Writer, để ghi dữ liệu vào file.
		Writer w = new FileWriter("C:/test/test_writer2.txt");

		// 
		char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', //
				' ', 'w', 'r', 'i', 't', 'e', 'r' };

		// Ghi tất cả các ký tự trong mảng vào luồng.
		w.write(chars);

		// Thông thường Java sử dụng bộ đệm (buffer)
		// để lưu trữ dữ liệu một cách tạm thời.
		// Khi bộ đệm (buffer) đầy, dữ liệu sẽ được đẩy (flush) xuống file.
		// Bạn cũng có thể chủ động đẩy (flush) dữ liệu xuống file.
		w.flush();

		// Ghi ký tự xuống dòng (new line character) vào luồng.
		w.write('\n');

		String s = "FileWriter";

		// Ghi một chuỗi vào luồng (stream).
		w.write(s);

		// Đóng luồng (Close stream).
		// Nó sẽ đẩy các dữ liệu trên bộ đệm (buffer) vào file.
		// Đồng thời kết thúc việc ghi dữ liệu.
		w.close();
	}
}
Kết quả chạy ví dụ:

6. Làm thế nào để chuyển một luồng nhị phân thành luồng ký tự?

Bạn có một luồng nhị phân (binary stream). Và bạn muốn chuyển nó thành luồng ký tự (character stream)?
Trên các ví dụ trên chúng ta làm quen với Reader, Writer. Ví dụ tiếp theo cho phép bạn chủ động đọc và ghi dữ liệu vào luồng với một mã cụ thể.
Tạo một file test_utf8.txt
test_utf8.txt
JP日本-八洲
Khi Save, Eclipse sẽ hỏi bạn lưu với mã hóa gì, hãy chọn UTF-8.
InputStreamReaderExample.java
package org.o7planning.tutorial.javaio;

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("test_utf8.txt");

		// Tạo một Character stream (luồng ký tự) bao lấy (wrap) luồng nhị phân trên.
		// 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();
	}
}
Kết quả chạy ví dụ
Ví dụ tiếp theo, ghi ra file chỉ định rõ mã hóa UTF-8.
OutputStreamWriterExample.java
package org.o7planning.tutorial.javaio;

import java.io.File;
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 {
		
		File dir = new File("C:/test");
		// Tạo thư mục 'C:/test' nếu nó không tồn tại.
		dir.mkdirs();
		// Tạo một OutputStream (luồng đầu ra) để ghi dữ liệu vào file.
		OutputStream out = new FileOutputStream("C:/test/test_write_utf8.txt");

		// Tạo một Character Stream (luồng ghi ký tự) bao lấy OutputStream ở trên.
		// Mã hóa (encoding) là UTF-8.
		Writer writer = new OutputStreamWriter(out, "UTF-8");

		String s = "JP日本-八洲";
		writer.write(s);
		writer.close();
	}

}
Kết quả chạy ví dụ

7. Class java.io.BufferedReader

Nếu bạn muốn đọc từng dòng dữ liệu của một file văn bản. BufferedReader là một sự lựa chọn tốt.
// BufferedReader là một lớp con trực tiếp của Reader .
// Constructor:
public BufferedReader(Reader in);

// Một phương thức tiện lợi có được từ BufferedReader.
// Đọc một dòng văn bản.
public String readLine();
Ví dụ:
// Ví dụ 1:
Reader r=new FileReader("C:/test.txt");
BufferedReader br=new BufferedReader(r);

// Ví dụ 2:
InputStream in = new FileInputStream("C:/test.txt");
Reader r = new InputStreamReader(in, "UTF-8");
BufferReader br = new BufferedReader(r);
test_multi_lines.txt
## Fruit List
Apricots
Barbados Cherries
Bitter Melon
Cherimoya
Honeydew
Jackfruit
Limes
Lychee
Mango
Oranges
Pineapple
Strawberries
BufferedReaderExample.java
package org.o7planning.tutorial.javaio.buffered;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class BufferedReaderExample {

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

		InputStream in = new FileInputStream("test_multi_lines.txt");
		Reader reader = new InputStreamReader(in, "UTF-8");
		BufferedReader br = new BufferedReader(reader);

		String s = null;
		int i = 0;

		// Đọc từng dòng (line) dữ liệu.
		// Nếu đọc được null nghĩa là kết thúc Stream.
		while ((s = br.readLine()) != null) {
			i++;
			System.out.println(i + " : " + s);
		}
		br.close();
	}

}
Kết quả chạy ví dụ:

8. Class java.io.BufferedWriter

BufferedWriter là một lớp con trực tiếp của lớp Writer.
// Tạo một đối tượng BufferedWriter 
// bằng cách gói (wrap) một đối tượng Writer khác.
public BufferedWriter(Writer out);

// Tương đương với write('\n'); 
public String newLine();
Ví dụ:
// Tạo đối tượng Writer.
Writer w=new FileWriter("C:/test/test_bufferedWriter.txt");

// Tạo một đối tượng BufferedWriter bao lấy (wrap) một writer.
BufferedWriter bw=new BufferedWriter(w);

bw.write("Hello..");

// Ghi một ký tự xuống dòng '\n'.
bw.newLine();

9. Class java.io.FilterReader

FilterReader là một lớp con của lớp Reader. Nó đọc một cách có chọn lọc các ký tự theo yêu cầu. Chẳng hạn bạn muốn đọc một văn bản HTML, và bỏ qua các ký tự trong các thẻ (tag). Bạn cần viết một lớp con của FilterReader và sau đó sử dụng lớp con đó, bạn không thể sử dụng trực tiếp FilterReader vì nó là một lớp trừu tượng (abstract class).
Ví dụ, tạo một lớp con của lớp FilterReader, đọc dữ liệu HTML nhưng bỏ qua các ký tự trong thẻ.
Ví dụ đầu vào "<h1>Hello</h1>" ==> đọc ra "Hello".
RemoveHTMLReader.java
package org.o7planning.tutorial.javaio.filter;

import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;

public class RemoveHTMLReader extends FilterReader {

	boolean intag = false;

	public RemoveHTMLReader(Reader in) {
		super(in);
	}

	// Chúng ta ghi đè (override) phương thức này.
	// Nguyên tắc sẽ là:
	// Chỉ đọc các ký tự nằm ngoài các thẻ (tag).
	@Override
	public int read(char[] buf, int from, int len) throws IOException {

		int charCount = 0;

		while (charCount == 0) {

			charCount = super.read(buf, from, len);
			if (charCount == -1) {
				// Kết thúc stream.
				return -1;
			}

			int last = from;

			for (int i = from; i < from + charCount; i++) {
				// Nếu không nằm trong thẻ HTML.
				if (!intag) {
					if (buf[i] == '<') {
						intag = true;
					} else {
						buf[last++] = buf[i];
					}
				} else if (buf[i] == '>') {
					intag = false;
				}
			}

			charCount = last - from;
		}
		return charCount;
	}

	// Cũng cần ghi đè phương thức này.
	@Override
	public int read() throws IOException {
		char[] buf = new char[1];
		int result = read(buf, 0, 1);
		if (result == -1) {
			return -1;
		} else {
			return (int) buf[0];
		}
	}
}
RemoveHTMLReaderTest.java
package org.o7planning.tutorial.javaio.filter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

public class RemoveHTMLReaderTest {

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

		// Tạo một đối tượng Reader từ Constructor của StringReader.
		Reader in = new StringReader("<h1>Hello \n <b>World</b><h1>");

		RemoveHTMLReader filterReader = new RemoveHTMLReader(in);
		BufferedReader br = new BufferedReader(filterReader);

		String s = null;
		while ((s = br.readLine()) != null) {
			System.out.println(s);
		}
		br.close();
	}
}
Kết quả chạy ví dụ:

10. Class java.io.FilterWriter

FilterWriter là một lớp con trực tiếp của Writer, nó ghi một cách có chọn lọc các ký tự theo yêu cầu. Bạn cần viết một lớp con của FilterWriter và có ghi đè (override) một vài phương thức của lớp FilterWriter.
Ví dụ: Các ký tự bị thay đổi khi ghi vào luồng.
Rot13.java
package org.o7planning.tutorial.javaio.filter;

public class Rot13 {
    
    /**
     * <pre>
     *   a ==> n
     *   b ==> o
     *   c ==> p
     *   d ==> q
     *   e ==> r
     *   ...
     *   y ==> l
     *   z ==> m
     * </pre>
     */
    public static int rotate(int inChar) {
        int outChar;
        
        if (inChar >= (int) 'a' && inChar <= (int) 'z') {
            outChar = (((inChar - 'a') + 13) % 26) + 'a';
        } else if (inChar >= (int) 'A' && inChar <= (int) 'Z') {
            outChar = (((inChar - 'A') + 13) % 26) + 'A';
        } else {
            outChar = inChar;
        }
        return outChar;
    }
    
    // Test
    public static void main(String[] args)  {
        for(char ch='a'; ch<='z';ch++ ) {
            char m= (char)rotate(ch);
            System.out.println("ch="+ch+" ==> "+ m);    
        }      
        
    }
}
RotateWriter.java
package org.o7planning.tutorial.javaio.filter;

import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;

public class RotateWriter extends FilterWriter {
 
	public RotateWriter(Writer out) {
		super(out);
	}

	// Ghi đè một hoặc nhiều phương thức để sử lý việc lọc.
	// (Ghi đè cả 2 phương thức sẽ an toàn hơn).
	@Override
	public void write(int outChar) throws IOException {
		super.write(Rot13.rotate(outChar));
	}

	@Override
	public void write(char[] cbuf, int offset, int length) throws IOException {
		char[] tempbuf = new char[length];
		for (int i = 0; i < length; i++) {
			tempbuf[i] = (char) Rot13.rotate(cbuf[offset + i]);
		}
		super.write(tempbuf, 0, length);
	}

}
RotateWriterTest.java
package org.o7planning.tutorial.javaio.filter;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

public class RotateWriterTest {

    
    public static void main(String[] args) throws IOException  {
        String s="abcdef";
        
        Writer writer= new StringWriter();
        
        RotateWriter rw= new RotateWriter(writer);
        rw.write(s.toCharArray(),0,s.length());
        
        rw.close();
        
        String rotateString = writer.toString();
        System.out.println("rotateString="+ rotateString);
    }
}
Kết quả chạy ví dụ:

11. Class java.util.PushbackReader

Lớp PushbackReader cho phép một hoặc nhiều ký tự để được đặt trở lại (push back) cho luồng sau khi đã đọc chúng ra. Dưới đây là hai Constructor của nó:
public PushbackReader(Reader inputStream)

public PushbackReader(Reader inputStream, int bufSize)
Một số phương thức bổ xung:
// Đặt trở lại (push back) một ký tự về luồng.
public void unread(int c) throws IOException
Ví dụ:
PushbackReaderDemo.java
package org.o7planning.tutorial.javaio.pushback;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.PushbackReader;

class PushbackReaderDemo {

	public static void main(String args[]) throws IOException {
		String s = "if (a == 4) a = 0;\\n";
		char buf[] = new char[s.length()];
		s.getChars(0, s.length(), buf, 0);
		CharArrayReader in = new CharArrayReader(buf);
		PushbackReader f = new PushbackReader(in);
		int c;
		while ((c = f.read()) != -1) {
			switch (c) {
			// Tìm thấy ký tự '='
			case '=':
				// Đọc tiếp một ký tự nữa.
				// (Sau khi đã tìm thấy ký tự '=' trước đó).
				if ((c = f.read()) == '=') {
					System.out.print(".eq.");
				}

				else {
					System.out.print("<-");
					// Đẩ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í.
					f.unread(c);
				}
				break;
			default:
				System.out.print((char) c);
				break;
			}
		}
	}

}
Kết quả chạy ví dụ:

12. Class java.io.PrintWriter

// Constructor:
// Bao bọc (wrap) một đối tượng Writer.
public PrintWriter(Writer out) 
public PrintWriter(Writer out,boolean autoFlush)

// Bao bọc (wrap) một đối tượng OutputStream.
public PrintWriter(OutputStream out) 
public PrintWriter(OutputStream out,boolean autoFlush)
public PrintWriter(String fileName)
...

// Method:
public void println(String s)
public void print(char ch)
StackTraceToFile.java
package org.o7planning.tutorial.javaio.printwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;

public class StackTraceToFile {

	public static void main(String[] args) {
		try {
			// Làm một việc gì đó.
			// Lỗi chia cho 0.
			int i = 10 / 0;
		} catch (Exception e) {
			System.out.println("EXCEPTION ....");
			try {
				File dir = new File("C:/test");
				// Tạo thư mục nếu nó không tồn tại.
				dir.mkdirs();
				// Tạo một Stream để ghi dữ liệu vào file.
				Writer w = new FileWriter("C:/test/stackTrace.txt");
				
				// Tạo đối tượng PrintWriter bao lấy đối tượng Writer 'w'.
				// Như vậy dữ liệu ghi vào PrintWriter sẽ được ghi vào FileWriter 'w'.
				PrintWriter pw = new PrintWriter(w);

				// Ghi thông tin lỗi vào luồng 'pw'.
				e.printStackTrace(pw);
				System.out.println("Finish !");
			} catch (Exception e1) {
				System.out.println("Error:" + e);
			}
		}
	}

}
StackTraceToString.java
package org.o7planning.tutorial.javaio.printwriter;

import java.io.PrintWriter;
import java.io.StringWriter;

public class StackTraceToString {

	public static void main(String[] args) {
		try {
			// Làm một việc gì đó
			// Lỗi chia cho 0.
			int i = 1000 / 0;
		} catch (Exception e) {
			System.out.println("EXCEPTION ....");
			try {
				StringWriter sw = new StringWriter();
				// Tạo đối tượng PrintWriter bao lấy StringWriter 'sw'.
				// Như vậy dữ liệu ghi vào PrintWriter sẽ ghi vào 'sw'.
				PrintWriter pw = new PrintWriter(sw);

				// Ghi thông tin lỗi vào 'pw'.
				e.printStackTrace(pw);

				StringBuffer sb = sw.getBuffer();
				String s = sb.toString();
				System.out.println("Exception String:");
				System.out.println(s);
			} catch (Exception e1) {
				System.out.println("Error:" + e);
			}
		}

	}

}
Kết quả chạy ví dụ:

13. Class java.io.CharArrayReader

CharArrayReaderDemo.java
package org.o7planning.tutorial.javaio.chararray;

import java.io.CharArrayReader;
import java.io.IOException;

public class CharArrayReaderDemo {
    
    public static void main(String args[]) throws IOException {
        
        String tmp = "abcdefghijklmnopqrstuvwxyz";
        int length = tmp.length();
        char c[] = new char[length];
        tmp.getChars(0, length, c, 0);
        
        CharArrayReader input1 = new CharArrayReader(c);
        CharArrayReader input2 = new CharArrayReader(c, 0, 5);
        
        int i;
        System.out.println("input1 is:");
        while ((i = input1.read()) != -1) {
            System.out.print((char) i);
        }
        System.out.println();
        System.out.println("input2 is:");
        while ((i = input2.read()) != -1) {
            System.out.print((char) i);
        }
        System.out.println();
    }
}
Kết quả chạy ví dụ:

14. Class java.io.CharArrayWriter

Một số phương thức bổ xung:
// Ghi dữ liệu của luồng này sang một luồng khác.
public void writeTo(Writer out) throws IOException
CharArrayWriterDemo.java
package org.o7planning.tutorial.javaio.chararray;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class CharArrayWriterDemo {

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

		char[] c = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
		CharArrayWriter out = new CharArrayWriter();
		// Ghi dữ liệu vào 'out'
		// Write characters in array to 'out'.
		out.write(c);

		File dir = new File("C:/test");
		dir.mkdirs();

		FileWriter f1 = new FileWriter(new File("C:/test/a.txt"));

		// Ghi dữ liệu của 'out' vào 'f1'.
		out.writeTo(f1);

		FileWriter f2 = new FileWriter(new File("C:/test/b.txt"));

		// Ghi dữ liệu từ 'out' vào 'f2'.
		out.writeTo(f2);

		f1.close();
		f2.close();

		// Đóng luồng CharArrayWriter 'out'.
		out.close();

		FileWriter f3 = new FileWriter(new File("C:/test/c.txt"));

		// Với CharArrayWriter, sau khi đóng.
		// Phương thức writeTo(..) không còn có tác dụng.
		// Và cũng không gây ra ngoại lệ nếu bạn sử dụng writeTo(..).
		out.writeTo(f3);

		System.out.println("Done!");
	}
}

15. Class java.io.PipedReader

Đặ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 A và B 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
  • một là thao tác tạo luồng dữ liệu đọc X ,
  • hai là tạo luồng ghi dữ liệu Y ,
  • và ba là đọ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 (Thread ...) .
  • TODO
PipeReaderExample1.java
package org.o7planning.tutorial.javaio.pipereader;

import java.io.IOException;
import java.io.Reader;
import java.io.PipedReader;
import java.io.PipedWriter;

public class PipeReaderExample1 {

	private Reader pipedReader;

	public static void main(String[] args) throws IOException, InterruptedException {
		new PipeReaderExample1().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) {
			}
		}
	}

}
PipeReaderExample2.java
package org.o7planning.tutorial.javaio.pipereader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.Reader;

public class PipeReaderExample2 {

	private BufferedReader bufferedReader;

	public static void main(String[] args) throws IOException, InterruptedException {
		new PipeReaderExample2().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 pipedReader = new PipedReader(pipedWriter);
		
		// Tạo một 'bufferedReader' bao lấy 'pipedReader'.
		bufferedReader = new BufferedReader(pipedReader);

		new ThreadRead().start();

		String[] strs = new String[] { "Hello ", "There", "\n", "I am ", "Tran" };

		// Ghi dữ liệu vào 'pipedWriter'.
		for (String str : strs) {
			pipedWriter.write(str);
			Thread.sleep(500);
		}
		pipedWriter.close();
	}

	// Một Thread đọc dữ liệu xuất hiện trên 'bufferedReader' ('pipedReader').
	class ThreadRead extends Thread {

		@Override
		public void run() {
			try {
				String line = null;
				while ((line = bufferedReader.readLine()) != null) {
					System.out.println(line);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				closeQuietly(bufferedReader);
			}
		}
	}

	private void closeQuietly(Reader reader) {
		if (reader != null) {
			try {
				reader.close();
			} catch (IOException e) {
			}
		}
	}

}

16. Class java.io.PipedWriter

Các hướng dẫn Java IO

Show More