2015-03-27 4 views
3

Я читаю из текстового файла, который содержит фунт знаки (Е):Python регулярного выражения £ знак

f = open(file, 'r') 
string = f.read() 
f.close() 

Наряду с некоторыми другими операциями регулярных выражений, я хочу, чтобы удалить эти знаки фунта, и записать строку новый файл. Ближайший я должен сделать эту работу следующий код:

n = re.compile(unichr(163)) 
string = n.sub('', string) 

Это, кажется, правильно найти признаки фунт, но вместо того, чтобы заменить их ничем, то £ превращают этот символ: Â

Кто-нибудь знает, что происходит?

+3

Вы должны быть очень конкретными в кодировании при работе со специальными символами. Убедитесь, что вы знаете кодировку файла, который вы открываете, тот, который вы пишете, и свой собственный код. Если вы используете Python 2.x, пожалуйста, отметьте свой вопрос соответствующим образом, так как обработка кодировки отличается в двух версиях. – Cilyan

+0

используйте 'codecs.open' вместо open, а затем просто' string.replace (u '£', '') ', здесь нет необходимости в regex. – wim

+0

@alessadro: кодировка исходного кода Python имеет * ничего *, чтобы сделать это. – jfs

ответ

2

Резюме:

В utf8, £ карты в сырых байт \xc2\xa3. Модуль re допускает замену строк между кодировкой unicode и байтом, что является ошибкой.

Это мое мнение, что answer Я. Ф. Себастьяна является более сукцином, но вот пошаговое руководство.

Детали:

звонки в read() возвращение байтовой строки.

Чтобы проиллюстрировать это, давайте создадим следующий файл durp:

echo -n "£" > durp 

Следующая команда получает содержимое файла в шестнадцатеричном:

$ cat durp | xxd | cut -d " " -f 2 
c2a3 

Примечание: Посещение этого url будет отображаться £ в нескольких кодировках.

Это необработанные байты, которые составляют £. Что делает python с файлом при его чтении?

$ python 
> f = open("durp") 
> f.read() 
'\xc2\xa3' 

Он не знает, что такое кодировка, поэтому он представляет байты в их экранированной гексагоне.

Давайте импортировать код:

> import re 
> r = re.compile(u'£') 
> u'£' 
u'\xa3' 

Это последняя строка просто посмотреть, что мы делаем рисунок на. Это источник ошибки.

Теперь мы выполняем замену на содержимое файла:

> r.sub('', '\xc2\xa3') 
'\xc2' 

Что возможно, но не так. Мы заменили '\xa3' на номер '' в '\xc2\xa3' и получили '\xc2'. Это ошибка в re, потому что строки Юникода смешиваются с байтами. Не имеет смысла выполнять подстановки символов, которые имеют разные кодировки. Это по существу заменяет байты, а не символы.

answer J.F. Sebastian объясняет, как ваш терминал будет интерпретировать '\xc2' как Â.

+0

