Java: Scanning và Formatting

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Lập trình I/O thường liên quan đến việc dịch thành và dịch từ những dữ liệu được định dạng gần với những gì mà con người thích làm việc. Để giúp ta về những điều này thì nền tảng Java cung cấp hai API là API scanner dùng để nhập liệu với các bit dữ liệu, và API formatting dùng để hiển thị dữ liệu theo định dạng mà con người có thể đọc được.

Scanning

Đối tượng của lớp Scanner rất hữu ích cho việc định dạng đầu vào có định dạng vào thẻ và các dịch thẻ cá nhân theo kiểu dữ liệu của họ.

Breaking Input vào Tokens

Theo mặc định, một máy quét sử dụng ký tự dấu cách trắng để phân tách các token (ký tự trắng bao gồm khoảng trống (blank), các tab, và các ký tự kết thúc của dòng). Để xem cách quét hoạt động, chúng ta hãy xem lớp ScanXan sau đây, một chương trình đọc từng từ một từ tập tin xanadu.txt và in chúng ra từng dòng một.

package demo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

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

    Scanner s = null;

    try {
      s = new Scanner(new BufferedReader(new FileReader(
        "E:\\Courses\\Java\\Solutions\\src\\demo\\input.txt")));

      while (s.hasNext()) {
        System.out.println(s.next());
      }
    } finally {
      if (s != null) {
        s.close();
      }
    }
  }
}

Chú ý rằng ScanXan gọi Scanner gần phương thức khi nó được thực hiện với các đối tượng của lớp Scanner. Mặc dù một đối tượng Scanner không phải là một stream, nhưng bạn vẫn cần phải đóng lại để chỉ ra rằng bạn đang thực hiện với dòng cơ bản của nó.

Output của lớp ScanXan sẽ như sau:

In
Xanadu
did
Kubla
Khan
A
stately
pleasure-dome
...

Để sử dụng một tách thẻ khác nhau, gọi useDelimiter () , chỉ định một biểu thức chính quy. Ví dụ, giả sử bạn muốn tách token để có một dấu phẩy, tùy theo sau bởi khoảng trắng. Bạn sẽ gọi,

s.useDelimiter ( ",\\s*");

Dịch các token riêng lẻ

Các ScanXan dụ đối xử với tất cả những mã đầu vào đơn giản chuỗi giá trị. Scanner cũng hỗ trợ thẻ cho tất cả các loại nguyên thủy của ngôn ngữ Java (trừ char ), cũng như BigInteger và BigDecimal . Ngoài ra, giá trị số có thể sử dụng hàng ngàn dải phân cách. Như vậy, trong một Mỹ bản địa, Scanner đọc một cách chính xác các chuỗi "32.767" là đại diện cho một giá trị số nguyên.

Chúng tôi có đề cập đến các địa phương, vì hàng ngàn dải phân cách và các ký hiệu thập phân là miền địa phương cụ thể. Vì vậy, ví dụ sau đây sẽ không hoạt động chính xác trong tất cả các miền địa phương nếu chúng ta không xác định rằng các máy quét nên sử dụng Mỹ bản địa. Đó không phải là một cái gì đó bạn thường phải lo lắng về, vì dữ liệu đầu vào của bạn thường xuất phát từ các nguồn mà sử dụng các miền địa phương giống như bạn làm. Nhưng ví dụ này là một phần của Hướng dẫn Java và được phân phối trên toàn thế giới.

Giả sử ta có tập tin "usnumbers.txt" chứa nội dung sau:

8.5
32,767
3.14159
1,000,000.1

Ví dụ ScanSum sau đây đọc các giá trị double từ tập tin "usnumbers.txt" và cộng chúng vào biến sum:

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Scanner;
import java.util.Locale; 

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

        Scanner s = null;
        double sum = 0; 

        try {
            s = new Scanner(new BufferedReader (new FileReader ("usnumbers.txt")));
            s.useLocale(Locale.US); 

            while (s.hasNext() ) {
                if (s.hasNextDouble()) {
                    sum + = s.nextDouble();
                } else {
                    s.next ();
                }   
            }
        } finally {
            s.close();
        } 

        System.out.println(sum);
    }
}

Chuỗi đầu ra là "1032778,74159". Thời gian sẽ là một ký tự khác nhau ở một số vùng do System.out là một đối tượng PrintStream, và lớp này không cung cấp một cách để ghi đè lên các miền địa phương mặc định. Ta có thể ghi đè lên các vị trí cho toàn bộ chương trình, hoặc chỉ có thể sử dụng định dạng, như mô tả trong phần tiếp theo sau đây.

Formatting

Các đối tượng Stream thực hiện việc định dạng là những thể hiện của lớp PrintWriter, là một lớp character stream, hoặc PrintStream, là một lớp byte stream. 


Lưu ý:  Chỉ có các đối tượng của PrintStream là System.out và System.err là bạn cần đến. Khi bạn cần tạo ra một output stream có định dạng thì ta cần PrintWriter, không cần PrintStream.

Giống như tất cả các đối tượng byte stream và character stream, các đối tượng của PrintStream và PrintWriter sẽ thực thi một tập các phương thức write chuẩn cho các byte và ký tự đầu ra đơn giản. Ngoài ra, cả PrintStream và PrintWriter đều thực hiện một tập các phương thức để chuyển đổi dữ liệu nội bộ thành output có định dạng. Có hai mức định dạng như sau:

  • print và println định dạng các giá trị riêng biệt thành các chuẩn khác nhau.
  • format định dạng hầu hết các giá trị dựa trên một chuỗi định dạng, với nhiều tùy chọn để định dạng chính xác.

