Когда вы используете
fh = codecs.open(fname,'r','utf8')
fh.read()
возвращает юникод. Если вы возьмете этот юникод и используете драйвер своей базы данных (например, mysql-python) для вставки данных в свою базу данных, тогда драйвер отвечает за преобразование юникода в байты. Драйвер использует кодировку, установленную
con.set_character_set('utf8')
Если вы используете
fh = open(fname, 'r')
тогда fh.read()
возвращает строку байтов. Вы находитесь во власти тех байтов, которые оказались в fname
. К счастью, согласно вашему сообщению, файл закодирован в UTF-8. Поскольку данные уже являются строкой байтов, драйвер не выполняет никакого кодирования, а просто передает строку байтов в базу данных.
В любом случае одна и та же строка байтов в кодировке UTF-8 вставляется в базу данных.
Давайте посмотрим на исходный код, определяющий codecs.open:
def open(filename, mode='rb', encoding=None, errors='strict', buffering=1):
if encoding is not None:
if 'U' in mode:
# No automatic conversion of '\n' is done on reading and writing
mode = mode.strip().replace('U', '')
if mode[:1] not in set('rwa'):
mode = 'r' + mode
if 'b' not in mode:
# Force opening of the file in binary mode
mode = mode + 'b'
file = __builtin__.open(filename, mode, buffering)
if encoding is None:
return file
info = lookup(encoding)
srw = StreamReaderWriter(file, info.streamreader, info.streamwriter, errors)
# Add attributes to simplify introspection
srw.encoding = encoding
return srw
Обратите внимание, в частности, на то, что происходит, если encoding
не установлен:
file = __builtin__.open(filename, mode, buffering)
if encoding is None:
return file
Таким образом, codecs.open
по сути то же самое, что и встроенный open
, когда кодировка не установлена. Встроенная функция open
возвращает файловый объект, read
метод которого возвращает объект str. Он вообще не выполняет декодирование.
Напротив, когда вы указываете кодировку, codecs.open
возвращает StreamReaderWriter
с srw.encoding
, установленным на encoding
. Теперь, когда вы вызываете read
метод StreamReaderWriter
, возвращается объект unicode - обычно. Сначала необходимо декодировать объект str с использованием указанной кодировки.
В вашем примере объект str
In [19]: content
Out[19]: '\xe2\x80\x9cThank you.\xe2\x80\x9d'
и если вы укажете кодировку как 'ascii'
, тогда StreamReaderWriter
попытается декодировать content
, используя кодировку 'ascii'
:
In [20]: content.decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)
Это неудивительно, поскольку кодировка ascii
может декодировать только байты в диапазоне 0–127, а '\xe2'
, первый байт в content
, имеет порядковое значение за пределами этого диапазона.
Для конкретности: Если вы не указываете кодировку:
In [13]: with codecs.open(filename, 'r') as f:
....: content = f.read()
In [14]: content
Out[14]: '\xe2\x80\x9cThank you.\xe2\x80\x9d'
content
is a str
.
Если вы укажете допустимую кодировку:
In [22]: with codecs.open(filename, 'r', encoding = 'utf-8') as f:
....: content = f.read()
In [23]: content
Out[23]: u'\u201cThank you.\u201d'
content
is a unicode
.
Если вы указали недопустимую кодировку:
In [25]: with codecs.open(filename, 'r', 'ascii') as f:
....: content = f.read()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)
Вы получите UnicodeDecodeError
.
person
unutbu
schedule
19.01.2013