это неправильно или, в лучшем случае, вводит в заблуждение. Наличие 'unichr' указывает на то, что OP использует Python 2.' read() 'возвращает байтовую последовательность (последовательность байтов) в этом случае - никакой тип unicode в картине нигде (вы можете записать * что-либо * в файл и 'read()' будет работать в любом случае). Если вы видите '' '' 'при печати двух * байтов *' print b '\ xc2 \ xa3'' означает, что ваш терминал/консоль использует 'cp1252' или аналогичную кодировку символов (я бы увидел' '', потому что мой терминал использует utf -8 символов). [Реальная проблема заключается в том, что OP смешивает unicode и байты, которые выдает ошибку в модуле 're'] (http://goo.gl/kczhRe). – jfs

+0

@ J.F.Sebastian Я думаю, что мой ответ полезен в прохождении кода OP и ошибки. У меня было несколько ошибок, которые, я считаю, я исправил. Ваше сообщение кратким и демонстрирует лучшее понимание юникода в Python. Реквизит. – cdosborn

+0

Лучше, но это все еще вводит в заблуждение. Все байты в байтовой строке - это просто байты. Не имеет значения для вопроса, являются ли некоторые из них в диапазоне ascii - ваша путаница возникает из-за того, что «rep (bytestring)» Python показывает некоторые байты (из класса «graph») в качестве символов, например, 'b '\ x22 \ x20 \ x22'' обычно отображается как 'b '" "'' - примечание: литералы (константы в исходном коде Python) различаются, но байты одинаковы - они состоят из одной и той же последовательности байтов: 34 , 32, 34 (более заметным в Python 3, где индексирование объекта 'bytes' возвращает Python' int': 'b'a '[0] == 97' – jfs

0

Проблема заключается в том, что вы смешиваете 8-битные строки и полные строки Unicode. @cdosborn дал отличное описание, как это привело к частичному замене символов.

В Python> 2.x существует два способа удержания текста: строки и строки Unicode. Строки могут содержать текст , закодированный в простых ASCII, ANSI, Windows-1252, UTF-8, UTF-16. Проблема в том, что вам должны знать, в какой кодировке находится текст, если вам нужно его преобразовать. Строки Unicode на руке абсолютно однозначны, так как они являются результатом явного преобразования из строки с использованием известной кодировки с использованием escape-кодов Unicode (u "\ u00A3") или таких функций, как unichr().

Лучшая практика - всегда декодировать строки в Юникоде при вводе кода. Затем закодируйте на выходе. Это поведение по умолчанию для Python 3.x и других языков, таких как Java.

Если вы работаете с файлами, то codecs модуль обеспечивает хороший способ для преобразования текста в строки Unicode на пути в:

my_file = codecs.open("filename.txt", "r", "utf-8") 
my_unicode_string = my_file.read() 

Очевидно, что если ваш файл находится в другой кодировке, изменить utf-8 для имени кодирования - См имена кодеков: https://docs.python.org/2/library/codecs.html#standard-encodings

Если вы имеете дело со строками из других (STDIN, WebForms), конвертировать с помощью:

my_unicode_string = "my €uro sign in utf-8".decode("utf-8") 

Опять же, изменить utf-8 аргумент соответственно

После того, как у вас есть строка Unicode, вы можете использовать его, как вы хотите. Для того, чтобы сделать простой поиск и замену для знака фунта сделать:

my_unicode_string.replace(unichr(163), "") 

Чтобы сделать код более удобным для чтения, вы можете кодировать исходный код в UTF-8 и объявить кодировку. Это означает, что вам не нужно скрывать символы Unicode в escape-последовательностях или ординалах.

Сведя в целом:

# -*- coding: utf-8 -*- 
my_file = codecs.open("filename.txt", "r", "utf-8") 
my_unicode_string = my_file.read() 
replaced_unicode_string = my_unicode_string.replace("£", "") 

Теперь, если вы хотите написать свой replaced_unicode_string в другой файл:

my_output_file = codecs.open("another_filename.txt", "w", "utf-8") 
my_output_file.write(replaced_unicode_string) 
+0

, это' codecs', а не 'codec' и вы должны [использовать 'io' модуль в любом случае] (http://stackoverflow.com/a/29317102/4279). – jfs

1

Это ошибка в re модуля в Python 2, что позволяет mix unicode pattern и inputtestring: он бесшумно кодирует шаблон с использованием кодировки latin-1, что приводит к неправильному результату. Python 3 правильно поднимает TypeError здесь.

>>> u'\N{POUND SIGN}'.encode('latin-1') 
'\xa3' 
>>> u'\N{POUND SIGN}'.encode('utf-8')                  
'\xc2\xa3' 
>>> import re 
>>> re.sub(u'\N{POUND SIGN}', '', u'\N{POUND SIGN}'.encode('utf-8'))          
'\xc2' 
>>> print(re.sub(u'\N{POUND SIGN}', '', u'\N{POUND SIGN}'.encode('utf-8')).decode('cp1252'))    
 
>>> print(re.sub(u'\N{POUND SIGN}', '', u'x\N{POUND SIGN}y')) 
xy 

Решение использовать Unicode как для шаблона и строки ввода:

import io 

with io.open('file.txt', encoding='utf-8') as file: 
    result = file.read().replace(u'\N{POUND SIGN}', '') 

codecs модули не обрабатывает универсальные символы новой строки правильно, используйте io модуль вместо этого. Встроенная функция open() в Python 3 - io.open().

Смежные вопросы