我有一个很长的 unicode 字符串:
alphabet = range(0x0FFF)
mystr = ''.join(chr(random.choice(alphabet)) for _ in range(100))
mystr = re.sub('\W', '', mystr)
我想将其视为一系列代码点,因此目前我正在执行以下操作:
arr = np.array(list(mystr), dtype='U1')
我希望能够将字符串作为数字进行操作,并最终得到一些不同的代码点。现在我想反转转换:
mystr = ''.join(arr.tolist())
这些转换速度相当快且可逆,但会占用 list
中介不必要的空间量。
有没有一种方法可以在不首先转换为列表的情况下将 numpy unicode 字符数组与 Python 字符串相互转换?
事后的想法
我可以让 arr
显示为带有类似内容的单个字符串
buf = arr.view(dtype='U' + str(arr.size))
这会产生一个包含整个原件的 1 元素数组。反过来也是可能的:
buf.view(dtype='U1')
唯一的问题是结果的类型是np.str_
,而不是str
。
最佳答案
fromiter
有效,但速度非常慢,因为它通过迭代器协议(protocol)。将数据编码为 UTF-32(按系统字节顺序)并使用 numpy.frombuffer
要快得多:
In [56]: x = ''.join(chr(random.randrange(0x0fff)) for i in range(1000))
In [57]: codec = 'utf-32-le' if sys.byteorder == 'little' else 'utf-32-be'
In [58]: %timeit numpy.frombuffer(bytearray(x, codec), dtype='U1')
2.79 µs ± 47 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [59]: %timeit numpy.fromiter(x, dtype='U1', count=len(x))
122 µs ± 3.82 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [60]: numpy.array_equal(numpy.fromiter(x, dtype='U1', count=len(x)), numpy.fr
...: ombuffer(bytearray(x, codec), dtype='U1'))
Out[60]: True
我使用 sys.byteorder
来确定是使用 utf-32-le
还是 utf-32-be
进行编码。此外,使用 bytearray
而不是 encode
会得到一个可变字节数组而不是不可变字节对象,因此生成的数组是可写的。
至于反向转换,arr.view(dtype=f'U{arr.size}')[0]
有效,但使用 item()
速度更快一些,并生成一个普通的字符串对象,避免了 numpy.str_
的行为与 str
不同的可能的奇怪边缘情况:
In [72]: a = numpy.frombuffer(bytearray(x, codec), dtype='U1')
In [73]: type(a.view(dtype=f'U{a.size}')[0])
Out[73]: numpy.str_
In [74]: type(a.view(dtype=f'U{a.size}').item())
Out[74]: str
In [75]: %timeit a.view(dtype=f'U{a.size}')[0]
3.63 µs ± 34 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [76]: %timeit a.view(dtype=f'U{a.size}').item()
2.14 µs ± 23.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
最后,请注意 NumPy 不像普通的 Python 字符串对象那样处理空值。 NumPy 无法区分 'asdf\x00\x00\x00'
和 'asdf'
,因此如果您的数据可能包含 null,则使用 NumPy 数组进行字符串操作是不安全的代码点。
关于python - 将代码点的 numpy 数组与字符串相互转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54424433/