ความแตกต่างระหว่าง open และ codecs.open ใน Python


98

มีสองวิธีในการเปิดไฟล์ข้อความใน Python:

f = open(filename)

และ

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

codecs.openควรเลือกเมื่อopenใด


46
โปรดทราบว่าcodecs.open()ใน 3.x ล้าสมัยเนื่องจากopen()ได้รับencodingอาร์กิวเมนต์
Ignacio Vazquez-Abrams

นอกจากนี้ยังมีวิธีที่ 3 (ใน Python 2.x เป็นอย่างน้อย): `f = file (filename) '
Adam Parkin

1
@ IgnacioVazquez-Abrams มีลิงค์ที่codecs.open()ล้าสมัยหรือไม่? ฉันไม่คิดว่าสิ่งนี้ในเอกสารpython3
varela

1
@varela: หน้าเอกสาร Python ที่คุณกล่าวถึงกล่าวว่า: "builtin open () และโมดูล io ที่เกี่ยวข้องเป็นแนวทางที่แนะนำสำหรับการทำงานกับไฟล์ข้อความที่เข้ารหัส"
Luciano Ramalho

คำตอบ:


83

ตั้งแต่ Python 2.6 การปฏิบัติที่ดีคือการใช้งานio.open()ซึ่งยังใช้เวลาโต้แย้งเช่นล้าสมัยในขณะนี้encoding codecs.open()ใน Python 3 io.openเป็นนามแฝงสำหรับไฟล์open(). ใช้io.open()งานได้ใน Python 2.6 และเวอร์ชันที่ใหม่กว่าทั้งหมดรวมถึง Python 3.4 ดูเอกสาร: http://docs.python.org/3.4/library/io.html

ตอนนี้สำหรับคำถามเดิม: เมื่ออ่านข้อความ (รวมทั้ง "ข้อความธรรมดา", HTML, XML และ JSON) ในหลาม 2 คุณควรเสมอใช้io.open()กับการเข้ารหัสอย่างชัดเจนหรือopen()มีการเข้ารหัสอย่างชัดเจนในหลาม 3. การทำเช่นนี้หมายความว่าคุณจะได้รับอย่างถูกต้อง Unicode ที่ถอดรหัสแล้วหรือได้รับข้อผิดพลาดจากแบ็ตทำให้ง่ายต่อการดีบัก

"ข้อความธรรมดา" ของ ASCII เป็นตำนานจากอดีตอันไกลโพ้น ข้อความภาษาอังกฤษที่ถูกต้องจะใช้เครื่องหมายคำพูดแบบหยิกเครื่องหมายขีดกลางสัญลักษณ์แสดงหัวข้อย่อย€ (เครื่องหมายยูโร) และแม้แต่ diaeresis (¨) อย่าไร้เดียงสา! (และอย่าลืมรูปแบบการออกแบบFaçade!)

เพราะ ASCII บริสุทธิ์ไม่ได้เป็นตัวเลือกที่แท้จริงopen()โดยไม่ต้องมีการเข้ารหัสอย่างชัดเจนเป็นเพียงประโยชน์ในการอ่านไบนารีไฟล์


5
@ForeverWintr คำตอบค่อนข้างชัดเจนในนั้น: ใช้io.open()สำหรับข้อความและopen()สำหรับไบนารีเท่านั้น ความหมายคือcodecs.open()ไม่ต้องการเลย
Bdoserror

2
@Bdoserror มีคำตอบอยู่ในนั้นอย่างเห็นได้ชัด แต่ก็ไม่ได้คำตอบสำหรับคำถามที่ถูกถาม คำถามเกี่ยวกับความแตกต่างระหว่างและและโดยเฉพาะอย่างยิ่งเมื่อคำหลังเป็นที่นิยมกว่าในอดีต คำตอบที่ไม่มากพอที่จะกล่าวถึงไม่สามารถตอบคำถามนั้นได้ opencodecs.opencodecs.open
ForeverWintr

3
@ForeverWintr หาก OP ถามคำถามผิด (เช่นมีข้อสันนิษฐานว่าcodecs.open()ถูกต้องในการใช้งาน) แล้วไม่มีคำตอบที่ "ถูกต้อง" เกี่ยวกับเวลาที่จะใช้ คำตอบคือใช้io.open()แทน มันเหมือนกับว่าฉันถามว่า "ฉันควรใช้ประแจเพื่อตอกตะปูเข้าไปในกำแพงเมื่อใด" คำตอบที่ถูกต้องคือ "ใช้ค้อน"
Bdoserror

20

โดยส่วนตัวแล้วฉันมักจะใช้codecs.openเว้นแต่จะมีการระบุไว้ชัดเจนว่าจำเป็นต้องใช้open** เหตุผลก็คือมีหลายครั้งที่ฉันถูกกัดจากการที่อินพุต utf-8 แอบเข้าไปในโปรแกรมของฉัน "โอ้ฉันเพิ่งรู้ว่ามันจะเป็น ascii เสมอ" มักจะเป็นข้อสันนิษฐานที่พังบ่อย

สมมติว่า 'utf-8' เป็นการเข้ารหัสเริ่มต้นมีแนวโน้มที่จะเป็นตัวเลือกเริ่มต้นที่ปลอดภัยกว่าในประสบการณ์ของฉันเนื่องจาก ASCII สามารถถือเป็น UTF-8 ได้ แต่การสนทนาไม่เป็นความจริง และในกรณีดังกล่าวเมื่อผมไม่ทราบว่าเข้าเป็น ASCII แล้วฉันยังคงทำcodecs.openตามที่ฉันศรัทธาใน"อย่างชัดเจนจะดีกว่าโดยปริยาย"

** - ใน Python 2.x เนื่องจากความคิดเห็นของคำถามใน Python 3 openเข้ามาแทนที่codecs.open


สิ่งที่ฉันไม่ได้รับจริงๆคือทำไมopenบางครั้งสามารถจัดการอักขระที่ไม่ใช่ภาษาละตินที่เข้ารหัส UTF-8 ของชุดยูนิโค้ดได้เป็นอย่างดีและบางครั้งก็ล้มเหลวอย่างไม่น่าเชื่อ ...
cedbeu

สิ่งนี้สมเหตุสมผลสำหรับฉัน io.openไม่ใช้พารามิเตอร์การเข้ารหัสจากสิ่งที่ฉันเห็นใน python 2.7.5
radtek

1
@radtek คุณคิดถูกแล้วที่ไม่มีเอกสาร อย่างไรก็ตาม (อย่างน้อยใน 2.7.12) io.openยอมรับencodingและnewlineกำหนดพารามิเตอร์และตีความเป็น Python 3 แตกต่างจากcodecs.openไฟล์ที่เปิดด้วยio.openจะเพิ่มขึ้นTypeError: write() argument 1 must be unicode, not strแม้ใน Python 2.7 หากคุณพยายามเขียนstr( bytes) เข้าไป ไฟล์ที่เปิดไว้กับcodecs.openแทนจะพยายามแปลงโดยปริยายที่จะunicodeมักจะนำไปสู่ความสับสนUnicodeDecodeErrors
jochietoch

9

ใน Python 2 มีสตริง unicode และ bytestrings หากคุณใช้ bytestrings คุณสามารถอ่าน / เขียนไปยังไฟล์ที่เปิดได้open()อย่างดี ท้ายที่สุดสตริงเป็นเพียงไบต์

ปัญหาเกิดขึ้นเมื่อพูดว่าคุณมีสตริงยูนิโคดและคุณทำสิ่งต่อไปนี้:

>>> 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)

เห็นได้ชัดว่าคุณเข้ารหัสสตริง Unicode ของคุณใน utf-8 อย่างชัดเจนหรือคุณใช้ codecs.openทำเพื่อคุณอย่างโปร่งใส

หากคุณเคยใช้ bytestrings เพียงอย่างเดียวก็ไม่มีปัญหา:

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

มันมีส่วนเกี่ยวข้องมากกว่านี้เพราะเมื่อคุณเชื่อมต่อ unicode และ bytestring string กับ+โอเปอเรเตอร์คุณจะได้สตริง unicode ง่ายต่อการถูกกัด

ยังcodecs.openไม่ชอบ bytestrings ที่มีการส่งผ่านอักขระที่ไม่ใช่ ASCII:

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)

คำแนะนำเกี่ยวกับสตริงสำหรับอินพุต / ouput โดยปกติคือ "แปลงเป็น Unicode ให้เร็วที่สุดและกลับไปใช้ bytestrings ให้ช้าที่สุด" การใช้codecs.openช่วยให้คุณทำอย่างหลังได้อย่างง่ายดาย

โปรดระวังว่าคุณกำลังให้สตริง unicode ไม่ใช่ bytestrings ที่อาจมีอักขระที่ไม่ใช่ ASCII


คุณสามารถอธิบายตัวอย่างที่สองของคุณได้หรือไม่? ดูเหมือนว่าจะเหมือนกับตัวอย่างแรกของคุณดังนั้นทำไมผลลัพธ์จึงแตกต่างกัน?
Chris Johnson

สังเกตการใช้งานu''ในตัวอย่างแรก ซึ่งหมายความว่าฉันสร้างสตริง Unicode ไม่ใช่ bytestring นี่คือความแตกต่างระหว่างสองตัวอย่าง ในตัวอย่างที่สองฉันกำลังสร้าง bytestring และเขียนหนึ่งในนั้นลงในไฟล์ก็ใช้ได้ สตริง Unicode ไม่ดีถ้าคุณใช้อักขระนอก ASCII
Mandible79

7

เมื่อคุณต้องการเปิดไฟล์ที่มีการเข้ารหัสบางอย่างคุณจะต้องใช้codecsโมดูลนี้


15
ฉันเดาว่าไฟล์ข้อความทั้งหมดมีการเข้ารหัสที่แน่นอน (:
cedbeu

5

codecs.openฉันคิดว่ามันเป็นเพียงส่วนที่เหลือจากPython 2สมัยที่การเปิดในตัวมีอินเทอร์เฟซที่ง่ายกว่ามากและความสามารถน้อยลง ใน Python 2 บิวท์อินopenจะไม่ใช้อาร์กิวเมนต์การเข้ารหัสดังนั้นหากคุณต้องการใช้อย่างอื่นนอกเหนือจากโหมดไบนารีหรือการเข้ารหัสเริ่มต้นควรใช้ codecs.open

ในPython 2.6โมดูล io มีส่วนช่วยในการทำให้สิ่งต่างๆง่ายขึ้นเล็กน้อย ตามเอกสารอย่างเป็นทางการ

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.

ต้องบอกว่าสิ่งเดียวที่ฉันคิดได้codecs.openในสถานการณ์ปัจจุบันคือการใช้งานร่วมกันได้ ในสถานการณ์อื่น ๆ ทั้งหมด (ยกเว้นกรณีที่คุณกำลังใช้งูหลาม <2.6) io.openมันเป็นที่นิยมในการใช้งาน นอกจากนี้ยังPython 3.x io.openเป็นเช่นเดียวกับbuilt-in open

บันทึก:

มีความแตกต่างทางไวยากรณ์ระหว่างcodecs.openและio.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)

ไม่เพียงcodecs.openและio.openแตกต่างกันในแง่ของไวยากรณ์ แต่ยังส่งคืนวัตถุประเภทต่างๆ ยังใช้codecs.openงานได้กับไฟล์ในโหมดไบนารีเสมอ
wombatonfire

4
  • เมื่อคุณต้องการโหลดไฟล์ไบนารีให้ใช้ f = io.open(filename, 'b').

  • สำหรับการเปิดไฟล์ข้อความให้ใช้f = io.open(filename, encoding='utf-8')กับการเข้ารหัสที่ชัดเจนเสมอ

ในหลาม 3แต่openจะเป็นสิ่งเดียวกันเป็นio.openและสามารถนำมาใช้แทน

หมายเหตุ: codecs.openการวางแผนที่จะกลายเป็นเลิกไปและแทนที่ด้วยio.openหลังจากที่เปิดตัวในหลาม 2.6 ฉันจะใช้มันก็ต่อเมื่อรหัสต้องเข้ากันได้กับ python เวอร์ชันก่อนหน้า สำหรับข้อมูลเพิ่มเติมเกี่ยวกับตัวแปลงสัญญาณและ Unicode ในหลามดูUnicode HOWTO


1. เหตุใดฉันจึงไม่สามารถเปิดไฟล์ในโหมดไบนารีโดยใช้io.openหรือcodecs.open? 2. codecs.openยังไม่เลิกใช้งานโปรดอ่านการสนทนาในหน้าที่คุณเชื่อมโยงไป
wombatonfire

จุดดี! 1. คุณสามารถใช้อย่างใดอย่างหนึ่ง แต่ฉันขอแนะนำอีกครั้งเกี่ยวกับตัวแปลงสัญญาณเปิดเว้นแต่คุณจะใช้ python 2.5 หรือเก่ากว่า 2. ฉันอัปเดตคำตอบเพื่อแสดงให้เห็นว่าการเลิกใช้งานไม่ได้เกิดขึ้นในทันที แต่จะเกิดขึ้นในอนาคต
wihlke


0

ฉันอยู่ในสถานการณ์ที่จะเปิดไฟล์. asm และประมวลผลไฟล์

#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:

ฉันสามารถอ่านไฟล์ทั้งหมดได้โดยไม่มีปัญหามีข้อเสนอแนะหรือไม่?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.