递增GUID


30

受到最近的每日WTF文章的启发...

编写一个程序或函数,该程序或函数采用GUID(格式为的字符串XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,其中每个X代表一个十六进制数字),并输出加一的GUID。

例子

>>> increment_guid('7f128bd4-b0ba-4597-8f35-3a2f2756dfbb')
'7f128bd4-b0ba-4597-8f35-3a2f2756dfbc'
>>> increment_guid('06b86883-f3e7-4f9d-87c5-a047e89a19fa')
'06b86883-f3e7-4f9d-87c5-a047e89a19fb'
>>> increment_guid('89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2cf')
'89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2d0'
>>> increment_guid('89f25f2f-2f7b-4aa6-b9d7-46a98e3cb29f')
'89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2a0'
>>> increment_guid('8e0f9835-4086-406b-b7a4-532da46963ff')
'8e0f9835-4086-406b-b7a4-532da4696400'
>>> increment_guid('7f128bd4-b0ba-4597-ffff-ffffffffffff')
'7f128bd4-b0ba-4598-0000-000000000000'

笔记

  • 与链接的文章不同,递增以F结尾的GUID必须“携带”到前一个十六进制数字。请参阅上面的示例。
  • 您可以假设输入将不是ffffffff-ffff-ffff-ffff-ffffffffffff
  • 对于大于9的十六进制数字,可以使用大写(AF)或小写(af)。
  • 是的,GUID可能以开头0
  • 您的输出必须准确包含32个十六进制数字和4个连字符(以预期的格式),包括任何必需的前导0s。
  • 您不必保留GUID的版本号或其他固定位。假设它只是一个128位整数,其中没有位具有任何特殊含义。同样,假定GUID以直接的字典顺序排序,而不是以Windows GUID结构的二进制顺序排序。
  • 如果写的函数,则输入可以是任何序列OF-的char数据类型:stringchar[]List<char>,等。

1
我们应该保留UUIDv4中的6个固定位不变吗?
Filip Haglund

2
@FilipHaglund:不,只需将GUID视为一个128位数字,其中所有位都没有任何特殊含义。同样,假定GUID以直接的字典顺序排序,而不是以Windows GUID结构的二进制顺序排序。
dan04

3
建议的测试用例:89f25f2f-2f7b-4aa6-b9d7-46a98e3cb29f确保答案可以进行过渡9 -> a
卡米尔·德拉卡里

1
@dana:您可以使用与您的语言等效的C#foreach (char ch in theInput)有效的任何数据类型。
dan04

Answers:


7

05AB1E17 15 18字节

感谢Kevin Cruijssen节省了2个字节

'-K1ìH>h¦Ž¦˜S·£'-ý

在线尝试! 或作为测试套件

说明

'-K                  # remove "-" from input
   1ì                # prepend a 1 (to preserve leading 0s)
     H               # convert from hex to base 10
      >              # increment
       h             # convert to hex from base 10
        ¦            # remove the extra 1
         ަ˜S·       # push [8, 4, 4, 4, 12]
              £      # split into parts of these sizes
               '-ý   # join on "-"

ang,你打败我了。。有非常相似的东西,但是用žKÃ代替'-K。顺便说一句,您可以通过更改•É]•S3+为保存2个字节ަ˜S·
凯文·克鲁伊森

@KevinCruijssen:谢谢!我不知道我多少次忘记了这Ž件事……
艾米娜

我不接受此答案,因为有人指出它将下降前导0。请解决。
dan04

@ dan04:好电话!我没想到。现在应该修复:)
Emigna


11

JavaScript(ES6),85个字节

输出字符串为小写。

s=>(g=(c,x=+('0x'+s[--n])+!!c)=>1/x?g(x>>4)+(x&15).toString(16):~n?g(c)+'-':'')(n=36)

在线尝试!

已评论

s => (                   // s = GUID
  g = (                  // g = recursive function taking:
    c,                   //   c = carry from the previous iteration
    x = +('0x' + s[--n]) //   x = decimal conversion of the current digit
        + !!c            //       add the carry
  ) =>                   //
    1 / x ?              // if x is numeric:
      g(x >> 4) +        //   do a recursive call, using the new carry
      (x & 15)           //   and append the next digit
      .toString(16)      //   converted back to hexadecimal 
    :                    // else:
      ~n ?               //   if n is not equal to -1:
        g(c)             //     do a recursive call, leaving the current carry unchanged
        + '-'            //     and append a hyphen
      :                  //   else:
        ''               //     stop recursion
)(n = 36)                // initial call to g with n = 36 and a truthy carry

5

Python 2,82字节

q='f0123456789abcdef--'
f=lambda s:[str,f][s[-1]in'f-'](s[:-1])+q[q.find(s[-1])+1]

在线尝试!

没有输入或十六进制转换。

这会扫描从字符串的后面,沿着该周期中的每个字符0123456789abcdef,以-将自身。碰到除f或以外的其他符号后-,它将停止向左扫描,仅返回其余部分不变。此解决方案不特定于UUID格式-任何数量的任何数量的十六进制字母的块都可以使用。

基本情况[str,f][s[-1]in'f-'](s[:-1])是我以前从未在高尔夫中使用过的花样。它终止递归没有任何ifandor或其他明确的控制流。

根据[s[-1]in'f-']最后一个字符的条件,代码将返回f(s[:-1])s[:-1]保持不变。由于str是字符串的标识,因此我们可以选择其中一个函数[str,f]并将其应用于s[:-1]。请注意,f如果未选择with,则不会进行递归调用,从而避免了Python急切地评估未使用选项的常见问题,从而导致递归无限递归。


好吧,我的大脑在早上醒来。
不要明亮的

3

APL(Dyalog Unicode),46 字节SBCS

匿名默认前缀功能。

CY'dfns'
(∊1hex 16(|+1⌽=)⍣≡1+@32dec¨)@('-'≠⊢)

在线尝试!

