繁体   English   中英

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

[英]Get the string that is the midpoint between two other strings

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

最好的代码是用Python。

背景:

从表面上看,这似乎是一个简单的问题,但我对此颇为挣扎:

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

实际应用:

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

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

如果字符串是以base-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)

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

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

这是一个通用解决方案,它给出任意两个Unicode字符串ab之间的近似中点m ,如果可能的话, 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返回。

如果您查看JAVA StringTokinizer方法,它将执行您想要的操作,甚至更多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM