从不带破折号的字符串创建 UUID

问题描述 投票:0回答:11

如何从不带破折号的字符串创建 java.util.UUID?

"5231b533ba17478798a3f2df37de2aD7" => #uuid "5231b533-ba17-4787-98a3-f2df37de2aD7"
java string clojure uuid
11个回答
66
投票

tl;博士

java.util.UUID.fromString(
    "5231b533ba17478798a3f2df37de2aD7"
    .replaceFirst( 
        "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5" 
    )
).toString()

5231b533-ba17-4787-98a3-f2df37de2ad7

或者将 hexadecimal 字符串的每一半解析为

long
整数,并传递给 UUID
构造函数。

UUID uuid = new UUID ( long1 , long2 ) ; 

位,而不是文本

A UUID 是一个 128 位值。 UUID 实际上“不是”由字母和数字组成,而是由位组成。你可以把它想象成描述一个非常非常大的数字。 我们可以将这些位显示为一百二十八个

0

1
字符。

0111 0100 1101 0010 0101 0001 0101 0110 0110 0000 1110 0110 0100 0100 0100 1100 1010 0001 0111 0111 1010 1001 0110 1110 0110 0111 1110 1100 1111 1100 0101 1111

人类不容易读取位,因此为了方便起见,我们通常将 128 位值表示为由字母和数字组成的
十六进制

字符串。

74d25156-60e6-444c-a177-a96e67ecfc5f

这样的十六进制字符串并不是 UUID 本身,只是一种人类友好的表示形式。连字符是根据 UUID 规范添加的,作为规范格式,但是可选的。

74d2515660e6444ca177a96e67ecfc5f

顺便说一句,UUID 规范明确规定,生成十六进制字符串时必须使用
小写

字母,而应允许使用大写字母作为输入。不幸的是,许多实现都违反了小写生成规则,包括来自 Apple、Microsoft 和其他公司的实现。请参阅我的博客文章

以下内容指的是Java,而不是Clojure。

在 Java 7(及更早版本)中,您可以使用

java.util.UUID

类根据以连字符作为输入的十六进制字符串来实例化 UUID。示例: java.util.UUID uuidFromHyphens = java.util.UUID.fromString("6f34f25e-0b0d-4426-8ece-a8b3f27f4b63"); System.out.println( "UUID from string with hyphens: " + uuidFromHyphens );

但是,该 UUID 类会因输入不带连字符的十六进制字符串而失败。这种失败是不幸的,因为 UUID 规范
not

需要十六进制字符串表示形式中的连字符。这失败了: java.util.UUID uuidFromNoHyphens = java.util.UUID.fromString("6f34f25e0b0d44268ecea8b3f27f4b63"); 正则表达式

一种解决方法是格式化十六进制字符串以添加规范连字符。这是我尝试使用正则表达式来格式化十六进制字符串。当心……这段代码可以工作,但我不是正则表达式专家。您应该使这段代码更加健壮,例如在格式化之前检查字符串的长度是否为 32 个字符,格式化之后检查字符串的长度是否为 36 个字符。

// -----| With Hyphens |---------------------- java.util.UUID uuidFromHyphens = java.util.UUID.fromString( "6f34f25e-0b0d-4426-8ece-a8b3f27f4b63" ); System.out.println( "UUID from string with hyphens: " + uuidFromHyphens ); System.out.println(); // -----| Without Hyphens |---------------------- String hexStringWithoutHyphens = "6f34f25e0b0d44268ecea8b3f27f4b63"; // Use regex to format the hex string by inserting hyphens in the canonical format: 8-4-4-4-12 String hexStringWithInsertedHyphens = hexStringWithoutHyphens.replaceFirst( "([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5" ); System.out.println( "hexStringWithInsertedHyphens: " + hexStringWithInsertedHyphens ); java.util.UUID myUuid = java.util.UUID.fromString( hexStringWithInsertedHyphens ); System.out.println( "myUuid: " + myUuid );

Posix 表示法

您可能会发现这种替代语法更具可读性,在正则表达式中使用 Posix 表示法,其中 
\\p{XDigit}

代替

[0-9a-fA-F]

(请参阅

Pattern
文档):

String hexStringWithInsertedHyphens = hexStringWithoutHyphens.replaceFirst( "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5" ); 完整示例。

java.util.UUID uuid =
        java.util.UUID.fromString (
                "5231b533ba17478798a3f2df37de2aD7"
                        .replaceFirst (
                                "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)",
                                "$1-$2-$3-$4-$5"
                        )
        );

System.out.println ( "uuid.toString(): " + uuid );

uuid.toString():5231b533-ba17-4787-98a3-f2df37de2ad7

    

Clojure 的
#uuid

19
投票
标记文字

是到

java.util.UUID/fromString 的传递。并且, fromString
 将其用“-”分割,并将其转换为两个 
Long
值。 (
UUID
的格式标准化为 8-4-4-4-12 十六进制数字,但“-”实际上仅用于验证和视觉识别。)

直接的解决方案是重新插入“-”并使用 java.util.UUID/fromString

