Java: Data Stream
Data stream hỗ trợ các hoạt động I/O nhị phân đối với các giá trị có kiểu dữ liệu nguyên thủy và các giá trị kiểu chuỗi (String). Tất cả các data stream đều thực thi một trong hai giao diện là DataInput
hoặc DataOutput
. Bài viết này tập trung vào việc thực thi các giao diện DataInputStream
và DataOutputStream
.
Ví dụ có tên DataStreams
dưới đây trình bày data stream bằng cách ghi ra một tập hợp các bản ghi dữ liệu, và sau đó đọc chúng.
package solutions; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.EOFException; public class DataStreams { static final String dataFile = "invoicedata"; static final double[] prices = {19.99, 9.99, 15.99, 3.99, 4.99}; static final int[] units = {12, 8, 13, 29, 50}; static final String[] names = {"Java Core", "Java OOP", "Java App Desktop", "Java App Mobile", "Java Web"}; public static void main(String[] args) throws IOException { DataOutputStream out = null; try { out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); for (int i = 0; i < prices.length; i++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(names[i]); } } finally { out.close(); } DataInputStream in = null; double total = 0.0; try { in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String name; try { while (true) { price = in.readDouble(); unit = in.readInt(); name = in.readUTF(); System.out.format("You ordered %d units of %s at $%.2f%n", unit, name, price); total += unit * price; } } catch (EOFException e) { } System.out.format("For a TOTAL of: $%.2f%n", total); } finally { in.close(); } } }
Mỗi bản ghi bao gồm ba giá trị liên quan đến một sản phẩm trên một hóa đơn, như thể hiện trong bảng sau:
Thứ tự | Loại dữ liệu | Mô tả dữ liệu | Phương thức output | Phương thức input | Giá trị mẫu |
---|---|---|---|---|---|
1 | double |
Giá sản phẩm | DataOutputStream.writeDouble |
DataInputStream.readDouble |
19.99 |
2 | int |
đơn vị tính | DataOutputStream.writeInt |
DataInputStream.readInt |
12 |
3 | String |
Mô tả sản phẩm | DataOutputStream.writeUTF |
DataInputStream.readUTF |
"Java T-Shirt" |
Hãy kiểm tra mã số rất quan trọng trong DataStreams
. Đầu tiên, chương trình định nghĩa một số hằng số chứa tên của các tập tin dữ liệu và dữ liệu sẽ được ghi vào nó:
static final String dataFile = "invoicedata"; static final double[] prices = {19.99, 9.99, 15.99, 3.99, 4.99}; static final int[] units = {12, 8, 13, 29, 50}; static final String[] names = {"Java Core", "Java OOP", "Java App Desktop", "Java App Mobile", "Java Web"};
Sau đó DataStreams
mở một output stream. Vì mỗi DataOutputStream
chỉ có thể được tạo ra như là một wrapper cho một đối tượng byte stream hiện có, nên DataStreams
sẽ cung cấp một tập tin được đệm để output byte stream.
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); for (int i = 0; i < prices.length; i++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(names[i]); }
Phương thức writeUTF
sẽ ghi ra các giá trị String theo định dạng UTF-8 đã sửa đổi, đây là một mã ký tự chiều rộng thay đổi mà chỉ cần một byte duy nhất cho các ký tự phương Tây thông thường.
Bây giờ DataStreams
đọc dữ liệu trở lại. Đầu tiên nó phải cung cấp một input stream và các biến để lưu dữ liệu đầu vào. Giống như DataOutputStream
, DataInputStream
phải được xây dựng như là một wrapper cho một byte stream.
in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String name;
Bây giờ DataStreams
có thể đọc mỗi bản ghi trong stream, báo cáo về dữ liệu mà nó gặp.
try { while (true) { price = in.readDouble(); unit = in.readInt(); name = in.readUTF(); System.out.format("You ordered %d units of %s at $%.2f%n", unit, name, price); total += unit * price; } } catch (EOFException e) { }
Chú ý rằng DataStreams
phát hiện một điều kiện cuối tập tin bằng cách bắt ngoại lệ EOFException
thay vì kiểm tra một giá trị trả về không hợp lệ. Tất cả các thực thi của các phương thức của DataInput đều sử dụng EOFException thay vì các giá trị trả về.
Cũng lưu ý rằng mỗi phương thức write
cụ thể trong DataStreams
được kết hợp chính xác với một phương thức read
tương ứng. Vậy nên, lập trình viên cần đảm bảo rằng các kiểu output và input phải tương thích với nhau theo cách thức sau: Các input stream bao gồm các dữ liệu nhị phân đơn giản, không cần chỉ ra các loại giá trị riêng, hoặc vị trí bắt đầu của chúng trong stream.
Ở đây, DataStreams
sử dụng một kỹ thuật lập trình rất tồi là: nó sử dụng các số dấu chấm động (số thực) để thể hiện các giá trị tiền tệ, mà kiểu dấu chấm động không thích hợp cho giá trị chính xác. Nó đặc biệt tồi khi áp dụng cho các phân số thập phân, bởi vì những ước số chung (như là 0.1
chẳng hạn) không thể hiện được ở dạng nhị phân.
Các kiểu chuẩn áp dụng cho các giá trị tiền tệ là java.math.BigDecimal
. Tuy nhiên, BigDecimal
là một kiểu đối tượng, vì vậy nó sẽ không làm việc với data stream. Tuy nhiên, BigDecimal
sẽ làm việc với object stream, được trình bày ở bài viết tiếp theo.