[英]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個字節,那么您可以相當簡單地定義映射:
顯然,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.