CodeGym /Blog Java /Ngẫu nhiên /Đọc từ bàn phím: "độc giả"

Đọc từ bàn phím: "độc giả"

Xuất bản trong nhóm
CHÀO! Các bài học và nhiệm vụ ở Cấp độ 3 đã dạy bạn cách hiển thị nội dung trên bảng điều khiển và di chuyển theo hướng khác, cách đọc dữ liệu từ bàn phím.
Đọc từ bàn phím: "độc giả" - 1
Bạn thậm chí đã học cách sử dụng cấu trúc phức tạp sau đây để thực hiện điều này:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Nhưng có một câu hỏi mà chúng tôi chưa trả lời được.

Làm thế nào trên thế giới này làm việc này?

Trong thực tế, các chương trình hiếm khi hoàn toàn độc lập. Chúng giao tiếp với các chương trình, hệ thống, Internet, v.v. Bằng cách "giao tiếp", ý chúng tôi chủ yếu là "trao đổi dữ liệu". Tức là chúng nhận một số dữ liệu bên ngoài và cũng gửi dữ liệu chương trình bên trong đến một nơi nào đó. Ví dụ về các chương trình trao đổi dữ liệu có rất nhiều trong cuộc sống hàng ngày. Ví dụ: nhiều trang web cho phép bạn đăng nhập bằng tài khoản Facebook hoặc Twitter thay vì đăng ký. Trong tình huống này, hai chương trình (ví dụ: Twitter và trang web bạn đang đăng nhập) trao đổi dữ liệu cần thiết. Kết quả cuối cùng là bạn đã đăng nhập thành công. Từ "stream"được sử dụng để mô tả quá trình trao đổi dữ liệu. Tên này đến từ đâu? Theo kinh nghiệm của bạn, một "luồng" có thể được liên kết nhiều hơn với các dòng sông hơn là với chương trình. Đó không phải là ngẫu nhiên :) Về bản chất, một luồng là một phần dữ liệu chuyển động. Nói cách khác, trong lập trình, nước không chảy - mà là dữ liệu ở dạng byte và ký tự. Chúng ta có thể nhận các bit dữ liệu từ một luồng dữ liệu và sau đó sử dụng chúng. Một lần nữa, chúng ta sẽ sử dụng phép loại suy nước/dòng chảy: bạn có thể múc nước từ sông để nấu súp, dập lửa hoặc tưới hoa. Luồng cho phép bạn làm việc với bất kỳ nguồn dữ liệu nào: cho dù là Internet, hệ thống tệp trên máy tính của bạn hay thứ gì khác — không có gì khác biệt. Luồng là một công cụ phổ quát. Chúng cho phép chương trình nhận dữ liệu từ bất kỳ đâu (luồng đầu vào) và gửi dữ liệu đến bất kỳ đâu (luồng đầu ra). Nhiệm vụ của họ là như nhau: lấy dữ liệu từ một nơi và gửi nó đến một nơi khác. Có hai loại luồng:
  1. Luồng đầu vào được sử dụng để nhận dữ liệu
  2. Luồng đầu ra là để gửi dữ liệu.
Trong Java, các luồng này được triển khai bởi các lớp InputStreamOutputStream. Nhưng các luồng có thể được phân loại theo cách khác. Ngoài luồng đầu vào và luồng đầu ra, chúng ta cũng nói về luồng byteluồng ký tự . Ý nghĩa ở đây phải đủ rõ ràng: luồng byte gửi thông tin dưới dạng một tập hợp các byte, trong khi luồng ký tự gửi nó dưới dạng một tập hợp các ký tự. Trong bài học này, chúng ta sẽ tập trung vào các luồng đầu vào. Tôi sẽ đặt một liên kết với thông tin về luồng đầu ra ở cuối bài học. Bạn có thể tự đọc nó :) Bây giờ hãy xem mã này:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Khi trải qua các bài học, bạn có nghĩ rằng dòng này khá đáng sợ không? :) Điều đó sẽ không xảy ra khi chúng tôi đã khám phá cách thức hoạt động của nó. Hãy đặt mọi thứ đúng. Chúng ta sẽ bắt đầu ở cuối. System.inlà một InputStreamđối tượng, một thể hiện của lớp mà chúng ta đã nói ở phần đầu. Nó là luồng đầu vào được liên kết với thiết bị đầu vào của hệ thống (bàn phím). Nhân tiện, bạn đang gián tiếp làm quen với luồng này. Rốt cuộc, bạn thường xuyên sử dụng "đồng nghiệp" của nó — System.out! System.outlà luồng đầu ra của hệ thống . Nó được sử dụng để xuất dữ liệu ra bảng điều khiển thông qua phương thức yêu thích của bạn System.out.println()mà bạn thường xuyên sử dụng :) System.outlà một luồng để gửi dữ liệu đến bảng điều khiển, trong khiSystem.inlà để lấy dữ liệu từ bàn phím. Tất cả đều đơn giản :) Hơn nữa, chúng ta có thể đọc dữ liệu từ bàn phím mà không cần cấu trúc khổng lồ này. Chúng ta có thể viết đơn giản: System.in.read();

