web-dev-qa-db-fra.com

Création d'un UUID à partir d'une chaîne sans tirets

Comment créer un fichier Java.util.UUID à partir d'une chaîne sans tirets?

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

Le #uuidtagged literal de Clojure est une transmission au Java.util.UUID/fromString . Et fromString le divise en "-" et le convertit en deux valeurs Long. (Le format de UUID est normalisé à 8-4-4-4-12 chiffres hexadécimaux, mais les "-" ne sont vraiment là que pour la validation et l'identification visuelle.)

La solution simple consiste à réinsérer le "-" et à utiliser 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")))

Si vous voulez quelque chose sans expressions régulières, vous pouvez utiliser un ByteBuffer et 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

Des bits, pas du texte

Un UUID est une valeur de 128 bits. Un UUID est pas réellement composé de lettres et de chiffres, il est composé de bits. Vous pouvez penser que cela décrit un très grand nombre. 

Nous pourrions afficher ces bits sous forme de cent vingt huit caractères 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

Les humains ne lisent pas facilement les bits. Pour des raisons pratiques, nous représentons généralement la valeur de 128 bits sous la forme d'une chaîne hexadécimale composée de lettres et de chiffres. 

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

Une telle chaîne hexagonale n'est pas le UUID lui-même, mais une représentation conviviale. Les traits d'union sont ajoutés par la spécification UUID sous forme de mise en forme canonique, mais sont facultatifs.

74d2515660e6444ca177a96e67ecfc5f

En passant, la spécification UUID indique clairement que les lettres minuscules doivent être utilisées lors de la génération de la chaîne hexagonale, tandis que les majuscules doivent être tolérées en tant qu'entrée. Malheureusement, de nombreuses implémentations violent cette règle de génération de minuscules, y compris celles de Apple, Microsoft et autres. Voir mon blog .


Ce qui suit fait référence à Java, pas à Clojure.

Dans Java 7 (et versions antérieures), vous pouvez utiliser la classe Java.util.UUID pour instancier un UUID basé sur une chaîne hexadécimale avec des tirets. Exemple:

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

Cependant, cette classe UUID échoue lors de la saisie d'une chaîne hexadécimale sans trait d'union. Cet échec est regrettable car la spécification UUID ne pas nécessite les traits d'union dans une représentation de chaîne hexagonale. Cela échoue:

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

Regex

Une solution consiste à formater la chaîne hexadécimale pour ajouter les traits d'union canoniques. Voici ma tentative d'utiliser regex pour formater la chaîne hexadécimale. Attention… Ce code fonctionne, mais je ne suis pas un expert en regex. Vous devriez rendre ce code plus robuste, en vérifiant que la longueur de la chaîne est de 32 caractères avant le formatage et de 36 après.

    // -----|  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 );

Notation Posix

Vous pouvez trouver cette syntaxe alternative plus lisible, en utilisant la notation Posix dans la regex où \\p{XDigit} prend la place de [0-9a-fA-F] (voir 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" );

Exemple complet.

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

38
Basil Bourque

Vous pouvez effectuer un remplacement d’expression régulière maladroit:

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

Une solution beaucoup plus rapide (environ 900%) par rapport à l'utilisation des expressions rationnelles et de la manipulation de chaînes consiste à analyser la chaîne hexadécimale en 2 longs et à créer l'instance UUID à partir de ces éléments:

(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

La solution Regexp est probablement plus rapide, mais vous pouvez aussi regarder ça :)

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();

À propos, conversion de 16 octets binaires en 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();
6
Paweł Woźniak
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();
}

Peut-être que quelqu'un d'autre peut commenter sur l'efficacité de calcul de cette approche. (Ce n'était pas un problème pour mon application.) 

5
Brad Knox

Version optimisée de la réponse de -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

Une autre solution serait quelque chose de similaire à la solution de Pawel, mais sans créer de nouvelles chaînes et ne résoudre que le problème des questions. Si les performances sont un sujet de préoccupation, évitez les expressions régulières/scindée/replaceAll et UUID.fromString.

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

Je crois que ce qui suit est le plus rapide en termes de performances. Il est même légèrement plus rapide que Long.parseUnsignedLong version . C'est un code légèrement modifié qui provient de 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);
}
0
Adam Gent