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

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).

1. Đọ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.maixuanviet.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: maixuanviet.com
Hello maixuanviet.com
Enter your name: exit
Finished!

2. Sử dụng lớp FileReader và FileWriter

2.1. 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("maixuanviet.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

2.2. Ví dụ đọc file sử dụng lớp FileReader

package com.maixuanviet.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:

maixuanviet.com

3. Chuyển một luồng nhị phân thành luồng ký tự

3.1. Ví dụ chuyển từ OutputStream sang Writer

package com.maixuanviet.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();
    }
}

3.2. Ví dụ chuyển từ InputStream sang Reader

package com.maixuanviet.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();
    }
}

4. 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ẻ.

4.1. Ví dụ ghi file sử dụng lớp FilterOutputStream

package com.maixuanviet.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 = "maixuanviet.com";
            byte b[] = s.getBytes();
            filter.write(b);
            filter.flush();
            System.out.println("Success...");
        } finally {
            filter.close();
            file.close();
        }
    }
}

4.2. Ví dụ đọc file sử dụng lớp FilterInputStream

package com.maixuanviet.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();
    }
}

5. 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.maixuanviet.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("maixuanviet.com");
        buffer.close();
        System.out.println("Success...");
    }
}

Ví dụ đọc file sử dụng BufferedReader

package com.maixuanviet.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();
    }
}

6. 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.

6.1. Ví dụ đọc một ký tự sử dụng lớp Java CharArrayReader

package com.maixuanviet.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

7. Ví dụ ghi một dữ liệu chung ra nhiều file

package com.maixuanviet.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("maixuanviet.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

8. 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.

8.1. Ví dụ StringWriter sử dụng BufferedReader để đọc file từ luồng (stream)

package com.maixuanviet.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();
    }
}

8.2. Ví dụ StringReader đọc chuỗi như luồng ký tự (stream)

package com.maixuanviet.characterstream;
 
import java.io.StringReader;
 
public class StringReaderExample {
    public static void main(String[] args) throws Exception {
        String srg = "maixuanviet.com";
        StringReader reader = new StringReader(srg);
        int k = 0;
        while ((k = reader.read()) != -1) {
            System.out.print((char) k);
        }
    }
}

9. 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.maixuanviet.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("maixuanviet.com");
        pout.println("Java I/O Tutorials");
        pout.close();
        fout.close();
        System.out.println("Success...");
    }
}

10. 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.maixuanviet.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.

11. 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.maixuanviet.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

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.maixuanviet.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

13. 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).

13.1. Ví dụ sử dụng PipedReader

package com.maixuanviet.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

Related posts:

Split a String in Java
Hướng dẫn Java Design Pattern – Transfer Object
How to Add a Single Element to a Stream
Java Program to Search Number Using Divide and Conquer with the Aid of Fibonacci Numbers
Dynamic Proxies in Java
Java Program to Implement Queue using Two Stacks
Java Copy Constructor
Guide to the Java Queue Interface
Java Program to Permute All Letters of an Input String
Java Program to Search for an Element in a Binary Search Tree
Java Program to Generate All Subsets of a Given Set in the Gray Code Order
Spring Boot - OAuth2 with JWT
Get and Post Lists of Objects with RestTemplate
Java Program to Describe the Representation of Graph using Adjacency List
A Guide to Spring Boot Admin
Java Program to Implement Bit Array
Assert an Exception is Thrown in JUnit 4 and 5
Java Program to Check Whether a Directed Graph Contains a Eulerian Path
Query Entities by Dates and Times with Spring Data JPA
Sao chép các phần tử của một mảng sang mảng khác như thế nào?
Spring Boot - Batch Service
Java Program to Implement Hash Tables Chaining with List Heads
Java Program to Implement CopyOnWriteArrayList API
Vector trong Java
Java Program to Convert a Decimal Number to Binary Number using Stacks
Java Program to Implement IdentityHashMap API
How to Read a Large File Efficiently with Java
Java Program to Find the Minimum Element of a Rotated Sorted Array using Binary Search approach
Guide to java.util.concurrent.Locks
Introduction to Spring Security Expressions
Java Program to Implement Rolling Hash
Remove HTML tags from a file to extract only the TEXT