Best way to check if a String is a UUID

Discussion in 'Plugin Development' started by KingFaris11, Apr 18, 2014.

Thread Status:
Not open for further replies.
  1. Hello, I'm making a method that converts all usernames into UUIDs from a configuration. Although, I need to check every key in an Entry<String, Object>, checking if it's a valid UUID or not. The way I use is:
    Code:
    public boolean isUUID(String string) {
        try {
            UUID.fromString(string);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    
    Although, I was told not to rely on catching Exceptions. Is there any other way?
     
  2. Offline

    DrEinsteinium

    KingFaris11 To be honest, that seems like the best way to me for this. I don't know if you know how to use RegEx, but here is a javascript UUID validator regex string.

    Code:
    /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
     
    KingFaris11 likes this.
  3. That actually seems legit, thanks!

    Edit: I don't know what the "$/i" is? o.o
     
  4. Offline

    AoH_Ruthless

    KingFaris11
    Those are just JS tags.

    You can just remove those; in Java, the regex will look like:

    Code:java
    1. String uuid = ""; //define it!
    2. if (uuid.matches("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")) {
    3. // CODE!
    4. }


    Don't read the following if you are content with what DrEinsteinium gave you, seeing as it should work wonderfully :)

    His logic in case you are unsure:
    All UUID's are in the form of 8-4-4-4-12, with the numbers representing hexadecimals.
    Because they are hexadecimals, we can check each block by getting all possible hexadecimals a certain number of times, followed by the universal block separator '-'.

    However, one modification: In the third block, the first hexadecimal is always a 4 (Mojang uses Version 4 UUIDs). Traditionally, UUID's could be capital letters, but I think Mojang eliminated that.
    Code:java
    1.  
    2. if (uuid.matches("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")) {
    3.  


    Also, the way DrEinsteinium showed it could have false positives since it is hex... so you need to do:

    Code:java
    1.  
    2. if (uuid.matches("[a-f0-9]{8}-[a-f0-9]{4}-4[0-9]{3}-[89ab][a-f0-9]{3}-[0-9a-f]{12}")) {
    3.  
     
    DrEinsteinium and KingFaris11 like this.
  5. Offline

    !Phoenix!

    You now restricted the expression to only accept UUID v4 strings. But: UUID v3 is used to create name-based offline-player UUIDs. So your expression will result in a false negative for new players who joined in offline mode*.
    Additionally you forgot(?) to consider a-f in your third block.

    This expression will accept all player UUIDs (lower case only):
    "[0-9a-f]{8}-[0-9a-f]{4}-[34][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"

    Also allowing the use of uppercase letters (recommended version):
    "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}"

    Alternative version making clear these are hexadecimal digits:
    "[\p{XDigit}]{8}-[\p{XDigit}]{4}-[34][\p{XDigit}]{3}-[89ab][\p{XDigit}]{3}-[\p{XDigit}]{12}"
    (\p{XDigit} := 0-9a-fA-F)


    Something else: I cannot see any difference in the last two expressions you posted despite that you changed the positions of 0-9 and a-f, which has absolutely no effect.



    *Not asking anyone to use offline mode for public servers here. I need to run my local servers in offline mode for testing purposes.
     
    Konato_K likes this.
  6. Offline

    AoH_Ruthless

    @!Phoenix!
    I haven't worked with UUIDs (or much of Minecraft at all) in a while now, but I hope I can address some things you said in an accurate way:

    I have never seen Mojang use v3 UUIDs so I cannot really comment on this part besides that I will look into it later for offline players. While the regex I provided will reject UUID v1-3 (and even 5!), I never thought it would make a difference.

    UUIDs are all lowercased on output and are case-insensitive on input, so there is no point making them uppercase in the regex.

    The third block is a series of 4 integers in v4 UUIDs and never contains a-f.

    In my post, there is no real difference between the last two expressions. [0-9a-f] is a bit slower than [a-f0-9], however. I don't exactly know why, but I would assume it is because 'more string' is getting parsed otherwise. Additionally, writing it as [0-9a-f] can return false positives.
     
  7. Offline

    mythbusterma

    Since you're probably going to be using it as a UUID anyway, you should probably just use the UUID.fromString(String) static method and just catch the IllegalArguementException that it throws when the supplied argument isn't a UUID.
     
  8. Offline

    xTrollxDudex

    Even better, check the UUID source and see why it is throwing it, and implement it yourself. Exception catching like this is not the recommended way to do it.

    Here, this is from OpenJDK 6 beta 14:
    Code:java
    1. public static UUID fromString(String name) {
    2. String[] components = name.split("-");
    3. if (components.length != 5)
    4. throw new IllegalArgumentException("Invalid UUID string: "+name);
    5. for (int i=0; i<5; i++)
    6. components[i] = "0x"+components[i];
    7.  
    8. long mostSigBits = Long.decode(components[0]).longValue();
    9. mostSigBits <<= 16;
    10. mostSigBits |= Long.decode(components[1]).longValue();
    11. mostSigBits <<= 16;
    12. mostSigBits |= Long.decode(components[2]).longValue();
    13.  
    14. long leastSigBits = Long.decode(components[3]).longValue();
    15. leastSigBits <<= 48;
    16. leastSigBits |= Long.decode(components[4]).longValue();
    17.  
    18. return new UUID(mostSigBits, leastSigBits);
    19. }[I][/I][/i][/i]


    Found here: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/UUID.java#UUID.fromString(java.lang.String)
     
    KingFaris11, teej107 and Konato_K like this.
Thread Status:
Not open for further replies.

Share This Page