Python 子流程调用因web表单输入中的unicode数据而失败-可用于其他地方的相同输入
我有一个库,其中包含一个函数,该函数根据多个regexp检查各种输入数据,以确保数据有效。对于CGI脚本从web表单(通过lighttpd)接收到的输入,以及从sqlite数据库读取的输入(该输入根据gammu smsd接收到的SMS依次放在那里),都会调用该函数 输入有时用英语,有时用印地语,即德夫纳加里语。应始终使用UTF-8对其进行编码。我一直在努力使用python的re和regex模块,在将字符类与Devnagari字符正确匹配时,这些模块似乎存在缺陷(您可以看到一个示例——在这种情况下,使用regex而不是重新修复问题,但我也遇到了regex的问题)。命令行“grep”看起来更加可靠和准确。因此,我求助于使用子流程调用将必要的字符串通过管道传输到grep,如下所示:Python 子流程调用因web表单输入中的unicode数据而失败-可用于其他地方的相同输入,python,unicode,cgi,subprocess,lighttpd,Python,Unicode,Cgi,Subprocess,Lighttpd,我有一个库,其中包含一个函数,该函数根据多个regexp检查各种输入数据,以确保数据有效。对于CGI脚本从web表单(通过lighttpd)接收到的输入,以及从sqlite数据库读取的输入(该输入根据gammu smsd接收到的SMS依次放在那里),都会调用该函数 输入有时用英语,有时用印地语,即德夫纳加里语。应始终使用UTF-8对其进行编码。我一直在努力使用python的re和regex模块,在将字符类与Devnagari字符正确匹配时,这些模块似乎存在缺陷(您可以看到一个示例——在这种情况下
def invalidfield(datarecord,msgtype):
for fieldname in datarecord:
if (msgtype,fieldname) in mainconf["MSG_FORMAT"]:
try:
check = subprocess.check_output("echo '" + datarecord[fieldname] + "' | grep -E '" + mainconf["MSG_FORMAT"][msgtype,fieldname] + "'",shell=True)
except subprocess.CalledProcessError:
return fieldname
return None
现在,让我们用以下字符串作为输入来尝试一下:न्याज अहमद्
和下面的正则表达式进行检查:^[[:alnum:][.]*[:alnum:][.[:alnum:][.]*$
奇怪的是,当从数据库中读取时,完全相同的输入会清除这个regexp(正如它应该的那样),并且函数会正确返回。但是,当通过webform输入相同的字符串时,subprocess.check_out将失败,并出现以下错误:
File "/usr/lib/python2.7/subprocess.py", line 537, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1259, in _execute_child
raise child_exception
TypeError: execv() arg 2 must contain only strings
我搞不懂发生了什么事。我已经用它修改了lighttpd.conf,至少应该确保lighttpd.conf使用utf-8字符集。我还使用了该模块,并对来自webform的输入运行chardet.detect。我知道:{'confidence':1.0,'encoding':'ascii'}{'confidence':0.99,'encoding':'utf-8'}
根据这一点,我试着用unicode(datarecord[fieldname])替换上面的datarecord[fieldname]
。编码('utf8'),并且第一次尝试用“ascii”编解码器解码datarecord[fieldname]。后者失败,通常出现“序号不在范围内”错误
这里出了什么问题?我就是搞不懂 在这种情况下,您希望避免使用
echo
;将输入直接写入Popen()
对象的stdin
管道
请确保您的环境设置为正确的语言环境,以便grep
知道如何将输入解析为UTF-8:
env = dict(os.environ)
env['LC_ALL'] = 'en_US.UTF-8'
p = subprocess.Popen(['grep', '-E', mainconf["MSG_FORMAT"][msgtype,fieldname]], stdin=subprocess.PIPE, env=env)
p.communicate(datarecord[fieldname])
if p.returncode:
return fieldname
为了补充Martijn Pieters的答案,他建议的解决方案在输入字符串为空的情况下将失败(与原始函数不同,grep将无法匹配空字符串,即使regexp允许它)。因此,原始功能的完整实现将是:
if (msgtype,fieldname) in mainconf["MSG_FORMAT"]:
if not datarecord[fieldname]:
if not regex.search(mainconf["MSG_FORMAT"][msgtype,fieldname],datarecord[fieldname],regex.UNICODE):
return fieldname
else:
curenv = os.environ
curenv['LC_ALL']="en_US.UTF-8"
check = subprocess.Popen(['grep','-E', mainconf["MSG_FORMAT"][msgtype,fieldname]], stdin=subprocess.PIPE, env=curenv, stderr=subprocess.STDOUT,stdout=subprocess.PIPE)
check.communicate (datarecord[fieldname])
if check.returncode:
return fieldname
return None
这就像
regex
匹配可以在空字符串上很好地工作。打印repr(datarecord[fieldname])给你什么?它将是datarecord[fieldname].encode('utf8')
(无需将unicode转换为unicode)。因为这是一个CGI脚本,我不得不要求它写出str(repr(datarecord[fieldname]))
到文件中。我知道:'\xe0\xa4\xa8\xe0\xa5\x8d\xe0\xa4\xaf\xe0\xa4\xbe\xe0\xa4\x9c\xe0\xa4\x85\xe0\xa4\xb9\xe0\xa4\xae\xe0\xa4\xa6\xe0\xa5\x8d'并使用数据记录[fieldname]。编码('utf8')也不起作用……如果必须使用grep-E
,为什么不将数据写入grep
子流程的stdin
管道,而不是使用echo
和shell=True
。对不起,我错了-这实际上不起作用。我的代码中有一个bug(我仍然在检查CalledProcessError,Popen没有提出这个问题)。当我重写代码时,我发现了两件事——首先是文件句柄有问题,之后grep一直无法匹配,即使它从命令行匹配。其他人似乎报告了Popen.communicate的编码问题(例如,请参阅,但对我来说,这个解决方案也没有什么好运气。您对unicode编码和Python的理解有多深?有帮助吗?您需要将编码与grep
所期望的匹配,这可能是基于LC
环境变量系列。我已经阅读了文档,但我承认我仍然对py中的unicode感到困惑。)我使用reload(sys);sys.setdefaultencoding('utf-8'))
在程序开始时,以及来自uuu future\uuuuu导入unicode\u文字的,以尝试强制统一编码。我在开始时遇到了许多问题,但大多数问题似乎已经解决。这是唯一剩下的问题。更奇怪的是,它可以处理来自sms数据库的数据,但不能处理来自CGI表单的数据。相信我,这是非常令人困惑和恼火的。:setdefaultencoding()
应该永远都不需要;这只是表明您在某个地方以不应该的方式混合了字节和unicode。可能吧。我使用它来确保CGI脚本的输出始终正确编码(而不是在每个输出上手动强制使用utf8,因为它的大部分都在Devnagari中)。