某些字符的正则表达式冲突(ISO-8859-1 Windows-1252)

时间:2018-07-18 07:31:41

标签: python regex text ascii iso-8859-1

全部-我正在尝试对一堆科学数据执行正则表达式,将某些特殊符号转换为ASCII友好字符。例如,我想将'µ'(UTF-8 \ xc2 \ xb5)替换为字符串'micro',并将'±'替换为'+/-'。我准备了一个python脚本来执行此操作,如下所示:

import re
def stripChars(string):
    outString = (re.sub(r'\xc2\xb5+','micro', string)) #Metric 'micro (10^-6)' (Greek 'mu') letter
    outString = (re.sub(r'\xc2\xb1+','+/-', outString)) #Scientific 'Plus-Minus' symbol
    return outString

但是,对于这两个特定字符,我得到了奇怪的结果。我对其进行了深入研究,似乎遇到了described here错误,其中某些字符出现错误,因为它们是被解释为Windows-1252(或ISO 8859-1)的UTF数据。

我抓取了相关数据,发现它也在那里返回了错误的结果(例如,“ µ”显示为“ µ”)。但是,在同一数据集中的其他地方,存在的数据中相同的符号是正确显示。这可能是由于系统中最初收集数据的错误所致。真正的怪异之处在于,看来我当前的代码只捕获了错误的版本,让正确的代码通过了。

无论如何,我真的对如何继续深感困惑。我需要能够提出一系列的正则表达式替换,以同时捕获这些字符的正确和错误版本,但是在这种情况下,正确版本的标识符将失败。

我必须承认,我对编程还是还很初级,除了最基本的正则表达式外,其他任何东西对我来说仍然像魔术。这个问题似乎比我以前必须解决的问题更加棘手,这就是为什么我将它带到这里来引起更多关注。

谢谢!

2 个答案:

答案 0 :(得分:2)

如果您的输入数据编码为UTF-8,则您的代码应该可以工作。这是一个 适用于我的完整程序。假设输入为UTF-8, 只需对原始字节进行操作,而不会与Unicode进行相互转换。 请注意,我从每个输入正则表达式的末尾删除了+;那 会接受一个或多个最后一个字符,您可能会 没打算。

import re

def stripChars(s):
    s = (re.sub(r'\xc2\xb5', 'micro', s)) # micro
    s = (re.sub(r'\xc2\xb1', '+/-', s)) # plus-or-minus
    return s

f_in = open('data')
f_out = open('output', 'w')

for line in f_in:
    print(type(line))
    line = stripChars(line)
    f_out.write(line)

如果您的数据以其他方式进行编码(例如,参见this question进行了解),则此版本会更有用。您可以 指定输入和输出的任何编码。解码为内部 读取时为Unicode,替换时对其进行操作,然后对 写作。

import codecs
import re

encoding_in = 'iso8859-1'
encoding_out = 'ascii'

def stripChars(s):
    s = (re.sub(u'\u00B5', 'micro', s)) # micro
    s = (re.sub(u'\u00B1', '+/-', s)) # plus-or-minus
    return s

f_in = codecs.open('data-8859', 'r', encoding_in)
f_out = codecs.open('output', 'w', encoding_out)

for uline in f_in:
    uline = stripChars(uline)
    f_out.write(uline)

请注意,如果尝试写入非ASCII数据,它将引发异常 使用ASCII编码。避免这种情况的简单方法是编写 UTF-8,但是您可能不会注意到未捕获的字符。你可以抓住 例外,做点优雅的事。或者你可以让程序 崩溃并针对您缺少的角色进行更新。

答案 1 :(得分:1)

好吧,当您使用Python2版本时,您将文件读取为字节字符串,并且您的代码应成功转换µ(U + 00B5)或±的所有utf-8编码版本( U + 00B1)。

这与您稍后所说的一致:

  

我当前的代码仅捕获了不正确的版本,从而使正确的代码通过了

这实际上是完全正确的。让我们首先看一下µ到底发生了什么。 µ是u'\u00b5',它在utf-8中编码为'\xc2\xb5',在Latin1或cp1252中编码为'\xb5'。由于'Â'为U + 00C2,因此其Latin1或cp1252代码为0xc2。这意味着在Windows 1252系统中,以utf-8正确编码的µ字符将读为µ。当它看起来正确时,是因为它不是utf-8编码的,而是Latin1编码的。

您似乎正在尝试处理文件,其中部分是utf-8编码,而其他部分是Latin1(或cp1252)编码。您确实应该尝试在正在收集数据的系统中修复该问题,因为它可能导致难以恢复故障。

好消息是,可以在此处将其修复,因为您只想处理2个非ASCII字符:您只需要像尝试那样尝试解码utf-8版本,然后尝试第二遍即可解码Latin1版本。代码可能是(这里需要正则表达式):

def stripChars(string):
    outString = string.replace('\xc2\xb5','micro') #Metric 'micro (10^-6)' (Greek 'mu') letter in utf-8
    outString = outString.replace('\xb5','micro') #Metric 'micro (10^-6)' (Greek 'mu') letter in Latin1
    outString = outString.replace('\xc2\xb1','+/-') #Scientific 'Plus-Minus' symbol in utf-8
    outString = outString.replace('\xb1','+/-') #Scientific 'Plus-Minus' symbol in Latin1
    return outString

供参考,Latin1 AKA ISO-8859-1编码具有低于256的所有unicode字符的确切unicode值。窗口代码页1252(Python中的cp1252)是Latin1编码的Windows变体,其中通常在Latin1中未使用的某些字符是用于更高的代码字符。例如,(U + 20AC)在cp1252中被编码为'\80',而在Latin1中则根本不存在。