获取两个其他字符串之间的中点字符串

时间:2013-05-25 17:17:27

标签: python string string-math

是否有可用的库或代码段,可以使用两个字符串并返回两个字符串之间的精确或近似中点字符串?

最好是代码用Python。

背景

这似乎是表面上的一个简单问题,但我有点挣扎:

  • 显然,“A”和“C”之间的中点字符串将是“B”。
  • 使用base64编码,“A”和“B”之间的中点字符串可能是“Ag”
  • 使用UTF-8编码,我不确定有效的中点是什么,因为中间字符似乎是一个控制字符:U+0088 c2 88 <control>

实际应用:

我问的原因是因为我希望编写map-reduce类型算法来读取数据库中的所有条目并处理它们。数据库中的主键是UTF-8编码的字符串,具有随机的字符分布。我们使用的数据库是Cassandra。

希望从数据库中获得最低密钥和最高密钥,然后通过找到中点将其分成两个范围,然后通过查找每个中间点将这两个范围分成两个较小的部分,直到我有几千个部分,然后我可以异步读取每个部分。

如果字符串是16位编码的示例:(某些中点是近似值):

Starting highest and lowest keys:  '000'                'FFF'
                                   /   \              /       \
                              '000'     '8'         '8'       'FFF'
                              /   \     /  \       /  \       /   \
Result:                  '000'    '4' '4'  '8'   '8'  'B8'  'B8'  'FFF'
(After 3 levels of recursion)

3 个答案:

答案 0 :(得分:2)

不幸的是not all sequences of bytes是有效的UTF-8,因此仅仅采用UTF-8值的中点并非易事,如下所示。

def midpoint(s, e):
    '''Midpoint of start and end strings'''
    (sb, eb) = (int.from_bytes(bytes(x, 'utf-8'), byteorder='big') for x in (s, e))
    midpoint = int((eb - sb) / 2 + sb)

    midpoint_bytes = midpoint.to_bytes((midpoint.bit_length() // 8) + 1, byteorder='big')
    return midpoint_bytes.decode('utf-8')

基本上,这段代码将每个字符串转换为由内存中的字节序列表示的整数,找到这两个整数的中点,并尝试再次将“中点”字节解释为UTF-8。

根据您想要的确切行为,下一步可能是使用某种替换字符替换midpoint_bytes中的无效字节,以形成有效的UTF-8字符串。对于您的问题,只要您保持一致,就可以确切地使用哪个角色进行替换。

但是,由于您尝试对数据进行分区并且似乎并不太关心中点的字符串表示,另一个选择是将中点表示保留为整数并将键转换为整数做分区。根据您的问题的规模,此选项可能是可行的,也可能是不可行的。

答案 1 :(得分:2)

这是一个通用解决方案,可以在任意两个Unicode字符串ma之间提供近似中点b,如果可能的话,a < m < b

from os.path import commonprefix

# This should be set according to the range and frequency of
# characters used.
MIDCHAR = u'm'


def midpoint(a, b):
    prefix = commonprefix((a, b))
    p = len(prefix)
    # Find the codepoints at the position where the strings differ.
    ca = ord(a[p]) if len(a) > p else None
    cb = ord(b[p])
    # Find the approximate middle code point.
    cm = (cb // 2 if ca is None else (ca + cb) // 2)
    # If a middle code point was found, add it and return.
    if ca < cm < cb:
        return prefix + unichr(cm)
    # If b still has more characters after this, then just use
    # b's code point and return.
    if len(b) > p + 1:
        return prefix + unichr(cb)
    # Otherwise, if cb == 0, then a and b are consecutive so there
    # is no midpoint. Return a.
    if cb == 0:
        return a
    # Otherwise, use part of a and an extra character so that
    # the result is greater than a.
    i = p + 1
    while i < len(a) and a[i] >= MIDCHAR:
        i += 1
    return a[:i] + MIDCHAR

该函数假定a < b。除此之外,它应该使用任意Unicode字符串,甚至包含u'\x00'个字符的字符串。另请注意,它可能会返回包含u'\x00'或其他非标准代码点的字符串。如果b == a + u'\x00'没有中间点,则返回a

答案 2 :(得分:0)

如果你看一下JAVA StringTokinizer方法它会做你想要的,等等。