44
\$\begingroup\$

Given a string input, write a program that prints a truthy value to STDOUT or equivalent if the input is a valid UUID, without using regexes.

A valid UUID is

32 hexadecimal digits, displayed in five groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 alphanumeric characters and four hyphens).

Source

Test Cases

0FCE98AC-1326-4C79-8EBC-94908DA8B034
    => true
00000000-0000-0000-0000-000000000000
    => true
0fce98ac-1326-4c79-8ebc-94908da8b034
    => true
0FCE98ac-1326-4c79-8EBC-94908da8B034
    => true

{0FCE98AC-1326-4C79-8EBC-94908DA8B034}
    => false (the input is wrapped in brackets)
0GCE98AC-1326-4C79-8EBC-94908DA8B034
    => false (there is a G in the input)
0FCE98AC 1326-4C79-8EBC-94908DA8B034
    => false (there is a space in the input)
0FCE98AC-13264C79-8EBC-94908DA8B034
    => false (the input is missing a hyphen)
0FCE98AC-13264-C79-8EBC-94908DA8B034
    => false (the input has a hyphen in the wrong place)
0FCE98ACD-1326-4C79-8EBC-94908DA8B034
    => false (one of the groups is too long)
0FCE98AC-1326-4C79-8EBC-94908DA8B034-
    => false (has a trailing hyphen)
0FCE98AC-1326-4C79-8EBC-94908DA8B034-123
    => false (too many groups)
0FCE98AC13264C798EBC94908DA8B034
    => false (there is no grouping)

Rules

  • Regular Expressions are not allowed
  • Literal pattern matching which is like a regex is not allowed. For example, using [0-9a-fA-F] or other hexadecimal identifiers (we'll call this n) and then matching nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn or n[8]-n[4]-n[4]-n[4]-n[12] is not allowed
  • The input may either be taken from STDIN or as an argument to a function
  • The input is case insensitive
  • It is safe to assume that the input will not contain linefeeds or newlines.
  • The input may contain any printable ASCII characters (spaces included)
  • A truthy value must be printed to STDOUT or equivalent if the input is a valid uuid
  • A falsey value must be printed to STDOUT or equivalent if the input is not a valid uuid
  • If using a function, instead of using STDOUT, the output can be the return value of the function
  • The truthy/falsey value cannot be printed to STDERR.
  • Standard loopholes apply
  • This is , so the shortest program in bytes wins. Good luck!

Leaderboard

This is a Stack Snippet that generates both a leaderboard and an overview of winners by language.

To ensure your answer shows up, please start your answer with a headline using the following Markdown template

## Language Name, N bytes

Where N is the size, in bytes, of your submission

If you want to include multiple numbers in your header (for example, striking through old scores, or including flags in the byte count), just make sure that the actual score is the last number in your header

## Language Name, <s>K</s> X + 2 = N bytes

var QUESTION_ID=66496;var OVERRIDE_USER=20634;function answersUrl(e){return"//api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"//api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(-?\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

\$\endgroup\$
  • 32
    \$\begingroup\$ Poor Retina. D: \$\endgroup\$ – BrainSteel Dec 13 '15 at 19:44
  • \$\begingroup\$ Related \$\endgroup\$ – FryAmTheEggman Dec 13 '15 at 20:44
  • 8
    \$\begingroup\$ Just for reference, I can come up with a 28-byte Retina solution. (So not a crazy advantage over the golfing languages although it would currently be leading.) \$\endgroup\$ – Martin Ender Dec 13 '15 at 22:53
  • 5
    \$\begingroup\$ Are Lua's patterns allowed? They are certainly not regular expressions. \$\endgroup\$ – manatwork Dec 14 '15 at 9:50
  • 1
    \$\begingroup\$ @JacobKrall I'm pretty sure it's "falsy" to all of your questions. I think the challenge is fairly clear that only strings of the form \h{8}-\h{4}-\h{4}-\h{4}-\h{12} (where \h is a hexadecimal digit) are valid. \$\endgroup\$ – Martin Ender Dec 15 '15 at 17:24

31 Answers 31

15
\$\begingroup\$

CJam, 31 30 29 bytes

8 4__C]Nf*'-*qA,s'G,_el^+Ner=

Run all test cases here.

Explanation

Instead of pattern matching the input directly, we're first transforming it to a simpler form which can be easily compared against a single pattern string.

8 4__C] e# Push the array of segment lengths, [8 4 4 4 12].
Nf*     e# Turn that into strings of linefeeds of the given length.
'-*     e# Join them by hyphens, giving "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN".
q       e# Read the input.
A,s     e# Push the string "0123456789".
'G,_el^ e# Push the string "ABCDEFabcdef".
+       e# Concatenate the two strings.
N       e# Push a linefeed.
er      e# Replace all hexadecimal digits with linefeeds.
=       e# Check for equality with the pattern string.
\$\endgroup\$
23
\$\begingroup\$

JavaScript ES6, 73 55 56 chars

s=>s.split`-`.map(x=>x.length+`0x${x}0`*0)=="8,4,4,4,12"

The previous 55 chars version has a problem with trailing spaces in group:

s=>s.split`-`.map(x=>x.length+("0x"+x)*0)=="8,4,4,4,12"
// "00000000-0000-0000-000 -000000000000" true

Test:

f=s=>s.split`-`.map(x=>x.length+`0x${x}0`*0)=="8,4,4,4,12"
;`0FCE98AC-1326-4C79-8EBC-94908DA8B034
0fce98ac-1326-4c79-8ebc-94908da8b034
0FCE98ac-1326-4c79-8EBC-94908da8B034
0GCE98AC-1326-4C79-8EBC-94908DA8B034
0FCE98AC-13264C79-8EBC-94908DA8B034
0FCE98AC-13264-C79-8EBC-94908DA8B034
0FCE98ACD-1326-4C79-8EBC-94908DA8B034
0FCE98AC-1326-4C79-8EBC-94908DA8B034-123
00000000-0000-0000-0000-000000000000
D293DBB2-0801-4E60-9141-78EAB0E298FF
0FCE98AC-1326-4C79-8EBC-94908DA8B034-
00000000-0000-0000-000 -000000000000`.split(/\n/g).every(s=>f(s)==/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.test(s))
\$\endgroup\$
  • \$\begingroup\$ Awesome (and withering for me) +1 \$\endgroup\$ – edc65 Dec 13 '15 at 23:06
  • \$\begingroup\$ @edc65, what do you mean by "withering"? \$\endgroup\$ – Qwertiy Dec 13 '15 at 23:29
  • \$\begingroup\$ Awesome abuse of implicit casting +1 \$\endgroup\$ – Downgoat Dec 14 '15 at 1:00
  • 4
    \$\begingroup\$ scornful, contemptuous, scathing, stinging, devastating, humiliating, mortifying - (comparing your answer to mine) \$\endgroup\$ – edc65 Dec 14 '15 at 7:42
11
\$\begingroup\$

PowerShell, 29 21 84 49 37 Bytes

param($g)@{36=$g-as[guid]}[$g.length]

Many thanks to the folks in the comments assisting with this golfing to keep up with the changing rules -- TessellatingHeckler, iFreilicht, Jacob Krall, and Joey. Please see the edit history for revisions and older versions.

This revision takes input as $g, then creates a new hash table @{} with one element, index 36 is set equal to $g-as[guid]. This uses the built-in -as operator to attempt conversion between two .NET data types -- from [string] to [guid]. If the conversion is successful, a [guid] object is returned, else $null is returned. This portion ensures that the input string is a valid .NET GUID.

The next step is to index into the hash table with [$g.length]. If $g is not exactly 36 characters in length, the hash table will return $null, which will be output as a falsey value. If $g is 36 characters in length, then the result of the .NET call will be output. If $g is not a valid .NET GUID (in any form), then it will output $null as a falsey value. Otherwise it will output a .NET GUID object as a truthy value - the only way that can be output is if it matches the requested format of the challenge.

Examples

Here I am encapsulating the script call in parens and explicitly casting as a Boolean for clarity.

PS C:\Tools\Scripts\golfing> [bool](.\check-if-a-uuid-is-valid.ps1 '0FCE98AC-1326-4C79-8EBC-94908DA8B034')
True

PS C:\Tools\Scripts\golfing> [bool](.\check-if-a-uuid-is-valid.ps1 '0FCE98AC-1326-4C79-8EBC-94908DA8B034D')
False

PS C:\Tools\Scripts\golfing> [bool](.\check-if-a-uuid-is-valid.ps1 '0FCE98AC13264C798EBC94908DA8B034')
False
\$\endgroup\$
  • 4
    \$\begingroup\$ I'll throw in !!($args[0]-as[guid]) at 21 bytes. \$\endgroup\$ – TessellatingHeckler Dec 15 '15 at 0:01
  • 2
    \$\begingroup\$ Couldn't you just save 4 bytes by leaving out the !!()? As the values $NULL and [guid] are mutually exclusive, they qualify to represent truthey and falsey values, don't they? Anyway, great way to convert to boolean, love the solution! \$\endgroup\$ – iFreilicht Dec 15 '15 at 14:42
  • \$\begingroup\$ @iFreilicht that is a point; looking at the linked "truthy/falsey interpretation" post - I agree it looks valid. \$\endgroup\$ – TessellatingHeckler Dec 15 '15 at 16:43
  • 1
    \$\begingroup\$ This solution incorrectly returns True for 0FCE98AC13264C798EBC94908DA8B034, which has no hyphens \$\endgroup\$ – Jacob Krall Dec 15 '15 at 16:45
  • 1
    \$\begingroup\$ @TessellatingHeckler Nope, too good to be true. Appending a digit, such as 0FCE98AC-1326-4C79-8EBC-94908DA8B034D (extra D on the end) returns falsey $TRUE, since it just trims the offending digit away and the first 36 characters are valid. \$\endgroup\$ – AdmBorkBork Dec 16 '15 at 14:12
9
\$\begingroup\$

Emacs Lisp, 236 Bytes

(lambda(s)(and(eq(string-bytes s)36)(let((l(string-to-list s))(i 0)(h '(8 13 18 23))(v t))(dolist(c l v)(set'v(and v(if(member i h)(and v(eq c 45))(or(and(> c 47)(< c 58))(and(> c 64)(< c 91))(and(> c 96)(< c 123))))))(set'i(+ i 1))))))

Ungolfed:

(lambda (s)
  (and (eq (string-bytes s) 36) ; check length
       (let ((l (string-to-list s))
             (i 0)
             ; location of hyphens
             (h '(8 13 18 23))
             (v t))
         (dolist (c l v)
           (set 'v (and v (if (member i h)      ; check if at hyphen position
                              (and v (eq c 45)) ; check if hyphen
                            (or (and (> c 47) (< c 58))      ; check if number
                                (and (> c 64) (< c 91))      ; check if upper case letter
                                (and (> c 96) (< c 123)))))) ; check if lower case letter
           (set 'i (+ i 1)))))) ; increment
\$\endgroup\$
8
\$\begingroup\$

Due to changes to the rules, this answer is no longer competitive :(

C, 98

main(a,n){printf("%d",scanf("%8x-%4hx-%4hx-%4hx-%4hx%8x%n%c",&a,&a,&a,&a,&a,&a,&n,&a)==6&&n==36);}

Mostly fairly self explanatory. The %n format specifier gives the number of bytes read so far, which should be 36. scanf() returns the number of matched items, which should be 6. The final %c should not match anything. If it does, then there is trailing text, and scanf() will return 7.

Compile with -w to suppress the pesky warnings (there are several).

\$\endgroup\$
6
\$\begingroup\$

JavaScript ES6, 70 83

NOTE thx to @Qwertiy for finding a bug (and suggesting some improvements and fixes)

Thx @CᴏɴᴏʀO'Bʀɪᴇɴ 2 bytes saved

Other 9 bytes saved simplifiying the length check (the complex way was shorter in the first draft, but not now)

u=>u.split`-`.every((h,l,u)=>u[4]&&-`0x${h}1`&&h.length-'40008'[l]==4)

Explained

u=>u.split`-` // make an array splitting at '-'
.every( // for every element the following must be true
 (h,l,u)=> // h is the element, l is the index, u is the whole array
 u[4] // element 4 must be present (at least 5 element in array)
 && -`0x${h}1` // element must be a valid hex string with no extraneous blanks (else NaN that is falsy)
 // get requested length from index (8,4,4,4,12 sub 4 to put in 1 char)
 // a 6th elements will be rejected as undefined != 4
 && h.length-'40008'[l]==4// then check element length
)

Test snippet

f=u=>u.split`-`.every((h,l,u)=>u[4]&&-`0x${h}1`&&h.length-'40008'[l]==4)

console.log=x=>O.innerHTML+=x+'\n'

;[
  ['0FCE98AC-1326-4C79-8EBC-94908DA8B034',true],
  ['0fce98ac-1326-4c79-8ebc-94908da8b034',true],
  ['0FCE98ac-1326-4c79-8EBC-94908da8B034',true],
  ['00000000-0000-0000-0000-000000000000', true],
  ['ffffffff-ffff-ffff-ffff-ffffffffffff', true],
  ['0GCE98AC-1326-4C79-8EBC-94908DA8B034',false],
  ['0FCE98AC-13264C79-8EBC-94908DA8B034',false],
  ['0FCE98AC-13264-C79-8EBC-94908DA8B034',false],
  ['0FCE98ACD-1326-4C79-8EBC-94908DA8B034',false],
  ['0FCE98AC-1326-4C79-8EBC',false],
  ['0FCE98AC-1326-4C79-8EBC-94908DA8B034-',false],
  ['00000000-0000-0000-000 -000000000000', false],
  ['0FCE98AC-1326-4C79-8EBC-94908DA8B034-123',false],
].forEach(x=>{
  var t=x[0], r=f(t), k=x[1]
  console.log('Test '+t+' result '+r+(r==k?' ok':' fail'))
})
<pre id=O></pre>

\$\endgroup\$
  • \$\begingroup\$ -1-('0x'+h) => 1/('0x'+h) \$\endgroup\$ – Qwertiy Dec 13 '15 at 23:27
  • \$\begingroup\$ Same problem as in my previous version: true for 00000000-0000-0000-000 -000000000000 \$\endgroup\$ – Qwertiy Dec 14 '15 at 20:23
  • \$\begingroup\$ No. But y've added a wrong test. Leading space is not a problem, but trailing one is, as a string is trimmed when converting to number. See my comment above with a test. \$\endgroup\$ – Qwertiy Dec 14 '15 at 20:37
  • \$\begingroup\$ '00000000-0000-0000-000 -000000000000' now I see it @Qwertiy I missed the 3 zeros instead of 4 \$\endgroup\$ – edc65 Dec 14 '15 at 20:43
  • 1
    \$\begingroup\$ @Stefnotch i don't agree. Outside the every call u is a string, not an array \$\endgroup\$ – edc65 Dec 15 '15 at 19:40
5
\$\begingroup\$

Due to changes to the rules, this answer is no longer competitive :(

Pure Bash (no external utilities), 78

printf -vv %8s-%4s-%4s-%4s-%12s
p=${v// /[[:xdigit:]]}
[ "$1" -a ! "${1/$p}" ]

Takes input from the command line.

  • The printf builds the following string - - - -.
  • The p= line transforms this to the following pattern: [[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]. Note this looks an awful lot like a regular expression. However, it is not in this context. It is a pattern for shell pattern matching. This is similar in concept to a regular expression, but is a different construct (and syntax).
  • The last line checks if
    • the input is non-empty
    • if pulling the pattern out of the input string produces an empty string

Idiomatic to shell, a return code of 0 indicates success/TRUE and 1 indicates failure/FALSE. The return code may be inspected with echo $? after running the script.

\$\endgroup\$
  • 1
    \$\begingroup\$ The shell pattern matching may not follow the syntax of regex, but the character class definitely uses the definition and syntax from POSIX regex. Anyway, it's up to OP to decide whether it's acceptable. \$\endgroup\$ – n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Dec 14 '15 at 3:38
  • 1
    \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Regular expressions may include Posix character classes, but I don't think that implies that anything that uses a Posix character class is a regex. As another example tr also uses Posix character classes, but is not a regex parser. \$\endgroup\$ – Digital Trauma Dec 14 '15 at 8:18
  • \$\begingroup\$ You can shave off a few characters by avoiding useless double quotes, e.g. in the printf format. \$\endgroup\$ – Jens Dec 14 '15 at 14:04
  • \$\begingroup\$ I've updated the challenge to clarify - the reason regexes were disallowed was to make it so hex patterns could not be used to match uuids \$\endgroup\$ – Jojodmo Dec 14 '15 at 19:40
4
\$\begingroup\$

Jolf, 32 bytes

Try it here!

 eGi'-DN&bH*28=lH.[8,4,4,4,12]S}
 e                               Property "e"very of next object
  Gi'-                           Split i at hyphen
      DN                       } every comparison function
        &                        logical conjugation of next two arguments
         bH*28                   base 16 of H (first arg); is NaN (falsey) if invalid
              =                  equality of next two items
               lH                the length of H (first arg)
                 .            S  the Sth (index) member of the object inbetween
                  [8,4,4,4,12]   array of lengths

Because of an error in my code, this is longer than it should be. :( [8,4,4,4,12] should be the same as {8444*26}, but } is also the closing of a function :P

\$\endgroup\$
  • 2
    \$\begingroup\$ So it is longer than it has to be because you have ambiguous commands in the language you created? :P \$\endgroup\$ – Rɪᴋᴇʀ Dec 14 '15 at 2:12
  • \$\begingroup\$ @RikerW Minor semantic mistake. It is fixed now. \$\endgroup\$ – Conor O'Brien Dec 14 '15 at 2:13
4
\$\begingroup\$

MATL, 55 bytes

jttn36=?[9,5,5,5]XsXK)45=?36:Km~)4Y2'A':'F'hm?}F]]]N~1$

I refrained from using the Yb function (strsplit) because it's somewhat similar to regexp(..., 'split'). This only uses indexing and character comparisons.

Example

>> matl
 > jttn36=?[9,5,5,5]XsXK)45=?36:Km~)4Y2'A':'F'hm?}F]]]N~1$
 > 
