Python删除额外的特殊Unicode字符

4
我正在使用Python处理一些文本,它已经是Unicode格式的内部表示,但我想要去掉一些特殊字符并将它们替换为更标准的版本。
目前我有一行代码看起来像这样,但它变得越来越复杂,我预见到它最终会带来更多麻烦。
tmp = infile.lower().replace(u"\u2018", "'").replace(u"\u2019", "'").replace(u"\u2013", "").replace(u"\u2026", "")

例如,u\2018 和 \u2019 是左单引号 left 和右单引号 right。虽然这些有些可以接受,但是对于这种文本处理,我认为它们是不必要的。
this u\2013 破折号和 this 水平省略号之类的东西绝对不需要。
是否有一种方法可以删除那些引号并使用简单的标准引号,而不会破坏使用'nltk'进行文本处理,并删除诸如 EN DASH、HORIZONTAL ELLIPSIS 之类的内容,而不会使像上面示例代码中开始出现的庞大调用?

你是如何获取这段文本的?你使用了 decode() 吗?当你使用 print() 时,你能看到这段代码吗? - furas
5个回答

7
如果您的文本是英语,并想以易读的方式进行清理,请使用第三方模块unidecode。它将广泛的字符替换为它们最接近的ascii似类。只需对任何字符串应用unidecode.unidecode()即可进行替换:
from unidecode import unidecode
clean = unidecode(u'Some text: \u2018\u2019\u2013\u03a9')

1
统一的解决方案是使用预定义的替换对字典。这样的字典可以很容易地扩展(修改)。使用re.compile和re.sub函数的解决方案:
import re

d = {
    u"\u2018" : "'", u"\u2019" : "'", u"\u2013" : "", u"\u2026" : ""
}

pattern = re.compile(r'(' + '|'.join(re.escape(k) for k in d.keys()) + ')')
replaced = pattern.sub(lambda c: d[c.group()], infile.lower())

通过在编译模式之前使用re.escape()转义d中的键,使此方法更加健壮。否则,例如将条目".": ""添加到d中将删除整个输入。 - lenz

1
如果您需要进行这种字符“规范化”,可以考虑实现编解码器注册表编解码器
实现方式与@RomanPerekhrest提出的类似,使用替换字符表。
实现编解码器:
导入codecs模块,为您的编解码器命名(避免使用现有名称)。 创建编码表(在执行u"something".encode(...)时使用的表):
import codecs

NAME = "normalize"

_ENCODING_TABLE = {
    u'\u2002': u' ',
    u'\u2003': u' ',
    u'\u2004': u' ',
    u'\u2005': u' ',
    u'\u2006': u' ',
    u'\u2010': u'-',
    u'\u2011': u'-',
    u'\u2012': u'-',
    u'\u2013': u'-',
    u'\u2014': u'-',
    u'\u2015': u'-',
    u'\u2018': u"'",
    u'\u2019': u"'",
    u'\u201a': u"'",
    u'\u201b': u"'",
    u'\u201c': u'"',
    u'\u201d': u'"',
    u'\u201e': u'"',
    u'\u201f': u'"',
    }

上面的表格可以“规范化”空格、连字符和引号。这就是规范化规则所在的地方...
然后,实现用于规范化字符串的函数:
def normalize_encode(input, errors='strict'):
    output = u''
    for char in input:
        output += _ENCODING_TABLE.get(char, char)
    return output, len(input)

你也可以实现解码,但需要反转 _ENCODING_TABLE,最佳实践是准备反转表格,稍后填充缺失字符。
_DECODING_TABLE = {v: k for k, v in _ENCODING_TABLE.items()}
# missing characters...

def normalize_decode(input, errors='strict'):
    output = u''
    for char in input:
        output += _DECODING_TABLE.get(char, char)
    return output, len(input)

现在,一切准备就绪,您可以实现编解码协议:
class Codec(codecs.Codec):
    def encode(self, input, errors='strict'):
        return normalize_encode(input, errors)

    def decode(self, input, errors='strict'):
        return normalize_decode(input, errors)


class IncrementalEncoder(codecs.IncrementalEncoder):
    def encode(self, input, final=False):
        assert self.errors == 'strict'
        return normalize_encode(input, self.errors)[0]


class IncrementalDecoder(codecs.IncrementalDecoder):
    def decode(self, input, final=False):
        assert self.errors == 'strict'
        return normalize_decode(input, self.errors)[0]


class StreamWriter(Codec, codecs.StreamWriter):
    pass


class StreamReader(Codec, codecs.StreamReader):
    pass


def getregentry():
    return codecs.CodecInfo(name=NAME,
                            encode=normalize_encode,
                            decode=normalize_decode,
                            incrementalencoder=IncrementalEncoder,
                            incrementaldecoder=IncrementalDecoder,
                            streamreader=StreamReader,
                            streamwriter=StreamWriter)

如何注册新创建的编解码器?
如果您有几个标准化编解码器,最佳实践是将它们收集在专用包的__init__.py文件中(例如:my_app.encodings)。
# -*- coding: utf-8 -*-
import codecs

import normalize


def search_function(encoding):
    if encoding == normalize.NAME:
        return normalize.getregentry()
    return None


# Register the search_function in the Python codec registry
codecs.register(search_function)

每当您需要使用编解码器时,您会写下以下内容:

import my_app.encodings

normalize = my_app.encodings.normalize.NAME

def my_function():
    normalized = my_string.encode(normalize)

过度杀伤。使用translate - Mark Tolonen
重复造轮子。使用unidecode而不是临时替换表。 - alexis

0
使用内置的字符串方法 translate。它使用Unicode序数字典作为键并将其翻译为值,这些值可以是Unicode序数、字符串或None。值None删除字符:
sample = '\u2018hello\u2019\u2013there\u2026'
print(sample)
replacements = { 0x2018 : "'",
                 0x2019 : "'",
                 0x2013 : '-',
                 0x2026 : '...' }
print(sample.translate(replacements))

输出:

‘hello’–there…
'hello'-there...

0

re.sub也可以实现这个功能:

import re
tmp = re.sub(u'\u2019|\u2018', '\'', infile.lower())
tmp = re.sub(u'\u2013|\u2026', '', tmp)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接