Python 如何预防;UnicodeDecodeError“;从sys.stdin读取管道输入时?
我正在阅读一些主要是Python3脚本中的十六进制输入。然而,该系统 设置为使用Python 如何预防;UnicodeDecodeError“;从sys.stdin读取管道输入时?,python,python-3.x,character-encoding,pipe,stdin,Python,Python 3.x,Character Encoding,Pipe,Stdin,我正在阅读一些主要是Python3脚本中的十六进制输入。然而,该系统 设置为使用UTF-8,当从bashshell到脚本中进行管道传输时,我保留 获取以下UnicodeDecodeError: UnicodeDecodeError:('utf-8'编解码器无法解码位置0处的字节0xed:无效的连续字节) 根据其他SO答案,我在Python3中使用读取管道输入,如下所示: 导入系统 ... isPipe=0 如果不是sys.stdin.isatty(): isPipe=1 尝试: inpipe=s
UTF-8
,当从bashshell到脚本中进行管道传输时,我保留
获取以下UnicodeDecodeError
:
UnicodeDecodeError:('utf-8'编解码器无法解码位置0处的字节0xed:无效的连续字节)
根据其他SO答案,我在Python3中使用读取管道输入,如下所示:
导入系统
...
isPipe=0
如果不是sys.stdin.isatty():
isPipe=1
尝试:
inpipe=sys.stdin.read().strip()
除UNICEDECODEDEERROR外,错误为e:
err_unicode(e)
...
使用这种方式进行管道安装时,它会起作用:
#echo“\xed\xff\xff\x0b\x04\x00\xa0\xe1”| some.py
但是,使用原始格式不会:
#echo-en“\xed\xff\xff\x0b\x04\x00\xa0\xe1”
▒▒▒
▒▒
#echo-en“\xed\xff\xff\x0b\x04\x00\xa0\xe1”| some.py
UnicodeDecodeError:('utf-8'编解码器无法解码位置0中的字节0xed:无效的连续字节)
还尝试了其他有希望的答案:
# echo -en "\xed\xff\xff\x0b\x04\x00\xa0\xe1" | python3 -c "open(1,'w').write(open(0).read())"
# echo -en "\xed\xff\xff\x0b\x04\x00\xa0\xe1" | python3 -c "from io import open; open(1,'w').write(open(0).read())"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib/python3.6/codecs.py", line 321, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 0: invalid continuation byte
#echo-en“\xed\xff\xff\x0b\x04\x00\xa0\xe1”| python3-c“打开(1,'w')。写入(打开(0.read())”
#echo-en“\xed\xff\xff\x0b\x04\x00\xa0\xe1”| python3-c”从io导入打开;打开(1,'w')。写入(打开(0.read())”
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“/usr/lib/python3.6/codecs.py”,第321行,解码中
(结果,消耗)=自身缓冲区解码(数据,自身错误,最终)
UnicodeDecodeError:“utf-8”编解码器无法解码位置0中的字节0xed:无效的连续字节
据我目前所知,当终端遇到一个序列时,后面会跟1-3个字节,如下所示:
UTF-8是一种可变宽度字符编码,能够使用一到四个8位字节对Unicode中的所有有效代码点进行编码。
因此,在
范围0x80-0xBF
然而,我不能总是确定我的输入流来自哪里,它很可能是原始数据,而不是上面提到的ASCII十六进制版本。所以我需要以某种方式处理这些原始输入
我已经考虑了一些备选方案,如:
- 使用
- 使用
open(“myfile.jpg”,“rb”,buffering=0)
- 使用
字节。从
- 或者只是使用
这不是一个副本。这里有一种像文件一样以二进制形式读取stdin的方法:
import sys
with open(sys.stdin.fileno(), mode='rb', closefd=False) as stdin_binary:
raw_input = stdin_binary.read()
try:
# text is the string formed by decoding raw_input as unicode
text = raw_input.decode('utf-8')
except UnicodeDecodeError:
# raw_input is not valid unicode, do something else with it
我最终通过而不是
sys.stdin
解决了这个问题
相反,我在open(0,'rb')中使用了。其中:
0
是等同于stdin的文件指针李>
'rb'
正在使用二进制模式进行读取
这似乎可以避免系统试图解释管道中的区域设置字符时出现的问题。我是在看到以下操作有效后得出这个想法的,并返回了正确的(不可打印的)字符:
echo-en“\xed\xff\xff\x0b\x04\x00\xa0\xe1”| python3-c”,打开(0,'rb')作为f:x=f.read();导入sys;sys.stdout.buffer.write(x);”
▒▒▒
▒▒
因此,为了正确读取任何管道数据,我使用了:
如果不是sys.stdin.isatty():
尝试:
打开(0,'rb')作为f:
inpipe=f.read()
例外情况除外,如e:
未知错误(e)
#这在二进制模式下不会发生:
#除UNICEDECODEDEERROR外,错误为e:
#err_unicode(e)
...
这将把管道数据读入python字节字符串
下一个问题是确定管道数据是来自字符串(如echo“BADDATA0”
)还是来自二进制流。后者可以通过echo-ne“\xBA\xDD\xAT\xA0”
进行模拟,如OP所示。在我的例子中,我只是使用正则表达式来查找越界的非ASCII字符。
当然,这可以做得更好、更明智。(请随意评论!)
附录:(摘自)
模式是一个可选字符串,用于指定打开文件的模式。它默认为r
,这意味着以文本模式打开阅读。在文本模式下,如果未指定编码,则使用的编码取决于平台:locale。调用getpreferredencoding(False)
以获取当前的locale编码。(对于读取和写入原始字节,请使用二进制模式并保留未指定的编码。)默认模式为“r”(打开以读取文本,同义词为“rt”)。对于二进制读写访问,模式w+b
打开并将文件截断为0字节r+b
打开文件时不截断
。。。Python区分二进制和文本I/O。以二进制模式打开的文件(包括模式参数中的b
)将内容作为字节对象返回,而不进行任何解码。在文本模式下(默认情况下,或者当模式参数中包含t
时),文件的内容返回为str,字节首先使用平台相关编码或指定编码(如果给定)进行解码
如果closefd为False
,并且提供了文件描述符而不是文件名,则在关闭文件时,基础文件描述符将保持打开状态。如果给定了文件名,closefd必须
if inpipe :
rx = re.compile(b'[^0-9a-fA-F ]+')
r = rx.findall(inpipe.strip())
if r == [] :
print("is probably a HEX ASCII string")
else:
print("is something else, possibly binary")