Python 3: os.walk () пути к файлам UnicodeEncodeError: кодек 'utf-8' не может кодировать: суррогаты не разрешены

этот код:

for root, dirs, files in os.walk('.'):
    print(root)

дает мне эту ошибку:

UnicodeEncodeError: 'utf-8' codec can't encode character 'udcc3' in position 27: surrogates not allowed

Как пройти через дерево файлов, не получая токсичных строк, как это?

3 ответов


в Linux имена файлов являются "просто кучей байтов" и не обязательно кодируются в определенной кодировке. Python 3 пытается превратить все в строки Unicode. При этом разработчики придумали схему перевода байтовых строк в строки Unicode и обратно без потерь и без знания исходной кодировки. Они использовали частичные суррогаты для кодирования "плохих" байтов, но обычный кодировщик UTF8 не может обрабатывать их при печати на терминал.

например, вот не-UTF8 байтовая строка:

>>> b'C\xc3N'.decode('utf8','surrogateescape')
'C\udcc3N'

он может быть преобразован в и из Unicode без потерь:

>>> b'C\xc3N'.decode('utf8','surrogateescape').encode('utf8','surrogateescape')
b'C\xc3N'

но его нельзя напечатать:

>>> print(b'C\xc3N'.decode('utf8','surrogateescape'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 1: surrogates not allowed

вам придется выяснить, что вы хотите сделать с именами файлов с кодировками не по умолчанию. Возможно, просто кодирует их обратно в исходные байты и декодирует их с неизвестной заменой. Используйте это для отображения, но сохраните исходное имя для доступа к файлу.

>>> b'C\xc3N'.decode('utf8','replace')
C�N

os.walk может также принять byte string и вернет байтовые строки вместо строк Unicode:

for p,d,f in os.walk(b'.'):

тогда вы можете декодировать, как вам нравится.


Я закончил тем, что передал строку байта в os.walk() который, по-видимому, вернет строки байтов вместо неправильных строк unicode

for root, dirs, files in os.walk(b'.'):
    print(root)

С sed или grep:

set | sed -n '/^[a-zA-Z0-9_]*=/p'
# ... or ...
set | grep '^[a-zA-Z0-9_]*='
# ... or ...
set | egrep '^[_[:alnum:]]+='

это чувствительно к тому, насколько сумасшедшие имена переменных. Последняя версия должна справиться с большинством сумасшедших вещей.