使用 Python 删除 "characters with encodings larger than 3 bytes" 3

Remove "characters with encodings larger than 3 bytes" using Python 3

我想删除编码大于 3 个字节的字符。 因为当我将我的 CSV 数据上传到 Amazon Mechanical Turk 系统时,它要求我这样做。

Your CSV file needs to be UTF-8 encoded and cannot contain characters with encodings larger than 3 bytes. For example, some non-English characters are not allowed (learn more).

为了克服这个问题, 我想制作一个 filter_max3bytes 函数来删除 Python3 中的那些字符。

x = 'below ð\x9f~\x83,'
y = remove_max3byes(x)  # y=="below ~,"

然后我会应用函数,然后将其保存到 UTF-8 编码的 CSV 文件中。

This post 与我的问题有关,但他们使用 python 2 并且该解决方案对我不起作用。

谢谢!

根据the UTF-8 standard,Unicode 代码点低于 U+0800 的字符在编码中最多使用两个字节。所以只需删除 U+0800 或以上的任何字符。此代码复制最多占用两个字节的所有字符,并忽略其他字符。

def remove_max3byes(x):
    return ''.join(c for c in x if ord(c) < 0x800)

正如评论所指出的,您的示例字符串中没有任何字符超过两个字节。但是这个命令在 REPL

remove_max3byes(chr(0x07ff))

给予

'\u07ff'

和这个命令

remove_max3byes(chr(0x0800))

给予

''

两个都想要。

None 字符串中的字符在 UTF-8 中似乎占用 3 个字节:

x = 'below ð\x9f~\x83,'

无论如何,删除它们的方法是:

filtered_x = ''.join(char for char in x if len(char.encode('utf-8')) < 3)

例如(有这样的字符):

>>> x = 'abcd漢字efg'
>>> ''.join(char for char in x if len(char.encode('utf-8')) < 3)
'abcdefg'

顺便说一句,您可以通过执行以下操作来验证您的原始字符串没有 3 字节编码:

>>> for char in 'below ð\x9f~\x83,':
...     print(char, [hex(b) for b in char.encode('utf-8')])
...
b ['0x62']
e ['0x65']
l ['0x6c']
o ['0x6f']
w ['0x77']
  ['0x20']
ð ['0xc3', '0xb0']
  ['0xc2', '0x9f']
~ ['0x7e']
  ['0xc2', '0x83']
, ['0x2c']

编辑:一个疯狂的猜测

我认为 OP 问错了问题,问题实际上是字符是否可打印。我假设任何 Python 显示为 \x<number> 是不可打印的,所以这个解决方案应该有效:

x = 'below ð\x9f~\x83,'
filtered_x = ''.join(char for char in x if not repr(char).startswith("'\x"))

结果:

'below ð~,'

虽然间接说明,但该网站只允许来自基本多语言平面 (BMP) 的字符。这包括 Unicode 代码点 U+0000 到 U+FFFF。在 UTF-8 中,需要四个字节来编码 U+FFFF 以上的任何内容:

>>> '\uffff'.encode('utf8')
b'\xef\xbf\xbf'
>>> '\U00010000'.encode('utf8')
b'\xf0\x90\x80\x80'

这会过滤掉 U+FFFF 以上的 Unicode 代码点:

>>> test_string = 'abc马克' # emoticon is U+1F600
>>> ''.join(c for c in test_string if ord(c) < 0x10000)
'abc马克'

编码时(注意每个汉字三个字节):

>>> ''.join(c for c in test_string if ord(c) < 0x10000).encode('utf8')
b'abc\xe9\xa9\xac\xe5\x85\x8b'