Đọc tệp CSV UTF8 bằng Python


93

Tôi đang cố đọc tệp CSV có các ký tự có dấu bằng Python (chỉ các ký tự tiếng Pháp và / hoặc tiếng Tây Ban Nha). Dựa trên tài liệu Python 2.5 cho csvreader ( http://docs.python.org/library/csv.html ), tôi đã nghĩ ra mã sau để đọc tệp CSV vì csvreader chỉ hỗ trợ 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:
        ...

Dưới đây là phần trích xuất của tệp CSV mà tôi đang cố đọc:

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

Mặc dù tôi cố gắng mã hóa / giải mã thành UTF-8, tôi vẫn nhận được ngoại lệ sau:

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)

Làm cách nào để sửa lỗi này?


Martin, nếu bạn đang ở đó, bạn có cân nhắc chuyển câu trả lời được chấp nhận từ câu trả lời duy nhất trong Python 2 của Martelli không.
Antti Haapala

Câu trả lời:


113

Các .encodephương pháp được áp dụng cho một chuỗi Unicode để tạo ra một byte-string; nhưng bạn đang gọi nó trên chuỗi byte thay vì ... sai cách 'vòng! Xem codecsmô-đun trong thư viện tiêu chuẩn và codecs.opennói riêng để biết các giải pháp chung tốt hơn để đọc các tệp văn bản được mã hóa UTF-8. Tuy nhiên, đối với csvmô-đun nói riêng, bạn cần chuyển dữ liệu utf-8 và đó là những gì bạn đã nhận được, vì vậy mã của bạn có thể đơn giản hơn nhiều:

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 

Tái bút: nếu hóa ra dữ liệu đầu vào của bạn KHÔNG ở utf-8, mà là ở ISO-8859-1, thì bạn cần "chuyển mã" (nếu bạn muốn sử dụng utf-8 ở csvcấp mô-đun) , có dạng line.decode('whateverweirdcodec').encode('utf-8')- nhưng có lẽ bạn chỉ có thể sử dụng tên của mã hóa hiện có của mình trong yielddòng trong mã của tôi ở trên, thay vì 'utf-8', như csvthực tế sẽ ổn với các chuỗi ký tự mã hóa ISO-8859- *.


4
Điều này có nghĩa là ví dụ trong tài liệu python (nơi sao chép & dán từ OP) là sai? Điểm của bước mã hóa bổ sung mà nó thực hiện là gì nếu nó bị hỏng khi bạn cung cấp cho nó một csv unicode?
Anentropic


80

Python 2.X

Có một thư viện unicode-csv sẽ giải quyết các vấn đề của bạn, với lợi ích bổ sung là không cần viết bất kỳ mã mới nào liên quan đến csv.

Đây là một ví dụ từ readme của họ:

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

Trong python 3, điều này được hỗ trợ ra khỏi hộp bởi csvmô-đun tích hợp. Xem ví dụ này:

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

8

Nếu bạn muốn đọc Tệp CSV có mã hóa utf-8, một cách tiếp cận tối giản mà tôi khuyên bạn nên sử dụng là sử dụng một cái gì đó như sau:

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

Với tuyên bố đó, bạn có thể sử dụng trình đọc CSV để làm việc sau này.


2
Có thể đây chỉ là Python 3? Nó không thành công đối với tôi, bằng Python 2. Nó không chấp nhận encodingtrongopen
Zvika

@Zvika có, trong python 3 giải pháp này hoạt động:open('file.csv', 'r', encoding="ISO8859")
luca76

Tôi cũng sẽ thêm open (file_name, "rt", encoding = 'utf-8'), tức là mở tệp ở chế độ "đọc văn bản"
Jimmy Lee Jones

3

Ngoài ra, hãy xem câu trả lời trong bài đăng này: https://stackoverflow.com/a/9347871/1338557

