5

我想做以下...

a) 将生成的 UUID 压缩为长度为 8 的字符串。

b) 将压缩后的 UUID 解压回原来的 UUID。

原因是因为我必须将 UUID 发送到合作系统,而合作系统只接受 8 个字符的 UUID,并且我不能请求更改合作系统。

因此,剩下要做的就是将我必须的 UUID 压缩为 8 个字符字符串,然后在从合作系统返回消息时将其解压缩回原始 UUID。

有任何想法吗?

谢谢。

4

3 回答 3

10

由于信息论的原因,您所问的内容是不可能的。

RFC 4122指定的 UUID是 128 位,UUIDJava 中的对象也是如此。

JavaString可以为每个字符存储 16 位,这构成一个 8char字符串。但是,并非所有位序列都是有效的 UTF-16 字符串,因此在 8 个字符中您可以存储少于 128 位的信息。

因此,如果将 UUID 压缩为有效的 8 字符字符串,则会丢失信息,因此通常无法将其解压缩以取回原始 UUID。

您可能打算生成一个较短的字符串以用作唯一标识符。如果是这样,请参阅仅生成 8 个字符的 UUID

于 2012-08-06T03:15:13.593 回答
5

实现 url 安全 uuid 压缩的最佳方法是将其编码为 base64

public class UUIDUtils {

  public static String compress(UUID uuid) {
    ByteBuffer bb = ByteBuffer.allocate(Long.BYTES * 2);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    byte[] array = bb.array();
    return Base64.getEncoder().encodeToString(array);
  }

  public static UUID decompress(String compressUUID) {
    ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getDecoder().decode(compressUUID));
    return new UUID(byteBuffer.getLong(), byteBuffer.getLong());
  }


}

结果:6227185c-b25b-4497-b821-ba4f8d1fb9a1 -> YicYXLJbRJe4IbpPjR+5oQ==

于 2017-01-17T16:47:11.727 回答
0

您可以将 UUID 转换为一个字符串,它实际上是一个 16 位char8 元素的序列,如下所示。

static String encodeUuid(final UUID id) {
  final long hi = id.getMostSignificantBits();
  final long lo = id.getLeastSignificantBits();
  return new String(new char[] {
    (char) ((hi >>> 48) & 0xffff), (char) ((hi >>> 32) & 0xffff),
    (char) ((hi >>> 16) & 0xffff), (char) ((hi       ) & 0xffff),
    (char) ((lo >>> 48) & 0xffff), (char) ((lo >>> 32) & 0xffff),
    (char) ((lo >>> 16) & 0xffff), (char) ((lo       ) & 0xffff)
  });
}

static UUID decodeUuid(final String enc) {
  final char[] cs = enc.toCharArray();
  return new UUID(
    (long) cs[0] << 48 | (long) cs[1] << 32 | (long) cs[2] << 16 | (long) cs[3],
    (long) cs[4] << 48 | (long) cs[5] << 32 | (long) cs[6] << 16 | (long) cs[7]
  );
}

这段代码确实看起来应该可以工作(在这里自己尝试),并且可以使用 UTF-8 和 UTF-16 进行编码/解码,而大多数情况下都不会出现问题:

static boolean validate(final UUID id, final Charset cs) {
  final ByteBuffer buf = cs.encode(encodeUuid(id));
  final UUID _id = decodeUuid(cs.decode(buf).toString());
  return id.equals(_id);
}

public static void main(final String[] argv) {
  final UUID id = UUID.randomUUID();
  assert validate(id, StandardCharsets.UTF_8)  : "failed using utf-8";
  assert validate(id, StandardCharsets.UTF_16) : "failed using utf-16";
}

C:\dev\scrap>javac UuidTest.java

C:\dev\scrap>java -ea UuidTest

然而,确实存在一些 UTF-16 代码点被保留为代理项的问题。如果发生这种情况,编码将不起作用,您将无法重建原始 UUID。有关更多信息,请参阅上面的机械蜗牛的回应。


您可以始终如一地从通过UUID.randomUUID生成的编码 UUID中删除的唯一数据是用于 (always ) 的 2 位和用于( always ) 的 4 位。variant2version4

这些全局标识符存在不同的变体。此类的方法用于操作 Leach-Salz 变体,尽管构造函数允许创建 UUID 的任何变体(如下所述)。

变体 2 (Leach-Salz) UUID 的布局如下: 最重要的 long 由以下无符号字段组成: 0xFFFFFFFF00000000 time_low
0x00000000FFFF0000 time_mid
0x000000000000F000 version
0x0000000000000FFF time_hi

最不重要的 long 由以下无符号字段组成: 0xC000000000000000 variant
0x3FFF000000000000 clock_seq
0x0000FFFFFFFFFFFF node

变体字段包含一个标识布局的值UUID。上述位布局仅对UUID变量值为 2 的 a 有效,表示Leach-Salz变量。

version 字段包含一个描述 this 类型的值UUID。UUID 有四种不同的基本类型:基于时间、DCE 安全、基于名称和随机生成的 UUID。这些类型的版本值分别为 1、2、3 和 4。

于 2012-08-06T03:19:17.267 回答