public class Main {

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

       while (true) {
           int x = System.in.read();
           System.out.println(x);
       }
   }
}
Lớp InputStream(hãy nhớ, System.inlà một InputStreamđối tượng) có một read()phương thức cho phép bạn đọc dữ liệu. Có một vấn đề: nó đọc byte chứ không phải ký tự . Thật nhàm chán khi chỉ sử dụng các chữ cái tiếng Anh, vì vậy hãy thử đọc ký tự tiếng Trung "魚" từ bàn phím (chỉ cần sao chép ký tự này từ đây và dán vào bảng điều khiển bằng cách sử dụng ctrl + v trên PC hoặc Command + v trên Mac). Nhân vật này có nghĩa là 'một con cá'. Đầu ra bảng điều khiển: 233 173 154 10 Ký hiệu này và nhiều ký hiệu khác của Trung Quốc chiếm 3 byte trong bộ nhớ của máy tính (không giống như các chữ cái Latinh, chỉ chiếm 1 byte). Trong trường hợp này, 4 byte được đọc từ luồng: ba byte đầu tiên biểu thị ký tự "魚" và byte khác biểu thị một dòng mới (Enter). Theo đó, System.inở dạng không trang trí không phải là một lựa chọn cho chúng tôi. Con người (với một số ngoại lệ hiếm hoi!) không biết cách đọc byte. Nhưng InputStreamReaderlớp học đến để giải cứu! Hãy xem đây là loài vật gì nhé.

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
Chúng tôi chuyển System.inđến đối tượng InputStreamReader . Tên lớp nói lên điều đó! Chúng tôi tạo một InputStreamReaderđối tượng và truyền cho nó một luồng đầu vào mà nó sẽ đọc dữ liệu từ đó. Trong trường hợp này...

new InputStreamReader(System.in)
...chúng tôi nói với nó, "bạn sẽ đọc dữ liệu từ luồng đầu vào hệ thống (từ bàn phím)". Nhưng đây không phải là chức năng duy nhất của nó! Không InputStreamReaderchỉ nhận dữ liệu từ luồng. Nó cũng chuyển đổi các luồng byte thành các luồng ký tự . Nói cách khác, bạn không còn cần phải chuyển đổi dữ liệu từ "số một và số không" sang "ngôn ngữ mà con người có thể đọc được". InputStreamreaderlàm mọi thứ cho bạn. Tất nhiên, InputStreamReaderkhông giới hạn việc đọc dữ liệu từ bảng điều khiển. Nó cũng có thể đọc dữ liệu từ những nơi khác. Ví dụ: từ một tệp:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

   public static void main(String[] args) throws IOException {
       InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\username\\Desktop\\testFile.txt"));
   }
}
Ở đây, chúng tôi tạo một FileInputStream(một hương vị của InputStream), chuyển vào đường dẫn tệp và chuyển chính luồng đó tới tệp InputStreamReader. Bây giờ nó sẽ có thể đọc dữ liệu từ tệp (tất nhiên nếu tệp thực sự tồn tại ở đường dẫn). Chúng tôi cũng sử dụng phương thức InputStreamReadercủa lớp read()để đọc dữ liệu (nguồn dữ liệu không quan trọng: bảng điều khiển, tệp hoặc một nơi nào khác). Đâu là sự khác biệt giữa System.in.read()InputStreamReader.read()?\ Hãy thử đọc lại ký tự "魚" bằng một ký tự InputStreamReader. Tôi nhắc bạn về những gì đã thực sự được đọc bởi System.in.read(): 233 173 154 10 Và làm thế nào để InputStreamReaderthực hiện công việc tương tự?

