การอ่านไฟล์ UTF8 CSV ด้วย Python


94

ฉันกำลังพยายามอ่านไฟล์ CSV ที่มีอักขระเน้นเสียงด้วย Python (เฉพาะอักษรฝรั่งเศสและ / หรือสเปน) จากเอกสาร Python 2.5 สำหรับ csvreader ( http://docs.python.org/library/csv.html ) ฉันคิดรหัสต่อไปนี้เพื่ออ่านไฟล์ CSV เนื่องจาก csvreader รองรับเฉพาะ ASCII

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')

filename = 'output.csv'
reader = unicode_csv_reader(open(filename))
try:
    products = []
    for field1, field2, field3 in reader:
        ...

ด้านล่างนี้เป็นไฟล์ CSV ที่ฉันพยายามอ่าน:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert
...

แม้ว่าฉันจะพยายามเข้ารหัส / ถอดรหัสเป็น UTF-8 แต่ฉันก็ยังคงได้รับข้อยกเว้นต่อไปนี้:

Traceback (most recent call last):
  File ".\Test.py", line 53, in <module>
    for field1, field2, field3 in reader:
  File ".\Test.py", line 40, in unicode_csv_reader
    for row in csv_reader:
  File ".\Test.py", line 46, in utf_8_encoder
    yield line.encode('utf-8', 'ignore')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 68: ordinal not in range(128)

ฉันจะแก้ไขปัญหานี้ได้อย่างไร


มาร์ตินถ้าคุณอยู่ใกล้ ๆ คุณจะพิจารณาเปลี่ยนคำตอบที่ยอมรับจากคำตอบ Python 2 ของ Martelli
Antti Haapala

คำตอบ:


114

.encodeวิธีการได้รับนำไปใช้กับสายอักขระ Unicode จะทำให้ไบต์สตริง; แต่คุณเรียกมันด้วยไบต์สตริงแทน ... ผิดทาง! ดูcodecsโมดูลในไลบรารีมาตรฐานและcodecs.openโดยเฉพาะอย่างยิ่งสำหรับโซลูชันทั่วไปที่ดีกว่าสำหรับการอ่านไฟล์ข้อความที่เข้ารหัส UTF-8 อย่างไรก็ตามสำหรับcsvโมดูลโดยเฉพาะคุณต้องส่งผ่านข้อมูล utf-8 และนั่นคือสิ่งที่คุณได้รับอยู่แล้วดังนั้นรหัสของคุณจึงง่ายกว่ามาก:

import csv

def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
    csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
    for row in csv_reader:
        yield [unicode(cell, 'utf-8') for cell in row]

filename = 'da.csv'
reader = unicode_csv_reader(open(filename))
for field1, field2, field3 in reader:
  print field1, field2, field3 

PS: หากปรากฎว่าข้อมูลอินพุตของคุณไม่อยู่ใน utf-8 แต่เช่นใน ISO-8859-1 คุณจำเป็นต้องมี "การแปลงรหัส" (หากคุณต้องการใช้ utf-8 ในcsvระดับโมดูล) ของรูปแบบline.decode('whateverweirdcodec').encode('utf-8')- แต่คุณสามารถใช้ชื่อของการเข้ารหัสที่มีอยู่ของคุณในyieldบรรทัดในโค้ดของฉันด้านบนแทนได้'utf-8'ตามที่csvเป็นจริงจะใช้ได้ดีกับ bytestrings ที่เข้ารหัส ISO-8859- *


4
นี่หมายความว่าตัวอย่างใน python docs (ที่ OP copy & pasteed มาจากไหน) ผิดหรือเปล่า? อะไรคือจุดสำคัญของขั้นตอนการเข้ารหัสพิเศษที่จะทำหากมันแตกเมื่อคุณให้ Unicode csv
Anentropic


84

Python 2.X

มีไลบรารีunicode-csvที่ควรแก้ปัญหาของคุณพร้อมประโยชน์เพิ่มเติมในการไม่ต้องเขียนโค้ดที่เกี่ยวข้องกับ csv ใหม่

นี่คือตัวอย่างจาก readme:

>>> import unicodecsv
>>> from cStringIO import StringIO
>>> f = StringIO()
>>> w = unicodecsv.writer(f, encoding='utf-8')
>>> w.writerow((u'é', u'ñ'))
>>> f.seek(0)
>>> r = unicodecsv.reader(f, encoding='utf-8')
>>> row = r.next()
>>> print row[0], row[1]
é ñ

Python 3.X

ใน python 3 สิ่งนี้ได้รับการสนับสนุนนอกกรอบโดยcsvโมดูลbuild-in ดูตัวอย่างนี้:

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

10

หากคุณต้องการอ่านไฟล์ CSV ที่มีการเข้ารหัส utf-8 วิธีง่ายๆที่ฉันแนะนำให้คุณใช้สิ่งนี้:

with open(file_name, encoding="utf8") as csv_file:

ด้วยคำสั่งดังกล่าวคุณสามารถใช้โปรแกรมอ่าน CSV ในภายหลังเพื่อใช้งานได้


2
เป็นไปได้ไหมว่านี่คือ Python 3 เท่านั้น? มันล้มเหลวสำหรับฉันใน Python 2 ไม่ยอมรับencodingในopen
Zvika

@Zvika ใช่ใน python 3 วิธีนี้ใช้ได้:open('file.csv', 'r', encoding="ISO8859")
luca76

ฉันจะเพิ่ม open (file_name, "rt", encoding = 'utf-8') นั่นคือเปิดไฟล์ในโหมด "อ่านข้อความ"
Jimmy Lee Jones

3

ตรวจสอบคำตอบในโพสต์นี้ด้วย: https://stackoverflow.com/a/9347871/1338557

แนะนำให้ใช้ไลบรารีชื่อ ucsv.py การแทนที่แบบสั้นและง่ายสำหรับ CSV ที่เขียนขึ้นเพื่อแก้ไขปัญหาการเข้ารหัส (utf-8) สำหรับ Python 2.7 ยังให้การสนับสนุนสำหรับ csv.DictReader

แก้ไข : การเพิ่มโค้ดตัวอย่างที่ฉันใช้:

import ucsv as csv

#Read CSV file containing the right tags to produce
fileObj = open('awol_title_strings.csv', 'rb')
dictReader = csv.DictReader(fileObj, fieldnames = ['titles', 'tags'], delimiter = ',', quotechar = '"')
#Build a dictionary from the CSV file-> {<string>:<tags to produce>}
titleStringsDict = dict()
for row in dictReader:
    titleStringsDict.update({unicode(row['titles']):unicode(row['tags'])})

คุณควรใส่รายละเอียดบางส่วนของลิงก์นั้นในคำตอบของคุณในกรณีที่ลิงก์เสีย \
Yaje

# Downvoter- ไม่แน่ใจว่าทำไมคุณถึงคิดว่าไม่มีประโยชน์ ห้องสมุด ucsv ใช้งานได้ดีสำหรับฉัน ช่วยแก้ไขข้อผิดพลาด unicde ที่ฉันดิ้นรนมาตั้งแต่ 2 วัน หากคุณกำลังมองหาโค้ดตัวอย่างอยู่ที่นี่แก้ไข @ Yaje- ฉันให้รายละเอียดบางอย่าง โค้ดตัวอย่างด้วย และแก้ไขลิงก์ด้วยเช่นกันซึ่งก่อนหน้านี้ชี้ไปที่โพสต์อื่น
Atripavan

เหตุผลพิเศษใดที่คุณเปิดไฟล์ข้อความเป็นไบนารี? 'rb' ใช้สำหรับเปิดไฟล์ไบนารี
Codeguy007

2

การใช้codecs.openตามที่ Alex Martelli แนะนำพิสูจน์แล้วว่ามีประโยชน์สำหรับฉัน

import codecs

delimiter = ';'
reader = codecs.open("your_filename.csv", 'r', encoding='utf-8')
for line in reader:
    row = line.split(delimiter)
    # do something with your row ...

3
มันใช้ไม่ได้กับ CSV ทั้งหมดต่อไปนี้เป็นแถว csv ที่ถูกต้อง: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.

คุณนำเข้าcsvโมดูล แต่ไม่ได้ใช้
Christophe Roussy

1

ลิงก์ไปยังหน้าช่วยเหลือจะเหมือนกันสำหรับ python 2.6 และเท่าที่ฉันรู้ว่าไม่มีการเปลี่ยนแปลงในโมดูล csv ตั้งแต่ 2.5 (นอกเหนือจากการแก้ไขข้อบกพร่อง) นี่คือรหัสที่ใช้งานได้โดยไม่ต้องเข้ารหัส / ถอดรหัสใด ๆ (ไฟล์ da.csv มีข้อมูลเดียวกับข้อมูลตัวแปร) ฉันถือว่าไฟล์ของคุณควรอ่านได้อย่างถูกต้องโดยไม่มีการแปลงใด ๆ

test.py:

## -*- coding: utf-8 -*-
#
# NOTE: this first line is important for the version b) read from a string(unicode) variable
#

import csv

data = \
"""0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert"""

# a) read from a file
print 'reading from a file:'
for (f1, f2, f3) in csv.reader(open('da.csv'), dialect=csv.excel):
    print (f1, f2, f3)

# b) read from a string(unicode) variable
print 'reading from a list of strings:'
reader = csv.reader(data.split('\n'), dialect=csv.excel)
for (f1, f2, f3) in reader:
    print (f1, f2, f3)

da.csv:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert

ฉันสงสัยว่างูหลามรุ่นนี้จะใช้งานได้กับอะไร ฉันได้รับข้อผิดพลาดทั้ง 2.7 และ 3.5 "ValueError: มีค่าไม่เพียงพอที่จะแกะ (คาดว่า 3, ได้ 1)"
eis

@eis: ฉันนึกได้ว่าในระบบของคุณลูกน้ำไม่ใช่ตัวคั่นเริ่มต้น พยายามที่จะเพิ่มแทนdelimiter=',' dialect=csv.excel
รถตู้

1

ที่น่าสังเกตว่าหากคุณไม่ได้ผลคุณอาจลืมที่จะหลีกหนีเส้นทางของคุณ
ตัวอย่างเช่นรหัสนี้:

f = open("C:\Some\Path\To\file.csv")

จะส่งผลให้เกิดข้อผิดพลาด:

SyntaxError: (ข้อผิดพลาดของ Unicode) ตัวแปลงสัญญาณ 'unicodeescape' ไม่สามารถถอดรหัสไบต์ในตำแหน่ง 2-3: การหลีกเลี่ยง \ UXXXXXXXX ที่ถูกตัดทอน

ในการแก้ไขให้ทำดังนี้

f = open("C:\\Some\\Path\\To\\file.csv")

0

เมื่อมองไปที่Latin-1ตารางยูนิโคดฉันเห็นรหัสอักขระ00E9" LATIN SMALL LETTER E WITH ACUTE " นี่คืออักขระเน้นเสียงในข้อมูลตัวอย่างของคุณ การทดสอบอย่างง่ายPythonแสดงให้เห็นว่าการUTF-8เข้ารหัสสำหรับอักขระนี้แตกต่างจากการUTF-16เข้ารหัสUnicode (เกือบ)

>>> u'\u00e9'
u'\xe9'
>>> u'\u00e9'.encode('utf-8')
'\xc3\xa9'
>>> 

ผมขอแนะนำให้คุณพยายามที่จะencode("UTF-8")ข้อมูล Unicode unicode_csv_reader()ก่อนที่จะเรียกพิเศษ เพียงแค่อ่านข้อมูลจากไฟล์อาจซ่อนการเข้ารหัสดังนั้นตรวจสอบค่าอักขระจริง


0

มีปัญหาเดียวกันบนเซิร์ฟเวอร์อื่น แต่ตระหนักว่าโลแคลมีปัญหา

export LC_ALL="en_US.UTF-8"

แก้ไขปัญหา

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