Nó gợi ý sử dụng thư viện có tên ucsv.py. Thay thế ngắn và đơn giản cho CSV được viết để giải quyết vấn đề mã hóa (utf-8) cho Python 2.7. Cũng cung cấp hỗ trợ cho csv.DictReader

Chỉnh sửa : Thêm mã mẫu mà tôi đã sử dụng:

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'])})

bạn nên đặt một số chi tiết của liên kết trong câu trả lời của bạn, chỉ trong trường hợp liên kết bị phá vỡ đi \
Yaje

# Downvoter- Không rõ tại sao bạn nghĩ nó không có ích gì. Thư viện ucsv hoạt động tốt đối với tôi. Đã giúp giải quyết lỗi unicde mà tôi đã gặp phải từ 2 ngày nay. Nếu bạn đang tìm kiếm một số mã mẫu, thì đây là phần chỉnh sửa @ Yaje- Tôi đã đưa ra một số chi tiết; cũng là mã mẫu. Và cũng sửa lại liên kết, đó là trước đó trỏ đến một số bài đăng khác.
Atripavan

Bất kỳ lý do cụ thể nào khiến bạn mở tệp văn bản dưới dạng tệp nhị phân? 'rb' là để mở các tệp nhị phân.
Codeguy007

2

Việc sử dụng codecs.opennhư Alex Martelli đề xuất tỏ ra hữu ích đối với tôi.

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
Nó sẽ không hoạt động với tất cả CSV, sau đây là hàng csv hợp lệ: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.

Bạn nhập csvmô-đun nhưng không sử dụng nó.
Christophe Roussy,

1

Liên kết đến trang trợ giúp giống nhau đối với python 2.6 và theo như tôi biết thì không có thay đổi nào trong mô-đun csv kể từ 2.5 (ngoài các bản sửa lỗi). Đây là mã chỉ hoạt động mà không cần bất kỳ mã hóa / giải mã nào (tệp da.csv chứa cùng dữ liệu với dữ liệu biến ). Tôi cho rằng tệp của bạn phải được đọc chính xác mà không có bất kỳ chuyển đổi nào.

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

Tôi tự hỏi phiên bản này sẽ hoạt động trong phiên bản python nào? Tôi gặp lỗi với cả 2.7 và 3.5. "ValueError: không đủ giá trị để giải nén (dự kiến ​​3, có 1)"
eis

@eis: Tôi có thể tưởng tượng rằng trên hệ thống của bạn, dấu phẩy không phải là dấu phân cách mặc định. Cố gắng thêm delimiter=','thay vì dialect=csv.excel.
van

1

Cần lưu ý rằng nếu không có gì hiệu quả với bạn, bạn có thể đã quên thoát khỏi con đường của mình.
Ví dụ, mã này:

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

Sẽ dẫn đến lỗi:

SyntaxError: (unicode error) codec 'unicodeescape' không thể giải mã các byte ở vị trí 2-3: truncated \ UXXXXXXXX Escape

Để khắc phục, chỉ cần làm:

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

0

Nhìn vào Latin-1bảng unicode , tôi thấy mã ký tự 00E9" LATIN NHỎ CHỮ E CÓ ACUTE ". Đây là ký tự có dấu trong dữ liệu mẫu của bạn. Một thử nghiệm đơn giản Pythoncho thấy rằng UTF-8mã hóa cho ký tự này khác với UTF-16mã hóa unicode (gần như ).

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

Tôi khuyên bạn nên thử encode("UTF-8")dữ liệu unicode trước khi gọi đặc biệt unicode_csv_reader(). Chỉ đọc dữ liệu từ tệp có thể ẩn mã hóa, vì vậy hãy kiểm tra các giá trị ký tự thực tế.


0

Gặp sự cố tương tự trên một máy chủ khác, nhưng nhận ra rằng ngôn ngữ bị nhầm lẫn.

export LC_ALL="en_US.UTF-8"

đã khắc phục sự cố

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.