Các phương thức print và println

Lời gọi đến phương thức print hoặc println sẽ xuất ra một giá trị duy nhất sau khi chuyển đổi các giá trị sử dụng phương thức toString một cách thích hợp. Ta có thể thấy điều này trong ví dụ về lớp Root dưới đây:

public class Root {
    public static void main (String [] args) {
        int i = 2;
        double r = Math.sqrt (i);

        System.out.print("The square root of ");
        System.out.print( i);
        System.out.print( " is ");
        System.out.print(r);
        System.out.println("."); 

        i = 5;
        r = Math.sqrt(i);
        System.out .println("The square root of " + i + " is " + r + ".");
    }
}

Và đây là đầu ra của lớp Root:

The square root of 2 is 1,4142135623730951.
The square root of 5 is 2,23606797749979.

Các biến i và r được định dạng hai lần: lần thứ nhất là sử dụng mã lệnh trong quá tải phương thức print, lần thứ hai là chuyển đổi mã lệnh được sinh ra tự động bởi trình biên dịch Java sử dụng toString. Ta có thể định dạng bất kỳ giá trị nào theo cách này, nhưng không có nhiều quyền kiểm soát kết quả.

Phương thức format

Phương thức này định dạng nhiều đối số dựa trên một chuỗi định dạng. Chuỗi định dạng bao gồm văn bản tĩnh được nhúng với các bộ đặc tả định dạng; nếu không có các bộ đặc tả định dạng thì chuỗi là đầu ra sẽ không thay đổi.

Chuỗi định dạng hỗ trợ nhiều tính năng, bài viết này chỉ giới thiệu một số tính năng cơ bản, xin xem thêm tại ĐÂY.

Ví dụ Root2 sau đây định dạng hai giá trị có kiểu khác nhau trong một lời gọi đến hàm format:

public class Root2 {
    public static void main(String [] args) {
        int i = 2;
        double r = Math.sqrt(i); 

        System.out.format("Căn bậc hai của %d là %f%n." , i, r);
    }
}

Dưới đây là kết quả:

Căn bậc hai của 2 là 1,414214.

Tất cả các bộ đặc tả định dạng đều bắt đầu bằng ký tự % và kết thúc bằng một 1 hoặc 2 ký tự chuyển đổi để xác định các loại định dạng đầu ra được tạo ra. Ví dụ trên có 3 định dạng được sử dụng là

  • d định dạng một giá trị số nguyên.
  • e định dạng một giá trị dấu chấm động.
  • n xuất ra một ký tự xuống dòng.

Dưới đây là một số dạng chuyển đổi khác:

  • x định dạng một số nguyên như là một giá trị dạng thập lục phân (hệ 16).
  • s định dạng cho một chuỗi.
  • tB định dạng một số nguyên như là một tên tháng địa phương cụ thể.

Có nhiều chuyển đổi khác.


Chú ý: Ngoại trừ %% và %n, tất cả các bộ đặc tả định dạng phải phù hợp với một đối số, nếu không thì một ngoại lệ sẽ được ném ra.

Trong ngôn ngữ lập trình Java, \n luôn tạo ra ký tự line-feed (\u000A ). \n chỉ không được sử dụng đến trừ khi bạn muốn có một ký tự line-feed. Để có được những dòng phân cách chính xác cho các nền tảng cục bộ thì ta sử dụng %n .


Ngoài việc chuyển đổi, một bộ đặc tả định dạng có thể chứa một số thành phần bổ sung để tùy chỉnh đầu ra có định dạng. Dưới đây là một ví dụ:

public class Format{
    public static void main (String [] args) {
        System.out.format( "%f, %1$+020.10f %n", Math.PI);
    }
}

Dưới đây là kết quả:

3.141593, +00000003.1415926536

Hình dưới đây mô tả cho định dạng ở ví dụ trên:

Các yếu tố của một đặc tả định dạng
Các thành phần của một Bộ đặc tả định dạng.

Các thành phần này phải xuất hiện theo thứ tự hiển thị. Các thành phần từ phải sang trái là:

- Precision : Đối với các giá trị dấu chấm động, đây là độ chính xác toán học của các giá trị được định dạng. Đối với định dạng s (dành cho chuỗi) thì đây là độ rộng tối đa của giá trị được định dạng; giá trị sẽ được cắt bớt nếu cần thiết.

- Width : Độ rộng tối thiểu của giá trị được định dạng; giá trị được đệm nếu cần thiết. Theo mặc định, giá trị được đệm thêm các dấu cách vào bên trái.

- Flags : Xác định các tùy chọn định dạng bổ sung. Trong ví dụ về lớp Format ở trên thì cờ + xác định rằng số lượng luôn phải được định dạng với một dấu hiệu, và cờ 0 xác định rằng 0 là ký tự đệm thêm. Các cờ khác bao gồm dấu trừ - (đệm sang bên phải) và dấu phẩy , (định dạng cho số để phân cách các hàng phần nghìn). Lưu ý rằng một số cờ không thể được sử dụng với một số cờ khác hoặc với các chuyển đổi nhất định.

- Argument Index cho phép tương thích một cách rõ ràng một đối số được chỉ định. Bạn cũng có thể sử dụng < để tương thích với một số đối số giống như bộ đặc tả trước. Như vậy, ví dụ về định dạng ở trên có thể viết lại thành: System.out.format("%f, %<+020.10f %n", Math.PI);

» Tiếp: I/O từ Command Line
« Trước: Buffered Stream
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!