Sự khác biệt giữa open và codecs.open trong Python


95

Có hai cách để mở tệp văn bản bằng Python:

f = open(filename)

import codecs
f = codecs.open(filename, encoding="utf-8")

Khi nào thì codecs.openthích hợp hơn open?


45
Lưu ý rằng điều đó codecs.open()đã lỗi thời trong 3.x, vì open()đạt được encodingđối số.
Ignacio Vazquez-Abrams

Ngoài ra còn có một cách thứ 3 (bằng Python 2.x ít nhất): `f = file (filename)'
Adam Parkin

1
@ IgnacioVazquez-Abrams Có liên kết nào codecs.open()đã lỗi thời không? Tôi không nghĩ rằng đây trong tài liệu python3: docs.python.org/3.7/library/codecs.html
Varela

1
@varela: trang tài liệu Python mà bạn đã đề cập cho biết: "nội trang open () và mô-đun io được liên kết là phương pháp được đề xuất để làm việc với các tệp văn bản được mã hóa"
Luciano Ramalho

Câu trả lời:


82

Kể từ Python 2.6, một phương pháp hay là sử dụng io.open(), điều này cũng cần một encodingđối số, giống như hiện đã lỗi thời codecs.open(). Trong Python 3, io.openlà một bí danh cho tích open()hợp sẵn. Vì vậy, io.open()hoạt động trên Python 2.6 và tất cả các phiên bản mới hơn, bao gồm cả Python 3.4. Xem tài liệu: http://docs.python.org/3.4/library/io.html

Bây giờ, đối với câu hỏi ban đầu: khi đọc văn bản (bao gồm "văn bản thuần túy", HTML, XML và JSON) trong Python 2, bạn nên luôn sử dụng io.open()với mã hóa rõ ràng hoặc open()với mã hóa rõ ràng trong Python 3. Làm như vậy có nghĩa là bạn hiểu chính xác đã giải mã Unicode hoặc gặp lỗi ngay lập tức, giúp việc gỡ lỗi dễ dàng hơn nhiều.

ASCII thuần túy "văn bản thuần túy" là một huyền thoại từ quá khứ xa xôi. Văn bản tiếng Anh thích hợp sử dụng dấu ngoặc kép, dấu gạch ngang, dấu đầu dòng, € (dấu euro) và thậm chí dấu ngoặc kép (¨). Đừng ngây thơ! (Và đừng quên mẫu thiết kế Façade!)

Bởi vì ASCII thuần túy không phải là một tùy chọn thực sự, open()không có mã hóa rõ ràng chỉ hữu ích để đọc các tệp nhị phân .


5
@ForeverWintr Câu trả lời khá rõ ràng trong đó: sử dụng io.open()cho văn bản và open()chỉ cho nhị phân. Hàm ý là điều đó codecs.open()không được ưa thích chút nào.
Bdoserror

2
@Bdoserror, Có một câu trả lời trong đó, rõ ràng, nhưng nó không phải là câu trả lời cho câu hỏi đã được đặt ra. Câu hỏi là về sự khác biệt giữa opencodecs.open, và cụ thể khi nào thì cái sau thích hợp hơn cái trước. Một câu trả lời không quá nhiều đề cập đến codecs.openkhông thể trả lời câu hỏi đó.
ForeverWintr

3
@ForeverWintr Nếu OP hỏi câu hỏi sai (nghĩa là với giả định codecs.open()là sử dụng đúng) thì sẽ không có câu trả lời "đúng" về thời điểm sử dụng. Câu trả lời là sử dụng io.open()thay thế. Nó giống như nếu tôi hỏi "khi nào thì nên dùng cờ lê để đóng đinh vào tường?". Câu trả lời đúng là "sử dụng một cái búa".
Bdoserror

20

Cá nhân tôi luôn sử dụng codecs.opentrừ khi có nhu cầu sử dụng open** được xác định rõ ràng . Lý do là đã rất nhiều lần tôi bị cắn khi có đầu vào utf-8 lẻn vào các chương trình của tôi. "Ồ, tôi chỉ biết nó sẽ luôn là ascii" có xu hướng là một giả định thường bị phá vỡ.

Theo kinh nghiệm của tôi, giả sử 'utf-8' làm mã hóa mặc định có xu hướng là lựa chọn mặc định an toàn hơn, vì ASCII có thể được coi là UTF-8, nhưng điều ngược lại là không đúng. Và trong những trường hợp khi tôi thực sự biết rằng đầu vào là ASCII, thì tôi vẫn làm codecs.opennhư một người tin tưởng chắc chắn vào "rõ ràng tốt hơn là ẩn" .

** - trong Python 2.x, vì nhận xét về trạng thái câu hỏi trong Python 3 openthay thếcodecs.open


những gì tôi không thực sự nhận được là lý do tại sao openđôi khi có thể xử lý rất tốt các nhân vật phi Latin UTF-8 mã hóa của tập unicode, và đôi khi nó không thành công miserabily ...
cedbeu

Điều này có ý nghĩa đối với tôi. io.openkhông mất một param mã hóa từ những gì tôi có thể nhìn thấy trong python 2.7.5
radtek

1
@radtek, bạn nói đúng rằng cái này không có giấy tờ; tuy nhiên (ít nhất là trong 2.7.12) io.openchấp nhận encodingnewlinecác tham số và giải thích chúng như Python 3. Không giống như codecs.open, một tệp được mở bằng io.opensẽ tăng lên TypeError: write() argument 1 must be unicode, not strngay cả trong Python 2.7 nếu bạn cố gắng ghi str( bytes) vào nó. codecs.openThay vào đó, một tệp được mở bằng will cố gắng chuyển đổi ngầm sang unicode, thường dẫn đến UnicodeDecodeErrorcác s gây nhầm lẫn .
jochietoch

9

Trong Python 2 có các chuỗi unicode và bytestrings. Nếu bạn chỉ sử dụng bytestrings, bạn có thể đọc / ghi vào một tệp được mở open()một cách dễ dàng. Rốt cuộc, các chuỗi chỉ là byte.

Vấn đề xảy ra khi bạn có một chuỗi unicode và bạn làm như sau:

>>> example = u'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Vì vậy, ở đây rõ ràng là bạn mã hóa chuỗi unicode của mình trong utf-8 một cách rõ ràng hoặc bạn sử dụng codecs.openđể làm điều đó cho bạn một cách minh bạch.

Nếu bạn chỉ sử dụng bytestrings thì không có vấn đề gì:

>>> example = 'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
>>>

Nó liên quan nhiều hơn điều này bởi vì khi bạn nối một chuỗi unicode và bytestring với +toán tử, bạn sẽ nhận được một chuỗi unicode. Dễ bị cắn bởi con đó.

Cũng codecs.openkhông thích các bytestrings với các ký tự không phải ASCII được chuyển vào:

codecs.open('test', 'w', encoding='utf-8').write('Μου αρέσει')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/codecs.py", line 691, in write
    return self.writer.write(data)
  File "/usr/lib/python2.7/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)

Lời khuyên về các chuỗi cho input / ouput thường là "chuyển đổi sang unicode càng sớm càng tốt và quay lại bytestrings càng muộn càng tốt". Sử dụng codecs.opencho phép bạn làm điều sau rất dễ dàng.

Chỉ cần cẩn thận rằng bạn đang cung cấp cho nó các chuỗi unicode chứ không phải các chuỗi bytest có thể có các ký tự không phải ASCII.


Bạn có thể giải thích ví dụ thứ hai của bạn? Nó có vẻ giống với ví dụ đầu tiên của bạn, vậy tại sao kết quả lại khác?
Chris Johnson

Lưu ý việc sử dụng u''trong ví dụ đầu tiên. Điều này có nghĩa là tôi đã tạo một chuỗi unicode, không phải một bytestring. Đây là sự khác biệt giữa hai ví dụ. Trong ví dụ thứ hai, tôi đang tạo một bytestring và việc ghi một trong số đó vào một tệp là tốt. Một chuỗi unicode sẽ không ổn nếu bạn đang sử dụng các ký tự bên ngoài ASCII.
Mandible 79

7

Khi bạn cần mở một tệp có một mã hóa nhất định, bạn sẽ sử dụng codecsmô-đun.


15
Tôi đoán tất cả các tệp văn bản đều có một mã hóa nhất định, bằng cách nào đó (:
cedbeu.

5

codecs.open, tôi cho rằng, chỉ là phần còn lại từ những Python 2ngày mà phần mềm mở tích hợp có giao diện đơn giản hơn nhiều và ít khả năng hơn. Trong Python 2, tích hợp sẵn openkhông sử dụng đối số mã hóa, vì vậy nếu bạn muốn sử dụng thứ gì đó khác ngoài chế độ nhị phân hoặc mã hóa mặc định, thì codecs.open phải được sử dụng.

Trong đó Python 2.6, mô-đun io đã hỗ trợ để làm cho mọi thứ đơn giản hơn một chút. Theo tài liệu chính thức

New in version 2.6.

The io module provides the Python interfaces to stream handling.
Under Python 2.x, this is proposed as an alternative to the
built-in file object, but in Python 3.x it is the default
interface to access files and streams.

Phải nói rằng, cách sử dụng duy nhất tôi có thể nghĩ đến codecs.opentrong kịch bản hiện tại là khả năng tương thích ngược. Trong tất cả các trường hợp khác (trừ khi bạn đang sử dụng Python <2,6) thì nên sử dụng io.open. Cũng Python 3.x io.opengiống nhưbuilt-in open

Ghi chú:

Cũng có sự khác biệt về cú pháp giữa codecs.openio.open.

codecs.open:

open(filename, mode='rb', encoding=None, errors='strict', buffering=1)

io.open:

open(file, mode='r', buffering=-1, encoding=None,
     errors=None, newline=None, closefd=True, opener=None)

Không chỉ codecs.openio.openkhác nhau về cú pháp, chúng trả về các đối tượng có kiểu khác nhau. Cũng codecs.openluôn hoạt động với các tệp ở chế độ nhị phân.
bellyatonfire

4
  • Khi bạn muốn tải một tệp nhị phân, hãy sử dụng f = io.open(filename, 'b').

  • Để mở tệp văn bản, hãy luôn sử dụng f = io.open(filename, encoding='utf-8')mã hóa rõ ràng.

Tuy nhiên, trong python 3open cũng làm điều tương tự io.openvà có thể được sử dụng thay thế.

Lưu ý: codecs.open dự kiến ​​sẽ không còn được dùng nữa và được thay thế bằng io.opensau khi được giới thiệu trong python 2.6 . Tôi sẽ chỉ sử dụng nó nếu mã cần tương thích với các phiên bản python trước đó. Để biết thêm thông tin về codec và unicode trong python, hãy xem Unicode HOWTO .


1. Tại sao tôi không thể mở tệp ở chế độ nhị phân với io.openhoặc codecs.open? 2. codecs.openvẫn chưa bị phản đối, hãy đọc thảo luận trên trang bạn đã liên kết đến.
bellyatonfire

Điểm tốt! 1. Bạn có thể sử dụng một trong hai, nhưng một lần nữa tôi khuyên bạn nên chống lại codecs.open trừ khi bạn đang sử dụng python 2.5 trở lên. 2. Tôi đã cập nhật câu trả lời của mình để phản ánh rằng việc ngừng sử dụng không diễn ra ngay lập tức mà là trong tương lai.
wihlke

3

Khi bạn đang làm việc với các tệp văn bản và muốn mã hóa và giải mã trong suốt thành các đối tượng Unicode.


0

Tôi đã gặp phải tình huống mở tệp .asm và xử lý tệp.

#https://docs.python.org/3/library/codecs.html#codecs.ignore_errors
#https://docs.python.org/3/library/codecs.html#codecs.Codec.encode
with codecs.open(file, encoding='cp1252', errors ='replace') as file:

Không có nhiều khó khăn, tôi có thể đọc toàn bộ tệp, bất kỳ đề xuất nào?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.