Bài 10.1. Python - Đọc dữ liệu từ tệp

Bài 10.1. Python - Đọc dữ liệu từ tệp
Một lượng dữ liệu khổng lồ có sẵn trong các tệp văn bản. Các tệp văn bản có thể chứa dữ liệu thời tiết, dữ liệu giao thông, dữ liệu kinh tế xã hội, tác phẩm văn học và nhiều hơn nữa. Đọc từ một tệp đặc biệt hữu ích trong các ứng dụng phân tích dữ liệu, nhưng nó cũng áp dụng cho bất kỳ tình huống nào mà bạn muốn phân tích hoặc sửa đổi thông tin được lưu trữ trong một tệp. Ví dụ, bạn có thể viết một chương trình đọc nội dung của một tệp văn bản và viết lại tệp với định dạng cho phép trình duyệt hiển thị nó.
Khi bạn muốn làm việc với thông tin trong một tệp văn bản, bước đầu tiên là đọc tệp vào bộ nhớ. Sau đó, bạn có thể làm việc qua tất cả nội dung của tệp cùng một lúc hoặc làm việc qua nội dung từng dòng một.
Đọc nội dung của một tệp
Để bắt đầu, chúng ta cần một tệp có vài dòng văn bản trong đó. Hãy bắt đầu với một tệp chứa số pi đến 30 chữ số thập phân, với 10 chữ số thập phân mỗi dòng:
3.1415926535
8979323846
2643383279
Dưới đây là một chương trình mở tệp này, đọc nó và in nội dung của tệp ra màn hình:
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
print(contents)
Để làm việc với nội dung của một tệp, chúng ta cần cho Python biết đường dẫn đến tệp. Đường dẫn là vị trí chính xác của một tệp hoặc thư mục trên hệ thống. Python cung cấp một module gọi là pathlib
giúp làm việc với các tệp và thư mục dễ dàng hơn, bất kể hệ điều hành mà bạn hoặc người dùng chương trình của bạn đang sử dụng. Một module cung cấp chức năng cụ thể như thế này thường được gọi là thư viện, do đó có tên pathlib
.
Chúng ta bắt đầu bằng cách nhập lớp Path
từ pathlib
. Có rất nhiều điều bạn có thể làm với một đối tượng Path
trỏ đến một tệp. Ví dụ, bạn có thể kiểm tra xem tệp có tồn tại trước khi làm việc với nó, đọc nội dung của tệp hoặc ghi dữ liệu mới vào tệp. Ở đây, chúng ta xây dựng một đối tượng Path
đại diện cho tệp pi_digits.txt
, mà chúng ta gán cho biến path
. Vì tệp này được lưu trong cùng thư mục với tệp .py
mà chúng ta đang viết, tên tệp là tất cả những gì Path
cần để truy cập tệp.
Khi chúng ta có một đối tượng Path
đại diện cho pi_digits.txt
, chúng ta sử dụng phương thức read_text()
để đọc toàn bộ nội dung của tệp. Nội dung của tệp được trả về dưới dạng một chuỗi, mà chúng ta gán cho biến contents
. Khi chúng ta in giá trị của contents
, chúng ta thấy toàn bộ nội dung của tệp văn bản:
3.1415926535
8979323846
2643383279
Sự khác biệt duy nhất giữa đầu ra này và tệp gốc là dòng trống thêm ở cuối đầu ra. Dòng trống xuất hiện vì read_text()
trả về một chuỗi trống khi nó đạt đến cuối tệp; chuỗi trống này xuất hiện dưới dạng một dòng trống.
Chúng ta có thể loại bỏ dòng trống thêm bằng cách sử dụng rstrip()
trên chuỗi contents
:
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
contents = contents.rstrip()
print(contents)
Nhớ lại từ Chương 2 rằng phương thức rstrip()
của Python loại bỏ, hoặc tước bỏ, bất kỳ ký tự khoảng trắng nào từ phía bên phải của một chuỗi. Bây giờ đầu ra khớp chính xác với nội dung của tệp gốc:
3.1415926535
8979323846
2643383279
Chúng ta có thể tước bỏ ký tự xuống dòng cuối cùng khi chúng ta đọc nội dung của tệp, bằng cách áp dụng phương thức rstrip()
ngay sau khi gọi read_text()
:
contents = path.read_text().rstrip()
Dòng này yêu cầu Python gọi phương thức read_text()
trên tệp mà chúng ta đang làm việc. Sau đó, nó áp dụng phương thức rstrip()
cho chuỗi mà read_text()
trả về. Chuỗi đã được làm sạch sau đó được gán cho biến contents
. Cách tiếp cận này được gọi là chaining phương thức, và bạn sẽ thấy nó được sử dụng thường xuyên trong lập trình.
Đường dẫn tương đối và tuyệt đối
Khi bạn truyền một tên tệp đơn giản như pi_digits.txt
cho Path
, Python sẽ tìm trong thư mục nơi tệp hiện đang được thực thi (tức là tệp chương trình .py
của bạn) được lưu trữ.
Đôi khi, tùy thuộc vào cách bạn tổ chức công việc của mình, tệp bạn muốn mở sẽ không nằm trong cùng thư mục với tệp chương trình của bạn. Ví dụ, bạn có thể lưu trữ các tệp chương trình của mình trong một thư mục gọi là python_work
; bên trong python_work
, bạn có thể có một thư mục khác gọi là text_files
để phân biệt các tệp chương trình của bạn với các tệp văn bản mà chúng đang thao tác. Mặc dù text_files
nằm trong python_work
, chỉ cần truyền Path
tên của một tệp trong text_files
sẽ không hoạt động, vì Python sẽ chỉ tìm trong python_work
và dừng lại ở đó; nó sẽ không tiếp tục và tìm trong text_files
. Để yêu cầu Python mở các tệp từ một thư mục khác với thư mục nơi tệp chương trình của bạn được lưu trữ, bạn cần cung cấp đường dẫn chính xác.
Có hai cách chính để chỉ định đường dẫn trong lập trình. Một đường dẫn tệp tương đối yêu cầu Python tìm một vị trí nhất định tương đối với thư mục nơi tệp chương trình hiện đang chạy được lưu trữ. Vì text_files
nằm trong python_work
, chúng ta cần xây dựng một đường dẫn bắt đầu với thư mục text_files
và kết thúc với tên tệp. Dưới đây là cách xây dựng đường dẫn này:
path = Path('text_files/filename.txt')
Bạn cũng có thể yêu cầu Python chính xác nơi tệp nằm trên máy tính của bạn, bất kể nơi tệp chương trình đang được thực thi được lưu trữ. Điều này được gọi là đường dẫn tệp tuyệt đối. Bạn có thể sử dụng một đường dẫn tuyệt đối nếu một đường dẫn tương đối không hoạt động. Ví dụ, nếu bạn đã đặt text_files
trong một thư mục khác ngoài python_work
, thì chỉ cần truyền Path
đường dẫn 'text_files/filename.txt' sẽ không hoạt động vì Python sẽ chỉ tìm vị trí đó bên trong python_work
. Bạn sẽ cần viết ra một đường dẫn tuyệt đối để làm rõ nơi bạn muốn Python tìm.
Đường dẫn tuyệt đối thường dài hơn đường dẫn tương đối, vì chúng bắt đầu từ thư mục gốc của hệ thống của bạn:
path = Path('/home/eric/data_files/text_files/filename.txt')
Sử dụng đường dẫn tuyệt đối, bạn có thể đọc các tệp từ bất kỳ vị trí nào trên hệ thống của bạn. Hiện tại, dễ nhất là lưu trữ các tệp trong cùng thư mục với các tệp chương trình của bạn, hoặc trong một thư mục như text_files
trong thư mục lưu trữ các tệp chương trình của bạn.
Truy cập các dòng của một tệp
Khi bạn làm việc với một tệp, bạn sẽ thường muốn kiểm tra từng dòng của tệp. Bạn có thể đang tìm kiếm thông tin nhất định trong tệp, hoặc bạn có thể muốn sửa đổi văn bản trong tệp theo một cách nào đó. Ví dụ, bạn có thể muốn đọc qua một tệp dữ liệu thời tiết và làm việc với bất kỳ dòng nào bao gồm từ "sunny" trong mô tả về thời tiết của ngày đó. Trong một báo cáo tin tức, bạn có thể tìm kiếm bất kỳ dòng nào có thẻ <headline>
và viết lại dòng đó với một loại định dạng cụ thể.
Bạn có thể sử dụng phương thức splitlines()
để biến một chuỗi dài thành một tập hợp các dòng, và sau đó sử dụng một vòng lặp for
để kiểm tra từng dòng từ một tệp, từng dòng một:
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
for line in lines:
print(line)
Chúng ta bắt đầu bằng cách đọc toàn bộ nội dung của tệp, như chúng ta đã làm trước đó. Nếu bạn dự định làm việc với các dòng riêng lẻ trong một tệp, bạn không cần tước bỏ bất kỳ khoảng trắng nào khi đọc tệp. Phương thức splitlines()
trả về một danh sách tất cả các dòng trong tệp, và chúng ta gán danh sách này cho biến lines
. Sau đó, chúng ta lặp qua các dòng này và in từng dòng:
3.1415926535
8979323846
2643383279
Vì chúng ta chưa sửa đổi bất kỳ dòng nào, đầu ra khớp chính xác với tệp văn bản gốc.
Làm việc với nội dung của một tệp
Sau khi bạn đã đọc nội dung của một tệp vào bộ nhớ, bạn có thể làm bất cứ điều gì bạn muốn với dữ liệu đó, vì vậy hãy khám phá ngắn gọn các chữ số của pi. Đầu tiên, chúng ta sẽ cố gắng xây dựng một chuỗi duy nhất chứa tất cả các chữ số trong tệp mà không có khoảng trắng trong đó:
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:
pi_string += line
print(pi_string)
print(len(pi_string))
Chúng ta bắt đầu bằng cách đọc tệp và lưu trữ từng dòng chữ số trong một danh sách, giống như chúng ta đã làm trong ví dụ trước. Sau đó, chúng ta tạo một biến, pi_string
, để giữ các chữ số của pi. Chúng ta viết một vòng lặp thêm từng dòng chữ số vào pi_string
. Chúng ta in chuỗi này và cũng hiển thị độ dài của chuỗi:
3.1415926535 8979323846 2643383279
36
Biến pi_string
chứa khoảng trắng ở phía bên trái của các chữ số trong mỗi dòng, nhưng chúng ta có thể loại bỏ điều đó bằng cách sử dụng lstrip()
trên mỗi dòng:
--snip--
for line in lines:
pi_string += line.lstrip()
print(pi_string)
print(len(pi_string))
Bây giờ chúng ta có một chuỗi chứa số pi đến 30 chữ số thập phân. Chuỗi này dài 32 ký tự vì nó cũng bao gồm số 3 đầu tiên và một dấu chấm thập phân:
3.141592653589793238462643383279
32
Tệp lớn: Một triệu chữ số
Cho đến nay, chúng ta đã tập trung vào việc phân tích một tệp văn bản chỉ chứa ba dòng, nhưng mã trong các ví dụ này sẽ hoạt động tốt trên các tệp lớn hơn nhiều. Nếu chúng ta bắt đầu với một tệp văn bản chứa số pi đến 1.000.000 chữ số thập phân, thay vì chỉ 30, chúng ta có thể tạo một chuỗi duy nhất chứa tất cả các chữ số này. Chúng ta không cần thay đổi chương trình của mình chút nào, ngoại trừ việc truyền cho nó một tệp khác. Chúng ta cũng sẽ chỉ in ra 50 chữ số thập phân đầu tiên, để chúng ta không phải xem một triệu chữ số cuộn qua trong terminal:
from pathlib import Path
path = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:
pi_string += line.lstrip()
print(f"{pi_string[:52]}...")
print(len(pi_string))
Đầu ra cho thấy rằng chúng ta thực sự có một chuỗi chứa số pi đến 1.000.000 chữ số thập phân:
3.14159265358979323846264338327950288419716939937510...
1000002
Python không có giới hạn nội tại về lượng dữ liệu bạn có thể làm việc; bạn có thể làm việc với nhiều dữ liệu như bộ nhớ của hệ thống của bạn có thể xử lý.
Sinh nhật của bạn có nằm trong số pi không?
Tôi luôn tò mò muốn biết liệu sinh nhật của mình có xuất hiện ở đâu đó trong các chữ số của pi hay không. Hãy sử dụng chương trình mà chúng ta vừa viết để tìm hiểu xem sinh nhật của ai đó có xuất hiện ở đâu đó trong một triệu chữ số đầu tiên của pi hay không. Chúng ta có thể làm điều này bằng cách biểu diễn mỗi sinh nhật dưới dạng một chuỗi chữ số và xem liệu chuỗi đó có xuất hiện ở đâu đó trong pi_string
hay không:
--snip--
for line in lines:
pi_string += line.strip()
birthday = input("Enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of pi.")
Đầu tiên, chúng ta yêu cầu người dùng nhập sinh nhật của họ, và sau đó kiểm tra xem chuỗi đó có nằm trong pi_string
hay không. Hãy thử nó:
Enter your birthday, in the form mmddyy: 120372
Your birthday appears in the first million digits of pi!
Sinh nhật của tôi thực sự xuất hiện trong các chữ số của pi! Một khi bạn đã đọc từ một tệp, bạn có thể phân tích nội dung của nó theo bất kỳ cách nào bạn có thể tưởng tượng.
Comments