web-dev-qa-db-ja.com

ダッシュなしの文字列からUUIDを作成する

ダッシュなしの文字列からJava.util.UUIDを作成するにはどうすればよいですか?

"5231b533ba17478798a3f2df37de2aD7" => #uuid "5231b533-ba17-4787-98a3-f2df37de2aD7"
40
yayitswei

Clojureの#uuidタグ付きリテラル は、 Java.util.UUID/fromString へのパススルーです。また、fromStringは「-」で分割し、2つのLong値に変換します。 ( [〜#〜] uuid [〜#〜] の形式は8-4-4-4-12の16進数に標準化されていますが、「-」は実際には検証と視覚的識別のためにあります。)

簡単な解決策は、「-」を再挿入し、 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
Jared314

tl; dr

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

テキストではなくビット

[〜#〜] uuid [〜#〜] は128ビット値です。 UUIDはnot実際には文字と数字で構成され、ビットで構成されます。非常に多くの数を記述していると考えることができます。

これらのビットを128個の01文字として表示できます。

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ビット値を 16進数 文字と数字で構成される文字列として表します。

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

このような16進数の文字列はUUID自体ではなく、人間にわかりやすい表現です。ハイフンは、UUID仕様ごとに標準形式として追加されますが、オプションです。

74d2515660e6444ca177a96e67ecfc5f

ちなみに、UUID仕様では、16進数文字列を生成する際にlowercase文字を使用する必要があり、大文字は入力として許容される必要があることが明記されています。残念なことに、Apple、Microsoft、その他からのものを含め、多くの実装はその小文字生成ルールに違反しています。 私のブログ投稿 を参照してください。


以下はClojureではなくJavaを指します。

Java 7(およびそれ以前)では、 Java.util.UUID クラスを使用して、入力としてハイフンを含む16進文字列に基づいてUUIDをインスタンス化できます。例:

Java.util.UUID uuidFromHyphens = Java.util.UUID.fromString("6f34f25e-0b0d-4426-8ece-a8b3f27f4b63");
System.out.println( "UUID from string with hyphens: " + uuidFromHyphens );

ただし、そのUUIDクラスは、16進数文字列なしハイフンを入力すると失敗します。 UUID仕様では16進文字列表現のハイフンがnotを必要とするため、この失敗は残念です。これは失敗します:

Java.util.UUID uuidFromNoHyphens = Java.util.UUID.fromString("6f34f25e0b0d44268ecea8b3f27f4b63");

正規表現

回避策の1つは、16進文字列をフォーマットして標準のハイフンを追加することです。正規表現を使用して16進文字列をフォーマットする試みは次のとおりです。注意してください...このコードは動作しますが、私は正規表現の専門家ではありません。このコードをより堅牢にする必要があります。たとえば、文字列の長さがフォーマット前は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表記

この代替構文は、\\p{XDigit}[0-9a-fA-F]の代わりに使用される正規表現内でPosix表記を使用して、より読みやすくなる場合があります( Pattern docを参照)。

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

43
Basil Bourque

間抜けな正規表現の置換を行うことができます:

String digits = "5231b533ba17478798a3f2df37de2aD7";                         
String uuid = digits.replaceAll(                                            
    "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})",                            
    "$1-$2-$3-$4-$5");                                                      
System.out.println(uuid); // => 5231b533-ba17-4787-98a3-f2df37de2aD7
10
maerics

Regexpソリューションはおそらくより高速ですが、それも見ることができます:)

String withoutDashes = "44e128a5-ac7a-4c9a-be4c-224b6bf81b20".replaceAll("-", "");      
BigInteger bi1 = new BigInteger(withoutDashes.substring(0, 16), 16);                
BigInteger bi2 = new BigInteger(withoutDashes.substring(16, 32), 16);
UUID uuid = new UUID(bi1.longValue(), bi2.longValue());
String withDashes = uuid.toString();

ところで、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();
7
Paweł Woźniak

正規表現と文字列操作を使用するよりもはるかに高速(〜900%)のソリューションは、16進文字列を2つのlongに解析し、それらからUUIDインスタンスを作成することです。

(defn uuid-from-string
  "Converts a 32digit hex string into Java.util.UUID"
  [hex]
  (Java.util.UUID.
    (Long/parseUnsignedLong (subs hex 0 16) 16)
    (Long/parseUnsignedLong (subs hex 16) 16)))
6
toxi
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();
}

他の誰かがこのアプローチの計算効率についてコメントできるかもしれません。 (これは私のアプリケーションの問題ではありませんでした。)

5
Brad Knox

@ maerics の回答の最適化されたバージョン:

    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);
    }
5
vahapt

別の解決策は、Pawelの解決策に似ていますが、新しい文字列を作成せず、質問の問題を解決するだけです。パフォーマンスが懸念される場合は、ペストのようにregex/split/replaceAllおよびUUID.fromStringを避けてください。

String hyphenlessUuid = in.nextString();
BigInteger bigInteger = new BigInteger(hyphenlessUuid, 16);
 new UUID(bigInteger.shiftRight(64).longValue(), bigInteger.longValue());
2
foozbar

パフォーマンスの面では、次のことが最も速いと思います。 Long.parseUnsignedLong version よりもわずかに高速です。 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);
}
1
Adam Gent