Hướng dẫn sử dụng luồng vào ra ký tự trong Java
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Giới thiệu

Trong tài liệu hướng dẫn trước tôi đã giới thiệu về luồng vào ra nhị phân, 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ự, 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, mỗi một lần đọc ra một byte (Tương đương với 8 bit)
Trong khi đó luồng ký tự mỗi lần đọc ra một ký tự, tùy thuộc vào mã hóa (UTF-8, UTF-16,..) mà lần đọc đó là  1, 2 hay 3 byte. Chúng ta hãy xem một hình ảnh minh họa sau:

UTF-16:

Đây là một dòng text tiếng Nhật, nếu nó được lưu tại File với mã hóa UTF-16, 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 đây thông báo đó là bắt đầu một chuỗi mã hóa UTF-16.
  • Các ký tự tiếp theo đều được mã hóa bởi 2 byte.
    • Ví dụ như chữ J được mã hóa bởi 2 byte (0 và 74)
    • Chữ P 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 text tiếng Nhật nói trên nếu được ghi xuống với mã hóa UTF-8 lúc đó sẽ khác đi, bạn có thể thấy các byte được lưu xuống ổ cứng:
  • Với các chữ latin thông thường, nó sẽ chỉ dùng 1 byte để lưu.
    • Ví dụ chữ J, dùng 1 byte để lưu (74)
    • Chữ P dùng 1 byte để lưu (80)
  • Các ký tự khác nó có thể dùng 2 byte hoặc 3 byte để lưu.
  • Nguyên tắc đọc ra, nó có một bảng ** các ký tự chuẩn UTF8
    • Đọc byte đầu tiên, nếu nó <= 127, thì đó là 1 ký tự ASII.
    • Ngược lại > 127, thì nó cần đọc tiếp byte thứ 2, và quyết định xem 2 byte đó là 1 ký tự được không và đó là ký tự gì.
    • Nếu bước trên không được 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 một ký tự.

Như vậy khi bạn lưu một tài liệu với mã hóa 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ự

Đây là hình minh họa các class của luồng vào ra ký tự:

4- Class java.io.Reader

Reader là class trìu tượng, các luồng đọc ký tự đều mở rộng từ class này.
Tạo một file test_reader.txt để bắt đầu với ví dụ 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 {
       // Luồng ký tự, đọc một file.
       // FileReader đọc ký tự theo mã hóa mặc định của Java trên máy chạy code này.
       
       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ề 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, nó được gán lên một mảng tạm. 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.
        // Đọc theo mã hóa mặc định của hệ thống Java.
        // Bạn có thể chủ động sét đặt mã hóa mặc định cho hệ thống.
        Reader r = new FileReader("test_reader.txt");
        // Tạo một mảng để mỗi lần đọc từ luồng, chúng được gán lên trên đó.
        char[] temp = new char[10];
        int i = -1;

        // Method read(char[]):
        // Đọc nhiều ký tự một lần, và gán lên các phần tử cho mảng.
        // Trả về số ký tự đọc được.
        // Khi không còn phần tử trên luồng, trả về -1
        while ((i = r.read(temp)) != -1) {
            String s = new String(temp, 0, i);
            System.out.println(s);
        }
        r.close();

    }

}

5- Class java.io.Writer

Class Writer là một class trìu tượng, tất cả các luồng ghi ký tự đều mở rộng từ class này.
  • 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 luồng ghi ký tự vào file
       // Mã hóa là mã hóa mặc định của Hệ thống Java.
       // Bạn có thể thay đổi mã hóa mặc định của hệ thống.
       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.
       for (int i = 0; i < chars.length; i++) {
           char ch = chars[i];
           // Ép kiểu về int
           int j = (int) ch;
           // Ghi vào luồng.
           w.write(j);
       }
       // Đóng luồng, việc ghi hoàn thành.
       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 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 luồng ký tự ghi vào file.
       Writer w = new FileWriter("C:/test/test_writer2.txt");

       // Tao mot mang ky tu , ta se ghi cac ky tu nay vao file
       char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'r',
               'i', 't', 'e', 'r' };

       // Ghi các ký tự trong mảng vào luồng.
       w.write(chars);
       // Thông thường Java sử dụng bộ đệm để lưu dữ liệu
       // khi đầy bộ đệm nó mới đẩy xuống file
       // Bạn có thể chủ động đẩy dữ liệu xuống file.
       w.flush();
       
       // Ghi ký tự xuống dòng vào luồng.
       w.write('\n');

       String s = "FileWriter";
       // Ghi một chuỗi vào luồng.
       w.write(s );
       // Đóng luồng ký tự.
       // Nó sẽ đẩy các dữ liệu trên bộ đệm xuống.
       // Việc ghi ra file thành công
       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. Và bạn muốn chuyển nó thành luồng ký tự?
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 vào luồng với mã hóa chỉ định rõ.
Tạo một file test_utf8.txt
  • test_utf8.txt
