如何使用Python 3查找/替换非可打印/非ASCII字符?

4
我有一个文件,其中一些行中的某些字段包含奇怪的字符,导致数据库导入出现问题。
我已经搜索过了Python 3中如何替换非ASCII字符的文章,但没有任何作用。
当我在vi中打开文件并执行:set list命令时,发现在一个行末尾有一个$符号,而在下一行开头有^I^I。这两行应该合并成为一行,并且不应该有^I符号。我知道$代表行末'\n',并尝试替换它们,但没有效果。
我不知道^I代表什么,可能是制表符。
我尝试了以下函数,但无济于事:
def remove_non_ascii(text):
    new_text = re.sub(r"[\n\t\r]", "", text)
    new_text = ''.join(new_text.split("\n"))
    new_text = ''.join([i if ord(i) < 128 else ' ' for i in new_text])
    new_text = "".join([x for x in new_text if ord(x) < 128])
    new_text = re.sub(r'[^\x00-\x7F]+', ' ', new_text)
    new_text = new_text.rstrip('\r\n')
    new_text = new_text.strip('\n')
    new_text = new_text.strip('\r')
    new_text = new_text.strip('\t')
    new_text = new_text.replace('\n', '')
    new_text = new_text.replace('\r', '')
    new_text = new_text.replace('\t', '')
    new_text = filter(lambda x: x in string.printable, new_text)
    new_text = "".join(list(new_text))

    return new_text

有没有一种工具可以准确地显示出问题字符,并找到替换它的方法?

我是这样打开文件的(.csv 文件以 UTF-8 格式保存)

f_csv_in = open(csv_in, "r", encoding="utf-8")

下面有两行文字,其中非ASCII字符会出现问题。

这两行应该是一行。注意第37行末尾的$符号和第38行以^I^I开头。

问题的一部分是vi显示了一个新的换行符$在第37行,而我不希望它出现在那里。这应该是一行。

37 Cancelled,01-19-17,,basket,00-00-00,00-00-00,,,,98533,SingleSource,,,17035 Cherry Hill Dr,"L/o 1-19-17 @ 11:45am$
38 ^I^IVictorville",SAN BERNARDINO,CA,92395,,,,,0,,,,,Lock:6111 ,,,No,No,,0.00,0.00,No,01-19-17,0.00,0.00,,01-19-17,00-00-00,,provider,,,Unread,00-00-00,,$

如果您使用的是Windows系统,^ 用于转义字符。而 $ 通常表示行末,例如在正则表达式中。您的文件是否为“管道分隔符”,前两个字段为空?您使用的编辑器是什么?您能否禁用格式字符的显示? - hyperTrashPanda
文件中的字段是逗号分隔的。文件中有150K行,除了81行有问题外,其余都正常。当我在IDE中打开时,这81行中的每一行都会被分成两行(请参见上面的37、38)。上面的示例给出了两个应该是一个的这样的行。如果任何字段为空,那没关系,在逗号(分隔符)之间没有任何内容。我不确定禁用格式字符的显示意味着什么。 - user10664542
我想删除任何转义字符,比如^I,并且我还想在第37行上方移除结尾处的新行$字符。如果有代码可以移除^I转义字符和行中间的新行字符(例如第37行),以便最终第37行和38行成为一行,请分享。我尝试了原帖中列出的所有选项来删除它们。 - user10664542
4个回答

3

一种简单的去除非ASCII字符的方法是进行以下操作:

new_text = "".join([c for c in text if c.isascii()])

重要提示:如果您从文件中阅读此文本,请确保使用正确的编码方式阅读。

我尝试了那段代码片段,但没有成功。这个 .csv 文件被保存为 UTF-8,我是这样打开它的。f_csv_in = open(csv_in, "r") - user10664542
尝试使用 f_csv_in = open(csv_in, "r", encoding='utf-8') - olinox14
到目前为止,使用encoding='utf-8'打开文件没有成功。在.csv文件的150K行中,有162行包含某些字符会导致程序出现问题,其余近150K行都是正常的。 - user10664542
你使用的是Linux吗?如果你运行file csv_in,那么报告的编码是什么? - hyperTrashPanda
我正在Windows下使用cygwin。我在cygwin下使用Python。该文件是使用Excel保存为UTF-8 .csv格式的。在Python中打开时,使用encoding='utf-8'编码。 - user10664542

