繁体   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