public class Main {

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

       InputStreamReader reader = new InputStreamReader(System.in);
       while (true) {
           int x = reader.read();
           System.out.println(x);
       }
   }
}
Đầu ra bảng điều khiển: 39770 10 Sự khác biệt rõ ràng ngay lập tức. Byte cuối cùng (đại diện cho dòng mới) không thay đổi (số 10), nhưng ký tự "魚" đã được chuyển đổi thành một mã duy nhất "39770". Đây là ý nghĩa của việc đọc các ký tự! Nếu bạn không tin rằng 39770 đại diện cho chữ "魚", thì bạn cũng dễ dàng thuyết phục mình :)
import java.io.IOException;

public class Main {

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

       char x = 39770;
       System.out.println(x);
   }
}
Đầu ra bảng điều khiển: Nhưng nếu InputStreamReadertuyệt vời như vậy, tại sao chúng ta cũng cần BufferedReader? InputStreamReaderbiết cách đọc dữ liệu và chuyển đổi byte thành ký tự. Chúng ta có thể yêu cầu gì hơn nữa? Tại sao một Reader khác? :/ Câu trả lời rất đơn giản: để có hiệu suất cao hơn và thuận tiện hơn . Hãy bắt đầu với hiệu suất. Khi BufferedReaderđọc dữ liệu, nó sử dụng một vùng đặc biệt được gọi là bộ đệm, nơi nó "lưu trữ" các ký tự mà nó đọc. Cuối cùng, khi các ký tự này cần thiết trong chương trình, chúng sẽ được lấy từ bộ đệm chứ không phải trực tiếp từ nguồn dữ liệu (bàn phím, tệp, v.v.). Điều này tiết kiệm rất nhiều tài nguyên. Để hiểu cách thức hoạt động của nó, hãy tưởng tượng một người chuyển phát nhanh trong một công ty lớn. Người đưa thư ngồi trong văn phòng, đợi ai đó mang gói hàng đi giao. Mỗi khi nhận được một gói hàng mới, anh ấy có thể lên đường ngay lập tức. Nhưng có thể có rất nhiều gói trong ngày. Anh ấy sẽ phải thực hiện rất nhiều chuyến đi giữa văn phòng và các địa chỉ giao hàng. Thay vào đó, người đưa thư đặt một chiếc hộp trong văn phòng của anh ta. Mọi người đặt các gói của họ vào hộp. Bây giờ người chuyển phát nhanh có thể bình tĩnh lấy chiếc hộp và chuyển từ địa chỉ này sang địa chỉ khác. Điều này tiết kiệm rất nhiều thời gian, bởi vì anh ấy không phải quay lại văn phòng mỗi lần. Trong ví dụ này, hộp chỉ là bộ đệm và văn phòng là nguồn dữ liệu. Người chuyển phát nhanh sẽ dễ dàng lấy các gói hàng từ một hộp duy nhất khi giao hàng hơn là phải quay lại văn phòng mỗi lần. Anh ấy cũng sẽ tiết kiệm xăng. Tương tự, trong một chương trình, việc lấy dữ liệu từ bộ đệm ít tốn tài nguyên hơn nhiều so với việc tham chiếu đến nguồn dữ liệu mỗi lần. Kết quả là,BufferedReader+ InputStreamReadernhanh hơn InputStreamReadermột mình . Chúng tôi đã xem xét hiệu suất. Còn về sự tiện lợi thì sao? Ưu điểm chính là Bufferedreadercó thể đọc dữ liệu không chỉ một ký tự tại một thời điểm (mặc dù nó có thể thực hiện điều này với read()phương thức của nó), mà còn đọc cả dòng tại một thời điểm! Điều này được thực hiện bằng readLine()phương pháp;

public class Main {

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

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
       String s = reader.readLine();
       System.out.println("We read this line from the keyboard:");
       System.out.println(s);
   }
}
Đầu ra bảng điều khiển: CodeGym là trang web tốt nhất để học Java! Chúng tôi đọc dòng này từ bàn phím: CodeGym là trang web tốt nhất để học Java! Điều này đặc biệt hữu ích khi đọc một lượng lớn dữ liệu. Đọc một hoặc hai dòng văn bản theo từng ký tự vẫn khả thi. Nhưng đọc từng chữ cái trong "Chiến tranh và Hòa bình" sẽ hơi có vấn đề :)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION