子进程调用使用来自Web表单输入的unicode数据失败 - 使用来自其他地方的相同输入

时间:2013-03-24 11:43:09

标签: python unicode cgi subprocess lighttpd

我有一个库,其中包含一个函数,该函数根据多个正则表达式检查各种输入数据,以确保数据有效。该函数既可以通过CGI脚本从Web表单(通过lighttpd)接收输入,也可以从sqlite数据库读取输入(根据gammu-smsd收到的SMS,将输入放在那里)。 p>

输入有时是英语,有时是印地语,即Devnagari脚本。它应始终使用UTF-8编码。我一直在努力使用python的re和regex模块,这对于将字符类正确匹配到Devnagari字符似乎是错误的(你可以看到一个例子here - 在这种情况下使用正则表达式而不是重新修复问题,但我从那以后也遇到了正则表达式的麻烦)。命令行'grep'显得更加可靠和准确。因此,我已经使用子进程调用将必要的字符串传递给grep,如下所示:

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:] .]*$

奇怪的是,当从数据库中读取完全相同的输入时,清除此正则表达式(应该如此)并且函数正确返回。但是当通过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

我无法弄清楚发生了什么。我使用this script修改了我的lighttpd.conf,至少应该确保lighttpd.conf使用utf-8字符集。我还使用了chardet模块并对webform的输入运行了chardet.detect。我明白了:{'confidence': 1.0, 'encoding': 'ascii'}{'confidence': 0.99, 'encoding': 'utf-8'}

根据this answer我尝试用datarecord[fieldname]替换上面的unicode(datarecord[fieldname]).encode('utf8'),并首先尝试使用'ascii'编解码器解码datarecord [fieldname]。后者因通常的“序数不在范围内”错误而失败。

这里出了什么问题?我只是想不出来!

2 个答案:

答案 0 :(得分:3)

在这种情况下,您希望避免使用echo;将您的输入直接写入stdin对象的Popen()管道。

确保您的环境设置为正确的语言环境,以便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

答案 1 :(得分:0)

只是为了添加Martijn Pieters的答案,他建议的解决方案在输入字符串为空的情况下会失败(与原始函数不同,即使正则表达式允许,grep也无法匹配空字符串) 。因此,原始函数的完整实现将是:

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匹配在空字符串上正常工作。