簡體   English   中英

如何將18個字符串轉換為唯一ID?

[英]How to convert an 18 Character String into a Unique ID?

我有一個18字符串,我需要轉換為一個獨特的長(在Java中)。 示例字符串將是:AAA2aNAAAAAAADnAAA

我的String實際上是一個Oracle ROWID,因此可以根據需要進行細分,請參閱: http//download-uk.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#CNCPT713

生成的長數字,(1)必須是唯一的,因為沒有兩個結果可以指向同一個數據庫行,(2)必須是可逆的,所以我可以從長整數中獲取ROWID字符串?

有關使用算法的任何建議都將受到歡迎。

幾年前的Oracle論壇問題: http//forums.oracle.com/forums/thread.jspa?messageID = 1059740

你不能滿足這些要求。

(假設)大寫和小寫字母的18個字符具有56 18或大約2.93348915×103 31個組合。 這是(方式)超過64位中可用的近似1.84467441×10 19組合。

更新:我的組合學錯了,呵呵。 但結果相同。

只需創建一個將ROWID字符串映射到(遞增)long的映射(字典/散列表)。 如果你保留兩個這樣的詞典並將它們包裝在一個很好的類中,你將在字符串和長ID之間進行雙向查找。

偽代碼:

class BidirectionalLookup:
    dict<string, long> stringToLong
    dict<long, string> longToString
    long lastId

    addString(string): long
        newId = atomic(++lastId)
        stringToLong[string] = newId
        longToString[newId] = string
        return newId

    lookUp(string): long
        return stringToLong[string]

    lookUp(long): string
        return longToString[long]

表示基本64位編碼的18個字符的字符串表示總共108位信息,幾乎是長64位的兩倍。如果我們想要表示每個可能的鍵並且表示是可逆的,我們在這里有一點問題。

字符串可以很容易地分解為4個數字。 這4個數字中的每一個都代表一些東西 - 塊號,該塊中的偏移量,等等。 如果您設法建立基礎數量的上限,以便您知道不會出現更大的數字(即如果您找到一種方法來識別至少44個始終為0的位),那么您可以將其余的數據映射到長,可逆。

另一種可能性是放寬對等long的要求。 BigInteger怎么樣? 這會讓事情變得簡單。

我假設這是一個不區分大小寫的字母數字字符串,因此從集合中繪制[a-zA-Z0-9]*

在那種情況下你有

26 + 26 + 10 = 62 

每個字符的可能值。

62 < 64 = 2^6

換句話說,您需要(至少)6位來存儲密鑰的18個字符中的每一個。

6 * 18 = 108 bits 

唯一地存儲整個字符串。

108 bits  = (108 / 8) = 13.5 bytes.

因此,只要您的數據類型可以存儲至少13.5個字節,那么您可以相當簡單地定義映射:

  1. 從每個字符的原始ASCII映射到僅使用6位的表示
  2. 將所有18個簡化表示連接到sinlde 14字節值
  3. 將其轉換為您的最終數據值

顯然,Java只有8個字節long 所以,如果你必須使用一個long那么它是不可能的唯一映射字符串,除非有別的東西可降低有效輸入字符串的空間。

從理論上講,你不能用長(8字節)來表示ROWID。 但是,根據數據庫的大小(整個服務器,而不僅僅是您的表),您可以將其編碼為long。

這是ROWID的布局,

   OOOOOO-FFF-BBBBBB-RRR

其中O是ObjectID。 F是FileNo。 B是Block,R是行號。 所有這些都是Base64編碼的。 正如您所見,O&B可以有36位,B&R可以有18位。

如果您的數據庫不是很大,則每個部分可以使用2個字節。 基本上,您的ObjectId和塊編號將限制為64K。 我們的DBA認為我們的數據庫必須要大幾倍才能接近這些限制。

我建議你找到數據庫中每個部分的最大值,看看你是否接近。 如果它們接近極限,我不會長時間使用。

找到了一種從數據庫中以不同方式提取ROWID的方法....

SQL> select DBMS_ ROWID.ROWID_ TO_RESTRICTED( ROWID, 1 ) FROM MYTABLE;

0000EDF4.0001.0000 0000EDF4.0002.0000 0000EDF4.0004.0000 0000EDF4.0005.0000 0000EDF4.0007.0000 0000EDF5.0000.0000 0000EDF5.0002.0000 0000EDF5.0003.0000

然后將其轉換為如此數字:

final String hexNum = rowid.replaceAll( "\.", "" );
final long lowerValue = Long.parseLong( hexNum.substring( 1 ), 16 );
long upperNibble = Integer.parseInt( hexNum.substring( 0, 1 ), 16 );
if ( upperNibble >= 8 ) {
  //Catch Case where ROWID > 8F000000.0000.0000
  upperNibble -= 8;
  return -( 9223372036854775807L - ( lowerValue - 1 + ( upperNibble << 60 ) ) );
} else {
  return ( lowerValue + ( upperNibble << 60 ) );
}

然后將該數字反轉回String格式,如下所示:

String s = Long.toHexString( featureID );
//Place 0's at the start of the String making a Strnig of size 16
s = StringUtil.padString( s, 16, '0', true ); 
StringBuffer sb = new StringBuffer( s );
sb.insert( 8, '.' );
sb.insert( 13, '.' );

return sb.toString();

為所有回應干杯。

這聽起來...... icky,但我不知道你的背景所以試圖不通過判斷。 8)

您是否考慮過將字符串中的字符轉換為ASCII等效字符?

ADDENDUM:當然需要截斷半超級字符以適應,這聽起來像是評論中的一個選項。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM