在python中过滤掉某些字节

在python中过滤掉某些字节,python,xml,text,unicode,lxml,Python,Xml,Text,Unicode,Lxml,我在python程序中遇到此错误:ValueError:所有字符串必须与XML兼容:Unicode或ASCII,没有空字节或控制字符 这个问题,解释了这个问题 解决方案是过滤掉某些字节,但我不知道该怎么做 有什么帮助吗 编辑:如果我没有提供足够的关于这个问题的信息,很抱歉。字符串数据来自外部api查询,我无法控制该查询的数据格式。正如对链接问题的回答所述,XML标准将有效字符定义为: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFF

我在python程序中遇到此错误:
ValueError:所有字符串必须与XML兼容:Unicode或ASCII,没有空字节或控制字符

这个问题,解释了这个问题

解决方案是过滤掉某些字节,但我不知道该怎么做

有什么帮助吗


编辑:如果我没有提供足够的关于这个问题的信息,很抱歉。字符串数据来自外部api查询,我无法控制该查询的数据格式。

正如对链接问题的回答所述,XML标准将有效字符定义为:

Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
将其转换为Python:

def valid_xml_char_ordinal(c):
    codepoint = ord(c)
    # conditions ordered by presumed frequency
    return (
        0x20 <= codepoint <= 0xD7FF or
        codepoint in (0x9, 0xA, 0xD) or
        0xE000 <= codepoint <= 0xFFFD or
        0x10000 <= codepoint <= 0x10FFFF
        )

您可以参考本网站上的解决方案:

这个解决方案对我有效。你也可能需要考虑John Machin的解决方案。
祝你好运

我认为这太苛刻/过分了,速度似乎慢得令人痛苦,但我的程序仍然很快,在努力理解出了什么问题后(即使在我尝试实现@John的cleaned_字符串实现之后),我只是使用以下(Python 2.7)修改了他的答案,以清除ASCII不可打印:


我不确定我对更好的选择做错了什么,但我只是想继续

另一种比上述答案快得多的方法是使用正则表达式,如下所示:

re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', text)
与上面的答案相比,我的测试速度快了10倍多:

import timeit

func_test = """
def valid_xml_char_ordinal(c):
    codepoint = ord(c)
    # conditions ordered by presumed frequency
    return (
        0x20 <= codepoint <= 0xD7FF or
        codepoint in (0x9, 0xA, 0xD) or
        0xE000 <= codepoint <= 0xFFFD or
        0x10000 <= codepoint <= 0x10FFFF
    );
''.join(c for c in r.content if valid_xml_char_ordinal(c))
"""

func_setup = """
import requests; 
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""

regex_test = """re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', r.content)"""
regex_setup = """
import requests, re; 
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""

func_test = timeit.Timer(func_test, setup=func_setup)
regex_test = timeit.Timer(regex_test, setup=regex_setup)

print func_test.timeit(100)
print regex_test.timeit(100)
因此,有意义的是,我们正在做的是下载这个网页一次(你当前正在阅读的页面),然后在其内容上运行函数技术和正则表达式技术各100倍

使用函数法大约需要2.6秒。
使用regex方法大约需要0.2


更新:如注释中所示,此答案中的正则表达式先前删除了一些字符,这在XML中应该是允许的。这些字符包括了世界上所有的文字,包括楔形文字、象形文字和(奇怪的)表情符号等古代文字

上面是正确的正则表达式。将来对此的快速测试将使用
re.DEBUG
,它将打印:

In [52]: re.compile(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', re.DEBUG)
max_repeat 1 4294967295
  in
    negate None
    range (32, 55295)
    literal 9
    literal 10
    literal 13
    range (57344, 65533)
    range (65536, 1114111)
Out[52]: re.compile(ur'[^ -\ud7ff\t\n\r\ue000-\ufffd\U00010000-\U0010ffff]+', re.DEBUG)
我为这个错误道歉。我只能说,我在别处找到了这个答案,并把它放在这里。这是别人的错误,但我传播了它。我向任何受此影响的人表示诚挚的歉意


更新22017-12-12:我从一些OSX用户那里了解到,这段代码在所谓的Python狭义版本上不起作用,而OSX有时显然会这样做。您可以通过运行
import sys;sys.maxunicode
。如果它打印65535,那么这里的代码在您安装“宽版本”之前无法工作

你是否也有你所指问题中输入的随机数据?这看起来可能有用。不幸的是,数据来自外部api,很少抛出错误。所以我无法重现导致错误的条件。我必须等到情况相同后才能测试这个解决方案。感谢您的回复,我对python还比较陌生,了解您的解决方案的实现(以及它如何解决我的问题)将使我有机会更好地理解该语言。@y3di:在您等待错误再次发生时,您可能希望更改代码以捕获错误,并将有问题的XML文档写入日志文件。这样,您就可以准确地找出非法字符是什么,并决定是删除它们还是替换其他字符更合适。在任何情况下,你的问题(实际上:如何从XML文档中过滤出非法字符)已经被回答,并且你应该考虑接受它(点击答案左边的“滴答”)。出于属性的目的,我只是在这里插入了一个要点,其中有一个代码,用于从字符串中过滤这个字符,因为我最近不得不使用它。谢谢你的回答。我发现lxml.builder.E不接受字符“\xa9”,但函数没有过滤掉它
从lxml.builder导入E
E('a','\xa9')
值错误:所有字符串必须是XML兼容的:Unicode或ASCII,没有空字节或控制字符
'.join(c表示'\xa9'中的c,如果有效的话)
'\xa9'您可以使用
字节。translate()
删除/替换不在
字符串中的字节。可打印
。与@John Machin的解决方案不同;它不适用于任意Unicode文本。您确定正则表达式按预期工作吗?我相信最后两个
F
字母没有被解析为
\u
转义的一部分。在python控制台中试试这个:
print(u'\u10ffff')
然后这个:
print(u'\u10ffzz')
你是对的,如果删除代码点在65536和1114111之间的unicode,肯定会导致数据擦除。我已经更新了答案。这会抛出一个错误
radars\u string=re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+,'',radars\u string)文件“/System/libraries/Python.framework/Versions/2.7/lib/python2.7/re.py”,第155行,在sub返回(模式、标志)文件中“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py”,第251行,在_compileraise error中,v#无效的表达式sre_常量。错误:错误的字符范围
我怀疑您可能有一个“窄”的Python构建?不是正的,但很容易检查使用宽的构建是否修复了问题:链接已断开
import timeit

func_test = """
def valid_xml_char_ordinal(c):
    codepoint = ord(c)
    # conditions ordered by presumed frequency
    return (
        0x20 <= codepoint <= 0xD7FF or
        codepoint in (0x9, 0xA, 0xD) or
        0xE000 <= codepoint <= 0xFFFD or
        0x10000 <= codepoint <= 0x10FFFF
    );
''.join(c for c in r.content if valid_xml_char_ordinal(c))
"""

func_setup = """
import requests; 
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""

regex_test = """re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', r.content)"""
regex_setup = """
import requests, re; 
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""

func_test = timeit.Timer(func_test, setup=func_setup)
regex_test = timeit.Timer(regex_test, setup=regex_setup)

print func_test.timeit(100)
print regex_test.timeit(100)
> 2.63773989677
> 0.221401929855
In [52]: re.compile(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', re.DEBUG)
max_repeat 1 4294967295
  in
    negate None
    range (32, 55295)
    literal 9
    literal 10
    literal 13
    range (57344, 65533)
    range (65536, 1114111)
Out[52]: re.compile(ur'[^ -\ud7ff\t\n\r\ue000-\ufffd\U00010000-\U0010ffff]+', re.DEBUG)