Bạn đang tìm đối tượng hành xử như đối tượng IO (tệp, socket, v.v.), nhưng bạn có thể kiểm soát như một chuỗi?
Thì StringIO là dành cho bạn.
Để tôi cho bạn thấy một vài ví dụ và điều cần chú ý!
Ví dụ Cơ bản
Để tạo đối tượng StringIO, bạn làm thế này:
io = StringIO.new("abc")
Sau đó bạn có thể đọc từ đối tượng này dùng phương thức như gets, read và each_line.
Tôi làm bảng tiện lợi cho bạn với các phương thức hữu ích nhất:
| Phương thức | Mô tả |
|---|---|
| gets | Đọc một dòng đầu vào |
| read | Đọc lượng byte cụ thể (mặc định là tất cả) |
| each_line | Cho khối, lặp qua mỗi dòng |
| each_char | Cho khối, lặp qua mỗi ký tự |
| << | Thêm dữ liệu |
| rewind | Đặt lại con trỏ vị trí nội bộ |
| string | Trả về chuỗi thực tế từ đối tượng stringio |
Lưu ý StringIO có con trỏ vị trí.
Con trỏ này theo dõi bạn đã đọc bao nhiêu byte, giống như đối tượng tệp làm.
Vì vậy mỗi lần bạn gọi phương thức như gets hoặc read, nó sẽ cho bạn lượng dữ liệu đó và đẩy con trỏ tiến lên.
Ngay cả phương thức Enumerable như map hoặc each_line cũng sẽ đẩy con trỏ vị trí, vì vậy hãy nhớ điều đó.
io.each_line { |line| puts line }
Bạn có thể đặt lại con trỏ vị trí về đầu bằng phương thức rewind:
io.pos # 45 io.rewind io.pos # 0
Đó là cơ bản.
Tôi sẽ cho bạn thấy một số ứng dụng thực tế cho StringIO, nhưng trước hãy để tôi cho bạn thấy điều khác.
StringScanner Có Gì Khác?
Bây giờ bạn đã thấy StringIO làm được gì, nhưng Thư viện Chuẩn Ruby bao gồm lớp liên quan đến chuỗi khác.
Lớp đó là StringScanner.
Nó có thể gây nhầm lẫn vì chúng có tên và phương thức tương tự, nhưng để tôi giúp bạn thấy sự khác biệt.
Điều chính là này:
Đối tượng StringIO có thể thay thế đối tượng IO khác (như File hoặc Socket), nhưng StringScanner dành cho việc như phân tích cú pháp (hiểu một số văn bản bằng cách chia thành tập token).
Hai lớp này vẫn chia sẻ thứ gì đó, ngoài việc có "String" trong tên, chúng đều dùng con trỏ vị trí nội bộ.
Thay thế Đầu vào & Đầu ra Chuẩn
Giả sử bạn đang viết ứng dụng dòng lệnh hỏi người dùng đầu vào dùng phương thức Kernel#gets...
...nếu bạn muốn test mã này, bạn sẽ phải nhập gì đó thủ công mỗi lần.
Vậy test tự động bị loại?
Không, không bị!
Đây là nơi StringIO đến giải cứu. Bạn có thể khởi tạo đối tượng StringIO với đầu vào test và sau đó thay thế đối tượng đầu vào chuẩn được trỏ bởi $stdin (đây là nơi Ruby tìm đầu vào người dùng khi bạn gọi gets).
Ví dụ:
io = StringIO.new("input") $stdin = io gets # input
Kỹ thuật này cũng có thể dùng để bắt đầu ra từ phương thức như puts.
Phương thức hiển thị in đến thiết bị đầu ra mặc định, gọi là 'đầu ra chuẩn'. Trong Ruby, đây được biểu diễn bằng đối tượng IO mà bạn có thể thay thế bằng đối tượng StringIO của bạn.
Ví dụ:
io = StringIO.new("") $stdout = io # In ra $stdout puts "Jesus Castello is from Spain & likes to help people learn Ruby." # Khôi phục giá trị gốc $stdout = STDOUT io.rewind io.read # "Jesus Castello is from Spain & likes to help people learn Ruby."
Điều này phức tạp hơn phiên bản đầu vào, vì chúng ta muốn chắc chắn khôi phục đối tượng STDOUT gốc và rewind StringIO để đọc đầu ra.
Lưu ý hầu hết framework test bao gồm phương thức làm điều này cho bạn (assert_output cho Minitest và matcher output trong RSpec), nhưng luôn tuyệt vời khi biết chuyện gì xảy ra đằng sau màn hình 🙂
Tóm tắt
Bạn đã học về lớp StringIO, mô phỏng đối tượng IO thực nên nó có thể làm thay thế loại đối tượng đó.
Điều này hữu ích cho test các lớp ghi đầu ra ra màn hình hoặc yêu cầu đầu vào người dùng qua terminal.
Nếu bạn biết một số ứng dụng thú vị cho StringIO, hãy cho chúng tôi biết trong bình luận và đừng quên chia sẻ bài viết này để nhiều người hơn tận hưởng!