2
对于不可打印字符,内置的字符串模块有一些过滤掉非可打印或非 ASCII 字符的方法,例如使用 isprintable() 功能。
下面介绍了一种过滤整个字符串的简洁方法。
>>> import string
>>>
>>> str1 = '\nsomestring'
>>> str1.isprintable()
False
>>> str2 = 'otherstring'
>>> str2.isprintable()
True
>>>
>>> res = filter(lambda x: x in string.printable, '\x01mystring')
>>> "".join(list(res))
'mystring'

这个问题在 SO 上过去曾经有一些讨论,但是有很多方法可以做到这一点,所以我理解这可能会令人困惑,因为你可以使用从正则表达式到str.translate()的任何东西。
另外一个可以做的事情是查看Unicode 类别,并根据您需要的符号集过滤出您的数据。

根据您的评论,我在该方法中添加了这两行代码:remove_non_ascii(text),但没有成功。 new_text = filter(lambda x: x in string.printable, new_text) new_text = "".join(list(new_text)) - user10664542
你能在另一个文本编辑器中打开你的文件吗?字符还在吗?你能提供几个实际数据的示例行吗? - hyperTrashPanda
我可以在文本编辑器中打开文件,也可以在vi中打开它。当我在vi中输入:set list时,我看到行尾有一个$符号,这是不应该有的。下面一行以^I(控制I)开头,连续出现两次。这两行应该合并为一行,第一行末尾没有$符号,整行中也没有^I^I。我可以提供一行来进行操作,但如何提供呢? - user10664542
Vi应该在键入“:set list”以表示行结尾时显示“$”,因此这不是错误。那里没有任何“真正”的字符需要删除,这只是“vi”样式的行结束符。至于“^ |”,您的CSV文件由什么分隔符分隔呢? - hyperTrashPanda
以下是两行应该合并在一起的非ASCII字符。这两行应该合并在一起。请注意第37行末尾的$,以及第38行以^I^I开头。 Vi显示的问题是,在我不想要它的第37行上有一个新行$。应该合并为一行。```37 Cancelled,01-19-17,,basket,00-00-00,00-00-00,,,,98533,SingleSource,,,17035 Cherry Hill Dr,"L/o 1-19-17 @ 11:45am$ 38 ^I^IVictorville",SAN BERNARDINO,CA,92395,,,,,0,,,,,Lock:6111 ,,,No,No,,0.00,0.00,No,01-19-17,0.00,0.00,,01-19-17,00-00-00,,provider,,,Unread,00-00-00,,$``` - user10664542
似乎什么都不起作用。我不确定这些错误行中有什么会导致问题。似乎没有任何东西能够替换行中间的换行符或者^I控制字符。没有Python函数可以解决这个问题。我已经尝试了数十种不同的方法。 - user10664542

0

另一种使用repython过滤非可打印ASCII字符的方法:

 import re
 import string
 string_with_printable = re.sub(f'[^{re.escape(string.printable)}]', '', original_string) 

re.escape函数将给定模式中的特殊字符进行转义。


0

看起来你有一个包含引号值的csv文件,即嵌入逗号或换行符等值必须用引号括起来,以便csv读取器正确处理它们。

如果您查看示例数据,可以看到第一行末尾有一个开头双引号但没有结束双引号,第二行有一个结束双引号但没有开头双引号,表明引号包含具有嵌入换行符的值。

行被分成两部分的事实可能是查看它们的应用程序或处理它们的代码的产物:如果软件不理解csv引用,它将假定每个换行符表示新行。

目前不清楚这在数据库中造成了什么问题,但很可能引号字符(特别是不匹配的引号)可能会导致问题,特别是如果在插入之前未正确转义数据。

此代码段重写文件,删除嵌入的逗号、换行符和制表符,并指示编写器不引用任何值。如果找到需要转义的值,则会出现错误消息_csv.Error: need to escape, but no escapechar set。根据您的数据,您可能需要调整正则表达式模式。

with open('lines.csv') as f, open('fixed.csv', 'w') as out:
    reader = csv.reader(f)
    writer = csv.writer(out, quoting=csv.QUOTE_NONE)
    for line in reader:
        new_row = [re.sub(r'\t|\n|,', ' ', x) for x in line]
        writer.writerow(new_row)

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