JP日本-八洲
Khi Save, eclipse sẽ hỏi bạn Save dướ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 luồng nhị phân, đọc file.
       InputStream in = new FileInputStream("test_utf8.txt");

       // Tạo một luồng ký tự từ luồng nhị phân.
       // Mã hóa đọc 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 về 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 luồng nhị phân ghi ra file.
       OutputStream out = new FileOutputStream("C:/test/test_write_utf8.txt");

       // Tạo một luồng ghi ký tự.
       // Mã hóa 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 file, đọc lần lượt từng dòng. BufferedReader là sự lựa chọn tốt.
// Là một class con trực tiếp của Reader .
// Cấu tử thông dụng
public BufferedReader(Reader in);

// Vậy là khởi tạo đối tượng BufferedBuffer bằng cách bao quanh một đối tượng Reader.
// Method tiện lợi có được từ BufferedReader :
// Đọc một dòng dữ liệu
public String readLine();

// Đoạn code ví dụ :
// Tạo một luồng đầu vào bằng cách đọc file có sẵn ..

Reader r=new FileReader("C:/test.txt");
BufferedReader br=new BufferedReader(r);

// Đoạn code ví dụ:
InputStream in = new FileInputStream("C:/test.txt");
Reader r = new InputStreamReader(in, "UTF-8");
BufferReader br = new BufferedReader(r);
Nếu bạn muốn đọc từng dòng dữ liệu của một file text. BufferedReader là một sự lựa chọn tốt.
  • 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 dữ liệu
        // Khi đọc 1 dòng trả về null nghĩa là kết thúc luồng.
        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à class con trực tiếp của Writer.
// Cấu tử
// Tạo một đối tượng BufferedWriter bao lấy một đối tượng Writer khác.
public BufferedWriter(Writer out);


// Method thêm vào so với class cha Writer
public String newLine();//Tương ứng với write('\n');

// Đoạn code ví dụ :
// Tạo một luồng đầu ra để ghi vào file ...
Writer w=new FileWriter("C:/jhelp/test_bufferedWriter.txt");
// Tạo một đối tượng BufferedWriter bao lấy luồng ghi vào file nói trên
BufferedWriter bw=new BufferedWriter(w);
bw.write("Hello..");
//In ký tự xuống dòng '\n' bw.write("Hello boys..");
bw.newLine();

9- Class java.io.FilterReader

