14

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

最好是代码在 Python 中。

背景:

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

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

实际应用:

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

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

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

开始最高和最低键:'000''FFF'
                                   / \ / \
                              '000' '8' '8' 'FFF'
                              / \ / \ / \ / \
结果:'000' '4' '4' '8' '8' 'B8' 'B8' 'FFF'
(经过3级递归)
4

2 回答 2

2

不幸的是,并非所有字节序列都是有效的 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 字符串。对于您的问题,只要您保持一致,您使用哪个字符来替换可能并不重要。

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

于 2013-06-01T03:00:10.577 回答
2

m这是一个通用解决方案,它给出了任意两个 Unicode 字符串a和之间的近似中点ba < 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'then没有中点,则a返回。

于 2013-06-01T22:47:10.450 回答