> This is a test
0

>> matl
 > jttn36=?[9,5,5,5]XsXK)45=?36:Km~)4Y2'A':'F'hm?}F]]]N~1$
 > 
> D293DBB2-0801-4E60-9141-78EAB0E298FF
1

Explanation

jt                     % input string, duplicate
tn36=?                 % if length is 36
  [9,5,5,5]XsXK        % build and copy indices of required '-' positions
  )45=?                % if those entries are indeed '-'
    36:Km~)            % logical index of remaining positions
    4Y2'A':'F'h        % allowed chars in those positions
    m?                 % if all those entries are legal: do nothing
    }                  % else
      F                % false value
    ]                  % end
  ]                    % end
]                      % end
N~                     % true if stack is empty
1$                     % display last result only
\$\endgroup\$
3
\$\begingroup\$

CJam, 52 42 bytes

qeu__{A7*)<},\'-/83 3b{)4*}%.{\,=}[1]5*=*=

Try it online. Outputs the original string if true, outputs empty string if false (this is allowed).

Explanation:

qeu__                                      e# Take input, make 2 copies
     {A7*)<},\                             e# Remove invalid characters from first copy
              '-/                          e# Split top of stack on '-
                 83 3b{)4*}%               e# Array of group lengths: [8 4 4 4 12]
                            .{\,=}[1]5*=   e# Compare two arrays, return true if group lengths are correct
                                        *= e# Multiply this value by original string (0 = empty string, 1 = same string)
\$\endgroup\$
  • \$\begingroup\$ A7*)< will not remove many invalid characters, like spaces, +, ? ... \$\endgroup\$ – Martin Ender Dec 13 '15 at 22:41
  • \$\begingroup\$ @MartinBüttner oh shoot... I did not realize that, Ill fix it in a few minutes. \$\endgroup\$ – GamrCorps Dec 13 '15 at 22:42
3
\$\begingroup\$

Julia, 86 bytes

s->(t=split(s,"-");map(length,t)==[8,4,4,4,12]&&all(i->!isnull(tryparse(Int,i,16)),t))

This is an anonymous function that accepts a string and returns a boolean. To call it, give it a name, e.g. f=s->....

Ungolfed:

function f(s::AbstractString)
    # Split the input into an array on dashes
    t = split(s, "-")

    # Ensure the lengths are appropriate
    ok1 = map(length, t) == [8, 4, 4, 4, 12]

    # Ensure each element is a valid hexadecimal number
    ok2 = all(i -> !isnull(tryparse(Int, i, 16)), t)

    return ok1 && ok2
end
\$\endgroup\$
3
\$\begingroup\$

C# 196 bytes

using System.Linq;class P{bool T(string v){var r=v.Length==36;for(var i=0;i<v.Length;i++)r&=new[]{8,13,18,23}.Any(t=>t==i)?v[i]=='-':v[i]>47&&v[i]<58|v[i]>64&&v[i]<71|v[i]>96&&v[i]<103;return r;}}

Ungolfed:

using System.Linq;
class P
{
    public bool T(string v)
    {
        var r = v.Length == 36;
        for (var i = 0; i < v.Length; i++)
            r &= new[] { 8, 13, 18, 23 }.Any(t => t == i) 
                ? v[i] == '-' 
                : v[i] > 47 && v[i] < 58 | v[i] > 64 && v[i] < 71 | v[i] > 96 && v[i] < 103;
        return r;
    }
}

Method T can be invoked with any non-null string and will return true for valid GUID's, false otherwise. This is a constant-time validation; at the cost of three chars you can early-exit the method (change i < v.Length to i < v.Length && r).

Will try to get the bytecount down further later.

I've obviously left out the Guid.ParseExact way because where's the fun in that? Here it is, without much attempt to golf it down further in 86 bytes:

using System;class P{bool T(string v){Guid x;return Guid.TryParseExact(v,"D",out x);}}

Ungolfed:

using System;
class P
{
    bool T(string v)
    {
        Guid x;
        return Guid.TryParseExact(v, "D", out x);
    }
}
\$\endgroup\$
2
\$\begingroup\$

Python 2, 99 112 bytes

def f(u):
 try:u=u.split()[0];int(u.replace('-',''),16);print[8,4,4,4,12]==map(len,u.split('-'))
 except:print 0

On a valid input, it prints True. On an invalid input it prints False or 0, depending on why it was invalid. False and 0 are both falsey in Python.

The function has to check 3 things:

  • Every non-hyphen character is a digit or is in ABCDEF
  • There are exactly 4 hyphens
  • There are 8 characters before the first hyphen, 12 after the last, and 4 between any other two

Here's a breakdown to show how it checks for them. It's slightly out of date but I'm hungry so I'll update it later.

def f(u):
    try:
        int(u.replace('-',''),16) # Remove all hyphens from the string and parse what's
                                  # left as a base 16 number. Don't do anything with this
                                  # number, but throw an exception if it can't be done.

        return[8,4,4,4,12]==map(len,u.split('-')) # Split the string at each hyphen and
                                                  # get the length of each resulting
                                                  # string. If the lengths == [8,4,4,4,12],
                                                  # there are the right number of groups
                                                  # with the right lengths, so the string
                                                  # is valid.
    except:
        return 0 # The only way to get here is if the string (minus hyphens) couldn't be
                 # parsed as a base 16 int, so there are non-digit, non-ABCDEF characters
                 # and the string is invalid.