(defn uuid-from-string [data]
  (java.util.UUID/fromString
   (clojure.string/replace data
                           #"(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})"
                           "$1-$2-$3-$4-$5")))
如果您想要不带正则表达式的内容,可以使用

ByteBuffer

DatatypeConverter
(defn uuid-from-string [data]
  (let [buffer (java.nio.ByteBuffer/wrap 
                 (javax.xml.bind.DatatypeConverter/parseHexBinary data))]
    (java.util.UUID. (.getLong buffer) (.getLong buffer))))

正则表达式解决方案可能更快,但你也可以看看:)

17
投票

顺便说一下,从16个二进制字节到uuid的转换

  InputStream is = ..binarty input..;
  byte[] bytes = IOUtils.toByteArray(is);
  ByteBuffer bb = ByteBuffer.wrap(bytes);
  UUID uuidWithDashesObj = new UUID(bb.getLong(), bb.getLong());
  String uuidWithDashes = uuidWithDashesObj.toString();

你可以做一个愚蠢的正则表达式替换:

13
投票

public static String addUUIDDashes(String idNoDashes) {
    StringBuffer idBuff = new StringBuffer(idNoDashes);
    idBuff.insert(20, '-');
    idBuff.insert(16, '-');
    idBuff.insert(12, '-');
    idBuff.insert(8, '-');
    return idBuff.toString();
}

8
投票

与使用正则表达式和字符串操作相比,更快(约 900%)的解决方案是将十六进制字符串解析为 2 个长整型,并从中创建 UUID 实例:


8
投票

@maerics

6
投票

String[] digitsList= { "daa70a7ffa904841bf9a81a67bdfdb45", "529737c950e6428f80c0bac104668b54", "5673c26e2e8f4c129906c74ec634b807", "dd5a5ee3a3c44e4fb53d2e947eceeda5", "faacc25d264d4e9498ade7a994dc612e", "9a1d322dc70349c996dc1d5b76b44a0a", "5fcfa683af5148a99c1bd900f57ea69c", "fd9eae8272394dfd8fd42d2bc2933579", "4b14d571dd4a4c9690796da318fc0c3a", "d0c88286f24147f4a5d38e6198ee2d18" }; //Use compiled pattern to improve performance of bulk operations Pattern pattern = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); for (int i = 0; i < digitsList.length; i++) { String uuid = pattern.matcher(digitsList[i]).replaceAll("$1-$2-$3-$4-$5"); System.out.println(uuid); }

另一种解决方案与 Pawel 的解决方案类似,但不创建新字符串,仅解决问题。如果担心性能,请像瘟疫一样避免使用 regex/split/replaceAll 和 UUID.fromString。

3
投票

这是一个更快的示例,因为它不使用正则表达式。

3
投票

uuid-creator
中有一个编解码器可以更有效地做到这一点:

Base16Codec。示例:

// Parses base16 strings with 32 chars (case insensitive)
UuidCodec<String> codec = new Base16Codec();
UUID uuid = codec.decode("0123456789AB4DEFA123456789ABCDEF");

我相信以下是性能最快的。它甚至比 
Long.parseUnsignedLong 版本

2
投票
java-uuid-generator

的稍微修改过的代码。 public static UUID from32( String id) { if (id == null) { throw new NullPointerException(); } if (id.length() != 32) { throw new NumberFormatException("UUID has to be 32 char with no hyphens"); } long lo, hi; lo = hi = 0; for (int i = 0, j = 0; i < 32; ++j) { int curr; char c = id.charAt(i); if (c >= '0' && c <= '9') { curr = (c - '0'); } else if (c >= 'a' && c <= 'f') { curr = (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { curr = (c - 'A' + 10); } else { throw new NumberFormatException( "Non-hex character at #" + i + ": '" + c + "' (value 0x" + Integer.toHexString(c) + ")"); } curr = (curr << 4); c = id.charAt(++i); if (c >= '0' && c <= '9') { curr |= (c - '0'); } else if (c >= 'a' && c <= 'f') { curr |= (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { curr |= (c - 'A' + 10); } else { throw new NumberFormatException( "Non-hex character at #" + i + ": '" + c + "' (value 0x" + Integer.toHexString(c) + ")"); } if (j < 8) { hi = (hi << 8) | curr; } else { lo = (lo << 8) | curr; } ++i; } return new UUID(hi, lo); }

Java 17 使这项任务变得更容易,因为它提供了 
java.util.HexFormat

0
投票

(import (java.util UUID HexFormat)) (def uuid "5231b533ba17478798a3f2df37de2aD7") (UUID. (HexFormat/fromHexDigitsToLong uuid 0 16) (HexFormat/fromHexDigitsToLong uuid 16 32)) => #uuid "5231b533-ba17-4787-98a3-f2df37de2ad7"

当然,假设您知道 

uuid
 字符串的格式正确。如果没有,您可以使用谓词通过健全性检查来保护它

(re-matches #"\p{XDigit}{32}" uuid)

但这是一个简单的正则表达式,不需要任何捕获组,所以它应该很快。

© www.soinside.com 2019 - 2024. All rights reserved.