Почему конкатенация строк Python работает с русским текстом, а string.format() - нет

Я пытаюсь проанализировать (и избежать) строки файла CSV, который хранится в кодировке символов Windows-1251. Используя этот превосходный ответ, чтобы справиться с этой кодировкой, я закончил этой строкой для проверки вывода, по некоторым причинам это работает:

print(row[0]+','+row[1])

Выведение:

Тяжелый Уборщик Обязанности,1 литр

Пока эта строка не работает:

print("{0},{1}".format(*row))

Вывод этой ошибки:

Name,Variant

Traceback (most recent call last):
  File "Russian.py", line 26, in <module>
    print("{0},{1}".format(*row))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 2-3: ordinal not in range(128)

Вот первые 2 строки CSV:

Name,Variant
Тяжелый Уборщик Обязанности,1 литр

и в случае, если это поможет, вот полный источник Russian.py:

import csv
import cgi
from chardet.universaldetector import UniversalDetector
chardet_detector = UniversalDetector()

def charset_detect(f, chunk_size=4096):
    global chardet_detector
    chardet_detector.reset()
    while 1:
        chunk = f.read(chunk_size)
        if not chunk: break
        chardet_detector.feed(chunk)
        if chardet_detector.done: break
    chardet_detector.close()
    return chardet_detector.result

with open('Russian.csv') as csv_file:
    cd_result = charset_detect(csv_file)
    encoding = cd_result['encoding']
    csv_file.seek(0)
    csv_reader = csv.reader(csv_file)
    for bytes_row in csv_reader:
        row = [x.decode(encoding) for x in bytes_row]
        if len(row) >= 6:
            #print(row[0]+','+row[1])
            print("{0},{1}".format(*row))

3 ответа

Решение

Строки в вашем списке, вероятно, уже были в Unicode, поэтому у вас не возникло проблем.

print(row[0]+','+row[1])
Тяжелый Уборщик Обязанности,1 литр

Но здесь мы пытаемся добавить юникод к нормальной строке! Вот почему вы получаете UnicodeEncodeError,

print("{0},{1}".format(*row))

Так что просто измените его на:

print(u"{0}, {1}".format(*row))

Ты используешь str.format() который преобразует unicode() в str() неявно. Это необходимо сделать для того, чтобы можно было интерполировать значения в предоставленный шаблон.

использование unicode.format() вместо:

print(u"{0},{1}".format(*row))

Обратите внимание u перед форматным литералом. unicode.format() должен декодировать str входы, чтобы вписаться в результирующий вывод Unicode.

Конкатенация, с другой стороны, может неявно декодировать для получения окончательного unicode() результат объекта. Если бы ваш ',' значение содержит не-ASCII байты, что неявное декодирование также завершится ошибкой.

Мораль истории: используйте строковые литералы Unicode во всем коде при обработке текста.

+ операнд прекрасно работает между unicode строка и str строка. С другой стороны, str.format не принимает unicode строки в качестве параметров.

Таким образом, вы можете просто заменить проблемную строку на следующую:

print(u"{0},{1}".format(*row))

Это должно делать свое дело.

Другие вопросы по тегам