\$\endgroup\$
  • \$\begingroup\$ I guess you can save 2 bytes if you replace both instances of return with print. (In which case you'll definitely want to be in Python 2, because print works differently in Python 3.) \$\endgroup\$ – mathmandan Dec 14 '15 at 7:45
  • 1
    \$\begingroup\$ This doesn't work in Python 3, because map now returns a "map object", not a list. \$\endgroup\$ – Tim Pederick Dec 14 '15 at 9:27
  • \$\begingroup\$ This doesn't work in python 2 (probably 3 too) because the int function allows spaces - 0FCE98ac-1326-4c79-8EBC-94908da8B03 with a trailing space. See comment in deleted Pyth answer here if able. \$\endgroup\$ – Blue Dec 14 '15 at 19:02
2
\$\begingroup\$

Python 2, 57 bytes

Thank goodness for built-in! - make sure to enclose strings in quotes.

import uuid
try:uuid.UUID(input());print 1
except:print 0
\$\endgroup\$
  • 5
    \$\begingroup\$ According to the docs you linked to, that would print 1 for input 12345678123456781234567812345678. \$\endgroup\$ – Dennis Dec 14 '15 at 6:00
  • \$\begingroup\$ if this worked you'd be able to save bytes by doing try:print uuid.UUID(input()) because all that's required is you print a truthy value \$\endgroup\$ – undergroundmonorail Dec 14 '15 at 13:58
  • 2
    \$\begingroup\$ This program accepts many UUID formats, but the question wants only the 36-character UUID format, with hyphens. \$\endgroup\$ – Jacob Krall Dec 15 '15 at 16:49
  • 2
    \$\begingroup\$ You could salvage this, coping with the updated rules, by checking if the input string is equal to the uuid converted back to string. Gives you a truthy value right away. \$\endgroup\$ – agtoever Dec 16 '15 at 21:36
2
\$\begingroup\$

Pyth, 39 bytes

&&!+1xzd.xi:zK\-k16ZqxKc+zK1mid36"8dinz

Try it here.

\$\endgroup\$
  • \$\begingroup\$ Your "try it here" link is missing the \ character in K\-k, so it fails to run as is. \$\endgroup\$ – Alex Dec 15 '15 at 10:30
  • \$\begingroup\$ That has been fixed now \$\endgroup\$ – Blue Dec 15 '15 at 11:38
2
\$\begingroup\$

Perl 6,  83  67 bytes

# 83 bytes
{
  (
    my@a=.uc.split('-')
  ).map(*.comb)⊆('0'..'9','A'..'F')
&&
  @a».chars~~(8,4,4,4,12)
}

# 67 bytes
{
  (
    $/=.split('-')
  ).map({:16($_)//|()})==5
&&
  $/».chars~~(8,4,4,4,12)
}

( counts do not include newlines or indents as they are not needed )

usage:

# give it a name
my &code = {...}

say map &code, «
  D293DBB2-0801-4E60-9141-78EAB0E298FF
  0FCE98AC-1326-4C79-8EBC-94908DA8B034
  0fce98ac-1326-4c79-8ebc-94908da8b034
  0FCE98ac-1326-4c79-8EBC-94908da8B034
  00000000-1326-4c79-8EBC-94908da8B034
»;
# (True True True True True)

say map &code, «
  0GCE98AC-1326-4C79-8EBC-94908DA8B034
 '0FCE98AC 1326-4C79-8EBC-94908DA8B034'
  0FCE98AC-13264C79-8EBC-94908DA8B034
  0FCE98AC-13264-C79-8EBC-94908DA8B034
  0FCE98ACD-1326-4C79-8EBC-94908DA8B034
  0FCE98AC-1326-4C79-8EBC-94908DA8B034-
  0FCE98AC-1326-4C79-8EBC-94908DA8B034-123
»;
# (False False False False False False False)
\$\endgroup\$
2
\$\begingroup\$

Common Lisp - 161

(lambda(s &aux(u(remove #\- s)))(and(=(length s)36)(=(length u)32)(every(lambda(p)(char=(char s p)#\-))'(8 13 18 23))(ignore-errors(parse-integer u :radix 16))))

The returned value if true is the hash, as a number, which is a useful result to have.

Ungolfed

(defun uuid-p (string &aux (undashed (remove #\- string)))
  (and
   ;; length of input string must be 36
   (= (length string) 36)

   ;; there are exactly 4 dashes
   (= (length undashed) 32)

   ;; We check that we find dashes where expected
   (every (lambda (position)
            (char= (char string position) #\-))
          '(8 13 18 23))

   ;; Finally, we decode the undashed string as a number in base 16,
   ;; but do not throw an exception if this is not possible.
   (ignore-errors
    (parse-integer undashed :radix 16))))
\$\endgroup\$
  • \$\begingroup\$ @Jojodmo Yes, definitely! Thanks \$\endgroup\$ – coredump Dec 17 '15 at 6:43
2
\$\begingroup\$

F# 44 characters

fun s->System.Guid.TryParseExact(s,"D")|>fst

In F#, functions with out parameters can be called by omitting the out parameter; its value at return will be combined with the function's true return value into a tuple.

Here, the tuple is piped to the fst function, which returns its first member, which in this case is the Boolean return value of TryParseExact, indicating the success or failure of the call.

As a check for the correct format, we return true only if the string is 36 characters long.

Before I saw RobIII's C# answer, I had not thought of using TryParseExact, so my answer was to have been three characters longer:

fun s->System.Guid.TryParse s|>fst&&s.Length=36

TryParse(string, Guid) accepts input in the following formats:

00000000000000000000000000000000 
00000000-0000-0000-0000-000000000000 
{00000000-0000-0000-0000-000000000000} 
(00000000-0000-0000-0000-000000000000)
{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}

Of these, only the second is 36 characters long.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 93 89 85 bytes

lambda u:(set(u)<=set("-0123456789abcdefABCDEF"))*map(len,u.split("-"))==[8,4,4,4,12]

The map() call guarantees that the sections are of the right lengths, and the all() tests each character for being either a hyphen or an arbitrary-case hex digit. The generator expression is testing each character by iterating through that entire string, so it's not the most performant method, I'm afraid, but it should satisfy the test cases:

>>> f=lambda u:(set(u)<=set("-0123456789abcdefABCDEF"))*map(len,u.split("-"))==[8,4,4,4,12]
>>> testcases = """\
... D293DBB2-0801-4E60-9141-78EAB0E298FF
... 0FCE98AC-1326-4C79-8EBC-94908DA8B034
... 0fce98ac-1326-4c79-8ebc-94908da8b034
... 0FCE98ac-1326-4c79-8EBC-94908da8B034
... 00000000-0000-0000-0000-000000000000""".splitlines()
>>> failcases = """\
... 0GCE98AC-1326-4C79-8EBC-94908DA8B034
... 0FCE98AC 1326-4C79-8EBC-94908DA8B034
... 0FCE98AC-13264C79-8EBC-94908DA8B034
... 0FCE98AC-13264-C79-8EBC-94908DA8B034
... 0FCE98ACD-1326-4C79-8EBC-94908DA8B034
... 0FCE98AC-1326-4C79-8EBC-94908DA8B034-
... 0FCE98AC-1326-4C79-8EBC-94908DA8B034-123
... 00000000-0000-0000-000 -000000000000
... 00000000-0000-0000- 000-000000000000""".splitlines()
>>> all(f(u) for u in testcases)
True
>>> any(f(u) for u in failcases)
False
>>> 
\$\endgroup\$
  • \$\begingroup\$ Anybody have any idea why the shortest correct Python answer has been downvoted? Not enough explanation? \$\endgroup\$ – rsandwick3 Dec 14 '15 at 23:17
  • \$\begingroup\$ Jojodmo -- in case there was confusion about this, I did not reject your proposed change -- I had already made an edit because I had missed the A-F characters (I copied from a window where I was testing negative cases), and Community auto-rejected your proposal without me knowing it had even been raised. By the time I knew you'd proposed it, @nimi had already made the correction to the heading. I very much hope that has nothing to do with the downvote, as that would reflect very poorly on this community. Anyway, I'll assume it's the other thing, and add a bit more explanation. \$\endgroup\$ – rsandwick3 Dec 14 '15 at 23:43
  • 2
    \$\begingroup\$ You can remove the f= and the spaces around the string in the all block. \$\endgroup\$ – FryAmTheEggman Dec 14 '15 at 23:48
  • \$\begingroup\$ oh awesome, nice catch -- edited \$\endgroup\$ – rsandwick3 Dec 14 '15 at 23:56
  • 1
    \$\begingroup\$ You can save 8 (or 6, you might need to add parenthesis) bytes by converting your all(..) to set inclusion testing: set(u)<=set("-0123456789abcdefABCDEF"). \$\endgroup\$ – Mathias Ettinger Dec 16 '15 at 14:50
1
\$\begingroup\$

SAS, 171 144 141

data;infile stdin;file stdout;input a$9b$14c$19d$24;e=(a!!b!!c!!d='----')*length(_infile_)=36*(1-missing(put(input(compress(_infile_,,'adk'),$hex32.),$hex32.)));put e;run;

Actually uses stdin and stdout - one of the lesser-known features of this particular language. Works for the examples given so far, but possibly not in all cases. Can probably be improved upon.

Better approach - one character at a time:

data;infile stdin;file stdout;do i=1 to 37;input@i c$1.@;a+ifn(i in(9,14,19,24),c='-',n(input(c,hex.))-36*(i>36&c^=''));end;b=a=36;put b;run;

Golfed another 6 characters off the central expression!

Ungolfed:

data;
infile stdin;
file stdout;
do i=1 to 37;
input@i c$1.@;
a+ifn(i in(9,14,19,24),c='-',n(input(c,hex.))-36*(i>36&c^=''));
end;
b=a=36;
put b;
run;

This generates quite a few warnings and notes in the log, but it doesn't print them to stdout or stderr, so I think this is fair game.

\$\endgroup\$
1
\$\begingroup\$

C, 391 bytes

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define F printf("0")
#define T printf("1")
#define E return 0
main(){char s[99],*t;int k=1,l,i;scanf("%99[^\n]",s);if(s[strlen(s)-1]=='-'){F;E;}t=strtok(s,"-");while(t!=NULL){for(i=0,l=0;t[i]!=0;i++,l++){if(!isxdigit(t[i])){F;E;}}if((k==1&&l!=8)||((k>1&&k<5)&&l!=4)||(k==5&&l!=12)){F;E;}k++;t=strtok(NULL,"-");}if(k==6){T;E;};F;}
\$\endgroup\$
1
\$\begingroup\$

MATLAB, 126 bytes

function f(a)
b='-';if length(a)==36&&a(9)==b&&a(13)==b&&a(17)==b&&a(21)==b;a(a==b)=[];if any(isnan(hex2dec(a)));0;end;1;end;0
\$\endgroup\$
1
\$\begingroup\$

Python 3, 134 bytes

def a(i):
 try:l=[1+int(k,16)and(len(k)==c)for k,c in zip(i.split("-"),[8,4,4,4,12])];return(len(l)==5)&(0 not in l)
 except:return 0

int(k,16) tries to cast k to a base-16 int. On a character other than 0-9a-fA-F- it fails, in which case we return 0, which is falsy. Add 1 to that int and we get a guaranteed truthy value - we've stripped away all hyphens with str.split() so we can't get the value -1 and all non-0 ints are truthy.

\$\endgroup\$
1
\$\begingroup\$

C function, 102

A rule change disallowed my previous c scanf()-based answer, so here's another c answer using isxdigit() which I think should be allowed to compete:

i;f(char *s){for(i=8;i<24;i+=5)s[i]=s[i]-45?1:s[i]+3;for(i=0;isxdigit(s[i]);i++);return i==36&&!s[i];}

Try it online.

  • Check for - characters (ASCII 45) at the relevant positions - if so, replace them with 0s (ASCII 48 (=45+3))
  • Walk the string checking each char with isxdigit()
  • Return TRUE if string length is 36 and final character is NUL.
\$\endgroup\$
1
\$\begingroup\$

Batch, 148 139 + 2 = 150 141 bytes

@set/pu=
@for %%d in (1 2 3 4 5 6 7 8 9 A B C D E F)do @set u=!u:%%d=0!
@if -!u!==-00000000-0000-0000-0000-000000000000 exit/b0
@exit/b1

Added 2 bytes because you need to use the /v switch to CMD.EXE.

Exits with ERRORLEVEL 0 on success, 1 on failure.

Edit: Saved some bytes mainly because := is case insensitive but there were other tweaks too.

\$\endgroup\$
1
\$\begingroup\$

Java, 345 bytes

interface q{static void main(String[]a){int i=-1;char[]b=a[0].toCharArray();java.io.PrintStream u=System.out;if(b.length>36||b.length<36)u.print(1<0);if(b[8]!='-'||b[13]!='-'||b[18]!='-'||b[23]!='-')u.print(1<0);while(++i<b.length){if(i!=8&&i!=13&&i!=18&&i!=23){if(!((b[i]>='0'&&b[i]<='F')||(b[i]>='a'&&b[i]<='f')))u.print(1<0);}}u.print(1>0);}}

Input is first command line argument. Output is error code(0 means valid UUID, 1 means not valid)

Ungolfed with comments:

interface q {
    static void main(String[] a) {
        int i = -1;                                                             // Index
        char[] b = a[0].toCharArray();                                          // Characters from input
        java.io.PrintStream u = System.out;                                     // STDOUT
        if (b.length > 36||b.length < 36)                                       // If input length is not 36
            u.print(1<0);                                                       // Invalid
        if (b[8]!='-'||b[13]!='-'||b[18]!='-'||b[23]!='-')                      // If hasn't got separators at correct positions
            u.print(1<0);                                                       // Invalid
        while (++i<b.length) {                                                  // Iterate over all characters
            if (i!=8 && i!=13 & i!=18 && i!=23) {                               // If not at separator indexes
                if ( !( (b[i]>='0'&&b[i]<='F') || (b[i]>='a'&&b[i]<='f') ))     // If incorrect hexadecimal number
                    u.print(1<0);                                               // Invalid
            }
        }
        u.print(1>0);                                                           // Valid
    }
}

EDIT: Didn't notice the STDOUT part. Oops, fixed now.

\$\endgroup\$
  • \$\begingroup\$ Nice! You can replace if(b.length>36||b.length<36) with simply if(b.length!=36). Also, because you can print truthy values, you could simply print 0 instead of 1<0, and 1 instead of 1>0. \$\endgroup\$ – Jojodmo Mar 14 '17 at 1:44
  • \$\begingroup\$ @Jojodmo Based on the votes, a truthy value is in the form of if(truthy_value){ doSomethingOnYes(); } else{ doSomethingOnFalse(); } So in Java a boolean is a truthy value, but 1 or 0 isn't. Only when the OP of a challenge says something like: "Your output can be true/false, 0/1, empty/non-empty; as long as you specify what you use." then you can indeed use 0 and 1 instead of true/false as truthy/falsey value. \$\endgroup\$ – Kevin Cruijssen Apr 14 '17 at 13:15
  • 1
    \$\begingroup\$ As for golfing tips for cookie: @Jojodmo is indeed right with replacing it for if(b.length!=36); || can be | on multiple places, as well as && to &; if(...!='-') can be if(...!=45); int i=-1; ... while(++i<b.length){ can be replaced with for(int i=-1;++i<b.length;){; 'F' can be 70 ('f' could be 102, but that doesn't matter since it's the same byte-count). I like how you've used java.io.PrintStream u=System.out; btw, I should remember that one! So thanks. \$\endgroup\$ – Kevin Cruijssen Apr 14 '17 at 13:19
1
\$\begingroup\$

Swift 3, 50 bytes

Pass in a string s

import Foundation
print(UUID(uuidString:s) != nil)
\$\endgroup\$
1
\$\begingroup\$

PHP, 109 Bytes

prints 1 for true and 0 for false

for($t=($l=strlen($a=$argn))==36;$i<$l;$i++)$t*=$i>7&$i<24&!($i%5-3)?$a[$i]=="-":ctype_xdigit($a[$i]);echo$t;

$i>7&$i<24&!($i%5-3) is 5 Bytes shorter then in_array($i,[8,13,18,23])

112 Bytes

echo array_filter(str_split($argn),function($i){return!ctype_xdigit($i);})==[8=>"-",13=>"-",18=>"-",23=>"-"]?:0;

113 Bytes

echo array_diff(str_split(strtolower($argn)),array_map(dechex,range(0,15)))==[8=>"-",13=>"-",18=>"-",23=>"-"]?:0;
\$\endgroup\$
0
\$\begingroup\$

Java, 172 bytes 168 bytes (Thanks Wheat Wizard)

Kinda cheaty since I used java.util.UUID, but here goes:

import java.util.UUID;class ValidUUID{public static void main(String[] a){try{UUID.fromString(a[0]);System.out.println(1);}catch(Exception e){System.out.println(0);}}}

Ungolfed version:

import java.util.UUID;

class ValidUUID {

    public static void main(String[] a) {
        try {
            UUID.fromString(a[0]);
            System.out.println(1);
        } catch(Exception e) {System.out.println(0);}
    }
}
\$\endgroup\$
  • \$\begingroup\$ Welcome to the site! I think you can remove the space between try and {. \$\endgroup\$ – Sriotchilism O'Zaic Mar 11 '17 at 18:41
  • \$\begingroup\$ @WheatWizard thank you :D also noticed I can remove the " around 0 and 1 \$\endgroup\$ – ryxn Mar 11 '17 at 19:21
  • 2
    \$\begingroup\$ You should be able to remove the space between String[] and a. Also, you should be able to replace println with print. \$\endgroup\$ – Qwerp-Derp Mar 12 '17 at 7:25
  • 1
    \$\begingroup\$ Class name can be 1 character. You can use java.util.UUID.fromString instead of importing. \$\endgroup\$ – Poke Apr 5 '17 at 13:41
0
\$\begingroup\$

AWK, 98 bytes

BEGIN{FS=""}{for(j=4;k<NF;){h+=(j+=5)<25?$j=="-":0
s+=strtonum("0x"$++k 1)>0}$0=h+s==36&&NF==36}1

Simply splits the line at every character and checks if each character is a hex digit and that there are hyphens at the appropriate places. strtonum converts invalid characters to 0. Making the comparison between 0 and m (and arbitrarily chosen invalid character) require additional steps. Luckily 01 is a valid hex number, but m1 is not.

Initially I wrote two for loops, but I saved 1 byte by squeezing them together. :)

NOTE: GAWK can read input as hex numbers, but this requires a very long command line option.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.