某些字符的Regex冲突(ISO-8859-1 Windows-1252)



all-我正试图对一堆科学数据执行正则表达式,将某些特殊符号转换为ASCII友好字符。例如,我想将"µ"(UTF-8 \xc2\xb5(替换为字符串"micro",并将"±"替换为"+/-"。我编写了一个python脚本来做这件事,看起来像这样:

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

然而,对于这两个特定的角色,我得到了奇怪的结果。我深入研究了一下,看起来我受到了这里描述的错误的困扰,其中某些字符出现了错误,因为它们是被解释为Windows-1252(或ISO 8859-1(的UTF数据。

我查看了相关数据,发现它也返回了错误的结果(例如,"µ"显示为"µ"(。然而,在同一数据集中的其他地方,存在正确显示同一符号的基准。这可能是由于最初收集数据的系统中存在错误。真正奇怪的是,我当前的代码似乎只捕获了不正确的版本,而让正确的版本通过。

无论如何,我真的很纠结如何继续。我需要能够想出一系列正则表达式替换,这些替换将捕获这些字符的正确版本和不正确版本,但在这种情况下,正确版本的标识符失败了。

我必须承认,我对编程还是相当初级的,除了最基本的正则表达式之外,任何东西对我来说都像黑魔法。这个问题似乎比我以前必须解决的任何问题都要棘手一些,这就是为什么我把它带到这里来更多地关注它。

谢谢!

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

import re
def stripChars(s):
s = (re.sub(r'xc2xb5', 'micro', s)) # micro
s = (re.sub(r'xc2xb1', '+/-', 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)

如果您的数据以其他方式编码(例如,请参阅这个版本会更有用。你可以为输入和输出指定任何编码。它解码为内部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,但您可能不会注意到未捕获的字符。你可以抓住例外,做一些优雅的事。或者你可以让程序崩溃并为您丢失的角色更新它。

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

这与你后来所说的一致:

我当前的代码只捕获不正确的版本,让正确的版本通过

事实上,这是完全正确的。让我们先来看看µ究竟发生了什么。µ是u'u00b5',在utf-8中编码为'xc2xb5',在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('xc2xb5','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('xc2xb1','+/-') #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中根本不存在。

最新更新