簡體   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