เขียนไปยังไฟล์ UTF-8 ใน Python


204

codecs.open functionฉันสับสนจริงๆด้วย เมื่อฉัน:

file = codecs.open("temp", "w", "utf-8")
file.write(codecs.BOM_UTF8)
file.close()

มันทำให้ฉันมีข้อผิดพลาด

UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xef ในตำแหน่ง 0: ลำดับไม่อยู่ในช่วง (128)

ถ้าฉันทำ:

file = open("temp", "w")
file.write(codecs.BOM_UTF8)
file.close()

มันใช้งานได้ดี

คำถามคือทำไมวิธีแรกล้มเหลว และฉันจะแทรก bom ได้อย่างไร?

หากวิธีที่สองเป็นวิธีที่ถูกต้องในการทำสิ่งที่ใช้codecs.open(filename, "w", "utf-8")?


54
อย่าใช้ BOM ใน UTF-8 กรุณา.
tchrist

7
@tchrist Huh? ทำไมจะไม่ล่ะ?
Salman von Abbas

8
@SalmanPK BOM ไม่จำเป็นต้องใช้ใน UTF-8 และเพิ่มความซับซ้อนเท่านั้น (เช่นคุณไม่สามารถต่อไฟล์ BOM'd และผลลัพธ์ที่ได้ด้วยข้อความที่ถูกต้อง) ดูคำถาม & คำตอบนี้ ; อย่าพลาดความคิดเห็นที่ยิ่งใหญ่ภายใต้ Q
Alois Mahdal

คำตอบ:


271

ฉันเชื่อว่าปัญหาคือcodecs.BOM_UTF8สตริงไบต์ไม่ใช่สตริง Unicode ฉันสงสัยว่าตัวจัดการไฟล์พยายามคาดเดาสิ่งที่คุณหมายถึงจริง ๆ โดยอ้างอิงจาก "ฉันตั้งใจจะเขียน Unicode เป็นข้อความที่เข้ารหัส UTF-8 แต่คุณได้ให้สตริงไบต์!"

ลองเขียนสตริง Unicode สำหรับเครื่องหมายคำสั่งไบต์ (เช่น Unicode U + FEFF) โดยตรงเพื่อให้ไฟล์เพิ่งเข้ารหัสว่าเป็น UTF-8:

import codecs

file = codecs.open("lol", "w", "utf-8")
file.write(u'\ufeff')
file.close()

(ดูเหมือนว่าจะให้คำตอบที่ถูกต้อง - ไฟล์ที่มีไบต์ EF BB BF)

แก้ไข: S. Lott แนะนำให้ใช้ "utf-8-sig" เนื่องจากการเข้ารหัสเป็นวิธีที่ดีกว่าการเขียน BOM อย่างชัดเจน แต่ฉันจะทิ้งคำตอบไว้ที่นี่เพราะจะอธิบายว่าเกิดอะไรขึ้นก่อนหน้านี้


คำเตือน: เปิดและเปิดไม่เหมือนกัน ถ้าคุณทำ "จาก codecs import open" มันจะไม่เหมือนกับที่คุณพิมพ์ "open"
Apache

2
คุณยังสามารถใช้ codecs.open ('test.txt', 'w', 'utf-8-sig') แทน
ปิดเบต้าเมื่อ

1
ฉันได้รับ "TypeError: ต้องการจำนวนเต็ม (มีประเภท str)" ฉันไม่เข้าใจว่าเราทำอะไรที่นี่ มีคนช่วยได้ไหม ฉันต้องการผนวกสตริง (ย่อหน้า) ลงในไฟล์ข้อความ ฉันต้องแปลงมันเป็นจำนวนเต็มก่อนเขียนหรือไม่?
Mugen

@Mugen: รหัสที่แน่นอนที่ฉันเขียนทำงานได้ดีเท่าที่ฉันเห็น ฉันขอแนะนำให้คุณถามคำถามใหม่ที่แสดงว่าคุณได้รับรหัสใดและเกิดข้อผิดพลาดที่ไหน
Jon Skeet

@Mugen คุณต้องโทรหาcodecs.openแทนแค่open
Northben

179

อ่านสิ่งต่อไปนี้: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

ทำเช่นนี้

with codecs.open("test_output", "w", "utf-8-sig") as temp:
    temp.write("hi mom\n")
    temp.write(u"This has ♭")

ไฟล์ผลลัพธ์คือ UTF-8 พร้อม BOM ที่คาดไว้


2
ขอบคุณ ใช้งานได้ (Windows 7 x64, Python 2.7.5 x64) โซลูชันนี้ทำงานได้ดีเมื่อคุณเปิดไฟล์ในโหมด "a" (ผนวก)
Mohamad Fakih

สิ่งนี้ไม่ได้ผลสำหรับฉัน Python 3 บน Windows ฉันต้องทำสิ่งนี้แทนด้วย open (file_name, 'wb') เป็น bomfile: bomfile.write (codecs.BOM_UTF8) จากนั้นเปิดไฟล์อีกครั้งเพื่อผนวก
ดัสตินแอนดรู

อาจจะเพิ่มtemp.close()?
user2905353

2
@ user2905353: ไม่จำเป็น นี้จะถูกจัดการโดยการจัดการบริบทopenของ
matheburg

11

@ S-Lott ให้ขั้นตอนที่ถูกต้อง แต่การขยายในประเด็นUnicode , Python interpreter สามารถให้ข้อมูลเชิงลึกมากขึ้น

Jon Skeet ถูกต้อง (ผิดปกติ) เกี่ยวกับcodecsโมดูล - มันมีสตริงไบต์:

>>> import codecs
>>> codecs.BOM
'\xff\xfe'
>>> codecs.BOM_UTF8
'\xef\xbb\xbf'
>>> 

การเลือก nit อีกอันBOMจะมีชื่อUnicodeมาตรฐานและสามารถป้อนเป็น:

>>> bom= u"\N{ZERO WIDTH NO-BREAK SPACE}"
>>> bom
u'\ufeff'

นอกจากนี้ยังสามารถเข้าถึงได้ผ่านunicodedata:

>>> import unicodedata
>>> unicodedata.lookup('ZERO WIDTH NO-BREAK SPACE')
u'\ufeff'
>>> 

8

ฉันใช้คำสั่ง file * nix เพื่อแปลงไฟล์ charset ที่ไม่รู้จักในไฟล์ utf-8

# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()

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