⎕CY'dfns'çŸ 的“DFN和”库(获取hexdec

(... )
 参数
 不同于
'-' 破折号
(... )@ 在由所述位置的所述子集其中,上述标准是真实的,适用:
dec¨ 每个十六进制字符转换为十进制数
 ... @32位置32(最后一位),适用于:
  1+ 增量
16(... )⍣≡ 反复应用左参数16,直到稳定:
  = 比较(给出掩模,其中十六进制数字是16)
  1⌽ 向左循环旋转一个步骤(这是进位位)
  |+ 到的是,分割时添加除法余数(由16,从而使所有的16到0) 转数字转换成长度为1的十六进制字符表示形式ϵ nlist(展平)
1hex


3

Java 11, 152 149 111 108 bytes

s->{var b=s.getLeastSignificantBits()+1;return new java.util.UUID(s.getMostSignificantBits()+(b==0?1:0),b);}

-38 bytes thank to @OlivierGrégoire.
-3 bytes thanks to @ASCII-only.

Try it online.

Explanation:

s->{         // Method with UUID as both parameter and return-type
  var b=s.getLeastSignificantBits()
             //  Get the 64 least significant bits of the input-UUID's 128 bits as long
        +1;  //  And increase it by 1
  return new java.util.UUID(
             //  Return a new UUID with:
    s.getMostSignificantBits()
             //   The 64 most significant bits of the input-UUID's 128 bits as long
    +(b==0?  //    And if the 64 least significant bits + 1 are exactly 0:
       1     //     Increase the 64 most significant bits by 1 as well
      :      //    Else:
       0,    //     Don't change the 64 most significant bits by adding 0
     b);}    //   And the 64 least significant bits + 1

Old 149 bytes answer:

s->{var t=new java.math.BigInteger(s.replace("-",""),16);return(t.add(t.ONE).toString(16)).replaceAll("(.{4})".repeat(5)+"(.*)","$1$2-$3-$4-$5-$6");}

Try it online.

Explanation:

s->{                              // Method with String as both parameter and return-type
  var t=new java.math.BigInteger( //  Create a BigInteger
         s.replace("-",""),       //  Of the input-string with all "-" removed
         16);                     //  Converted from Hexadecimal
  return(t.add(t.ONE)             //  Add 1
         .toString(16))           //  And convert it back to a Hexadecimal String
         .replaceAll("(.{4})".repeat(5)+"(.*)",
                                  //  And split the string into parts of sizes 4,4,4,4,4,rest
           "$1$2-$3-$4-$5-$6");}  //  And insert "-" after parts of size 8,4,4,4,
                                  //  and return it as result


@OlivierGrégoire Hadn't thought about using an actual UUID! Nice and shorter alternative. :D
Kevin Cruijssen


-1 more with var instead of long
ASCII-only



2

Python 2, 113 112 bytes

def f(s):a=hex(int(s.replace('-',''),16)+1+2**128);return'-'.join((a[3:11],a[11:15],a[15:19],a[19:23],a[23:-1]))

Try it online!

Without imports


2

Retina 0.8.2, 21 bytes

T`FfdlL`0dlL`.[-Ff]*$

Try it online! Link includes test cases. 9 becomes a. Explanation: The regex matches all trailing fs and -s plus one preceding character. The transliteration then cyclically increments those characters as if they were hex digits. Alternate approach, also 21 bytes:

T`L`l
T`fo`dl`.[-f]*$

Try it online! Link includes test cases. Works by lowercasing the input to simplify the transliteration. Would therefore be 15 bytes if it only had to support lowercase. Try it online! Link includes test cases.


2

MATLAB, 138 bytes

a=1;Z=a;for r=flip(split(input(''),'-'))'
q=r{:};z=dec2hex(hex2dec(q)+a,nnz(q));try
z+q;a=0;catch
z=~q+48;end
Z=[z 45 Z];end;disp(Z(1:36))

Fixed a bug in case a chunk is all zeros. Also golfed off a lot by abusing try/catch. Net result: 0 bytes saved.

An attempt to 'cheat' by using java.util.UUID failed because the long value returned from java.util.UUID.get[Most/Least]SignificantBits gets converted to a double which incurs a loss of precision. I invite you to take a look at this table and silently utter "...but why?"

Explanation

The hex2dec function spits out a double, so it cannot process the entire GUID at once to avoid exceeding flintmax. Instead, we have to process the GUID chunk by chunck, using split. The variable a checks if we need to carry a one, and cheatingly also is the initial increment we add. The condition for carrying over is whether the lengths of the original and incremented strings are no longer equal.

Original version was just under 160 bytes so I'd like to think this should not be easy to outgolf.



2

C# (Visual C# Interactive Compiler), 77 bytes

x=>{for(int i=35,c;(x[i]=(char)((c=x[i--])<48?c:c==57?65:c>69?48:c+1))<49;);}

Try it online!

-1 byte thanks to @ASCIIOnly!

Anonymous function that takes a char[] as input and outputs by modifying an argument.

Input is scanned from right to left and replaced using the following rules.

  • The - character is ignored and processing continues
  • The F character is converted to 0 and processing continues
  • The 9 character is converted to A and processing stops
  • The characters A-E and 0-8 are incremented by 1 and processing stops

2
==70 -> >69
ASCII-only

Excellent - Thanks :)
dana

2

Powershell, 101 bytes

for($p=1;$d=+"$args"[--$i]){$d+=$p*(1-@{45=1;57=-7;70=23;102=55}.$d)
$p*=$d-in45,48
$r=[char]$d+$r}$r

Try it online!

No external library or hex conversion. Any string length. Lower case and upper case are allowed. Input string matching to ^[f-]*$ is allowed too.

This script scans from the back of the string and inrement each char by the value from hashtable:

  • -: increment=1-1
  • 9: increment=1+7, result=A
  • F: increment=1-23, result=0
  • f: increment=1-55, result=0
  • increment=1 for other chars

Next, the script uses $p to determine whether to increment the current char.

Test script:

$f = {

for($p=1;$d=+"$args"[--$i]){$d+=$p*(1-@{45=1;57=-7;70=23;102=55}.$d)
$p*=$d-in45,48
$r=[char]$d+$r}$r

}

@(
    ,('f','0')
    ,('F','0')
    ,('0','1')
    ,('9','A')
    ,('A','B')
    ,('a','b')
    ,('0-f','1-0')
    ,('0-F','1-0')
    ,("7f128bd4-b0ba-4597-8f35-3a2f2756dfbb","7f128bd4-b0ba-4597-8f35-3a2f2756dfbc")
    ,("06b86883-f3e7-4f9d-87c5-a047e89a19f9","06b86883-f3e7-4f9d-87c5-a047e89a19fa")
    ,("89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2cf","89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2d0")
    ,("8e0f9835-4086-406b-b7a4-532da46963ff","8e0f9835-4086-406b-b7a4-532da4696400")
    ,("7f128bd4-b0ba-4597-ffff-ffffffffffff","7f128bd4-b0ba-4598-0000-000000000000")
    ,("89f25f2f-2f7b-4aa6-b9d7-46a98e3cb29f","89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2a0")
    ,("ffffffff-ffff-ffff-ffff-ffffffffffff","00000000-0000-0000-0000-000000000000")
) | % {
    $guid,$expected = $_
    $result = &$f $guid
    "$($result-eq$expected): $result"
}

Output:

True: 0
True: 0
True: 1
True: A
True: B
True: b
True: 1-0
True: 1-0
True: 7f128bd4-b0ba-4597-8f35-3a2f2756dfbc
True: 06b86883-f3e7-4f9d-87c5-a047e89a19fA
True: 89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2d0
True: 8e0f9835-4086-406b-b7a4-532da4696400
True: 7f128bd4-b0ba-4598-0000-000000000000
True: 89f25f2f-2f7b-4aa6-b9d7-46a98e3cb2A0
True: 00000000-0000-0000-0000-000000000000



1

PowerShell, 126 bytes

$a=("{0:X32}" -f (1+[Numerics.BigInteger]::Parse($args[0]-replace"-", 'AllowHexSpecifier')));5..2|%{$a=$a.Insert(4*$_,"-")};$a

Try it online!

Pretty trivial answer. Just thought I'd get the beloved PowerShell added to the list :)



0

Perl 5, 64 bytes

$c=reverse((1+hex s/-//gr)->as_hex);$c=~s/..$//;s/[^-]/chop$c/ge

The number of parentheses necessary here makes me sad, but -> binds very tightly, as ->as_hex is the quickest way I can find to get hexadecimal-formatted output.

Run with perl -Mbigint -p. Basically, it just converts the number to a bigint hexadecimal, adds one, and then subtitutes the digits of the result back into the original value, leaving dashes untouched.


0

Rust, 258 bytes

let x=|s:&str|s.chars().rev().scan(1,|a,c|{let d=c.to_digit(16).unwrap_or(99);match(d,*a){(15,1)=>{*a=1;Some(0)}(0..=14,1)=>{*a = 0;Some(d + 1)}_=> Some(d),}}).collect::<Vec<u32>>().iter().rev().for_each(|d| print!("{}", std::char::from_digit(*d, 16).unwrap_or('-')));

yes its long.. but technically its only one line with 1 expression? and no fancy libraries? and it will not crash on a fuzz input? ungolf:

let x=|s:&str|s.chars().rev().scan(1, |a, c| {
            let d = c.to_digit(16).unwrap_or(99);
            match (d, *a) {
                (15, 1) => {*a = 1;Some(0)}
                (0..=14, 1) => {*a = 0;Some(d + 1)}
                _ => Some(d),
            }
        }).collect::<Vec<u32>>().iter().rev()
        .for_each(|d| print!("{}", std::char::from_digit(*d, 16).unwrap_or('-')));

try it on rust playground



0

16/32/64-bit x86 assembly code, 28 bytes

bytes: 83C623FDAC3C2D74FB403C3A7502B0613C677502B03088460173E9C3

code:

     add esi, 35       ;point to end of string - 1
     std               ;go backwards
l1:  lodsb             ;fetch a character
     cmp al, '-'
     je  l1            ;skip '-'
     inc eax           ;otherwise increment
     cmp al, '9' + 1
     jne l2            ;branch if not out of numbers
     mov al, 'a'       ;otherwise switch '9'+1 to 'a'
l2:  cmp al, 'f' + 1   ;sets carry if less
     jne l3            ;branch if not out of letters
     mov al, '0'       ;otherwise switch 'f'+1 to '0'
                       ;and carry is clear
l3:  mov [esi + 1], al ;replace character
     jnb l1            ;and loop while carry is clear
     ret

Call with ESI pointing to GUID. Replace ESI with SI for 16-bit, or RSI for 64-bit (and +2 bytes).


0

C (clang), 62 bytes

g(char*i){for(i+=36;(*--i-45?*i+=*i-70?*i-57?1:8:-22:0)<49;);}

Try it online!


wait. does the lowercase/uppercase check not cost anything???
ASCII-only

i mean, it can handle both lowercase and uppercase at no cost to bytecount?!
ASCII-only

Ah ok.. ch-70%32 ? : to '0'... 64 and 96 are multiple of 32 so 70-6 and 102-6 %32 .
AZTECCO

1
you don't actually have to handle both, so 64
ASCII-only

0

Common Lisp, 166 bytes

(lambda(s &aux(r(format()"~32,'0x"(1+(parse-integer(remove #\- s):radix 16)))))(format()"~{~a~^-~}"(mapcar(lambda(x y)(subseq r x y))#1='(0 8 12 16 20 32)(cdr #1#))))

Try it online!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.