FilterReader là một luồng đọc ký tự, mà nó đọc một cách có chọn lựa các ký tự yêu cầu, chẳng hạn bạn muốn đọc một tài liệu text có các thẻ HTML, và loại bỏ các ký tự trong các thẻ. Bạn cần viết một class con của FilterReader và sau đó sử dụng class con đó, bạn không thể sử dụng trực tiếp FilterReader vì nó là một class là class trìu tượng (abstract class).
Ví dụ minh họa, một luồng đọc ký tự lọc, đọ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 {

   // Sử dụng để nhớ khi đang duyệt tới ký tự trong 1 thẻ.
   boolean intag = false;

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


   /**
    * Chúng ta ghi đè method này. Nguyên tắc sẽ làm là đọc luồng và bỏ qua các
    * ký tự trong các thẻ HTML.
    */
   @Override
   public int read(char[] buf, int from, int len) throws IOException {
       // Số lượng ký tự đã đọc
       int charCount = 0;
     
       while (charCount == 0) {
           // Đọc tối đa 'len' ký tự trong luồng và gán vào 'buf' từ vị trí
           // 'from' trở đi
           charCount = super.read(buf, from, len);
           if (charCount == -1) {
               // Kết thúc luồng đọc.
               return -1;
           }
           // Vị trí phần tử trên 'buf' được gán giá trị.
           int last = from;
           // Duyệt trên các ký tự vừa đọc được
           // Và lọc bỏ các ký tự trong thẻ HTML
           for (int i = from; i < from + charCount; i++) {
               // Nếu không trong thẻ HTML
               if (!intag) {
                   if (buf[i] == '<') {
                       // Bắt đầu vào các ký tự thẻ
                       intag = true;
                   } else {
                       // Không trong ký tự thẻ
                       // Gán ký tự vào vị trí 'last'
                       // Đồng thời tăng giá trị 'last'
                       buf[last++] = buf[i];
                   }
               } else if (buf[i] == '>') {
                   // Thoát ra khỏi thẻ
                   intag = false;
               }
           }
           // Số ký không trong thẻ vừa đọc được.
           // Nếu số ký tự này khác 0 nó sẽ thoát ra khỏi vòng while.
           charCount = last - from;
       }
       // Trả về số ký tự vừa đọc được này.
       return charCount;
   }

   /**
    * Cũng cần phải ghi đè lại method này, method này gọi tới method trên, để
    * đảm bảo rằng nó đọc ký tự tiếp theo trên luồng, nhưng ký tự đó phải nằm
    * ngoài thẻ.
    */
   @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 {
       // Một đối tượng Reader từ StringReader để đọc một đoạn text
       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 luồng ghi ký tự, mà nó ghi một cách có chọn lựa các ký tự yêu cầu. Thông thường bạn viết một class con của FilterWriter và có thể ghi đè mọi method của nó, và ghi vào luồng theo cách mà bạn muốn.
Ví dụ sau đây minh họa dữ liệu bị biến đổi đi khi ghi vào luồng, có thể coi đó là cách mật mã hóa đơn giản.
  • 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 method để thực hiện lọc
    @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 trả lại cho dòng đầu vào. Điều này cho phép bạn nhìn về phía trước trong các luồng đầu vào. Dưới đây là hai cấu trúc tử của nó:
public PushbackReader(Reader inputStream)
public PushbackReader(Reader inputStream, int bufSize)
Một số method thêm
// Trả lại ký tự về luồng, đồng nghĩa với việc lùi con trỏ lại một ký tự.
public void unread(int c) throws IOException
  • 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.");
               }
               // Nếu ký tự tiếp theo khác '='
               else {
                   System.out.print("<-");
                   // Trả lại ký tự này về luồng.
                   // Nghĩa là lùi con trỏ trở lại 1 đơn vị
                   f.unread(c);
               }
               break;
           default:
               System.out.print((char) c);
               break;
           }
       }
   }

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

12- Class java.io.PrintWriter

// Cấu tử :
// PrintWriter là class con trực tiếp của Writer .
// Nó có thể bao bọc một luồng đầu ra ký tự (Writer) hoặc luồng đầu ra nhị phân (OutputStream) , ..
public PrintWriter(Writer out) //Bao bọc một luồng ký tự
public PrintWriter(Writer out,boolean autoFlush)
public PrintWriter(OutputStream out) //Bao bọc một luồng nhị phân
public PrintWriter(OutputStream out,boolean autoFlush)
public PrintWriter(String fileName)
...

// Một số 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ì đó
           // Ngoại lệ khi chia cho 0, khối catch sẽ được chạy.
           int i = 10 / 0;
       } catch (Exception e) {
           System.out.println("EXCEPTION ....");
           try {
               File dir = new File("C:/test");
               // Tạo thư mục C:/test.
               dir.mkdirs();
               // Tạo luồng ghi vào file.
               Writer w = new FileWriter("C:/test/stackTrace.txt");
               // Tạo đối tượng PrintWriter bao lấy Writer w
               // Như vậy dữ liệu ghi vào PrintWriter sẽ 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ì đó
           // Ngoại lệ khi chia cho 0, khối catch sẽ được chạy.
           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 StringWriter
               // 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ố method thêm:
// Ghi dữ liệu 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 luồng '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 từ CharArrayWriter vào FileWriter f1
       out.writeTo(f1);

       FileWriter f2 = new FileWriter(new File("C:/test/b.txt"));
       // Ghi dữ liệu từ CharArrayWriter vào FileWriter 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 luồng
       // Không có ngoại lệ ném ra, nhưng writeTo(..) ko có tác dụng.
       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 sử lý đa luồng (Thread ...) .
  • TODO

16- Class java.io.PipedWriter