GUID(或G全球乌独特ID实体) 通常存储为 128 位值,并且通常显示为 32 个十六进制数字,各组之间用连字符分隔,例如:
21EC2020-3AEA-4069-A2DD-08002B30309D
在线资源可用于创建伪随机 GUID,但我想在编译时创建它们。理想情况下,我对一个\GUID
可以生成 GUID(我可以将其存储在宏中)的宏感兴趣,也许使用计算机时间来初始化伪随机数生成器。如果可能的话,也许一个可选参数可以允许提供一个应该返回固定 GUID 的种子。
我特别感兴趣的是版本 4 GUID其格式如下:
Hex digits Description
8 Data1
4 Data2
4 Data3
4 Initial two bytes from Data4
12 Remaining six bytes from Data4
“版本 4 GUID 只是使用伪随机数来填充除六位之外的所有位。它们4
在 4 位版本位置上有一个,并且的前两位data4
是1
和0
(因此的第一个十六进制数字data4
是8
、、9
或) A
,B
例如38A52BE4-9352-453E-AF97-5C3B448652F0
。更具体地说,data3
位模式将是 [...] 0100xxxxxxxxxxxx
[...]。”
答案1
random
由 D. Arsenau合作。
\documentclass{article}
\usepackage{xparse}
\input{random}
\ExplSyntaxOn
\cs_set_eq:NN \guid_set_random_number:Nnn \setrannum
\cs_set_eq:NN \g_guid_seed_int \randomi
\tl_new:N \l__guid_four_bytes_tl
\int_new:N \l__guid_random_int
\cs_new_protected:Nn \__guid_generate:n
{
\guid_set_random_number:Nnn \l__guid_random_int { 0 } { #1 }
\int_case:nn { #1 }
{
{ 4095 }{ \int_add:Nn \l__guid_random_int { "4000 } }
{ 16383 }{ \int_add:Nn \l__guid_random_int { 32768 } }
}
\tl_set:Nx \l__guid_four_bytes_tl { \int_to_Hex:n { \l__guid_random_int } }
\tl_set:Nx \l__guid_four_bytes_tl
{
\prg_replicate:nn { 4 - \tl_count:N \l__guid_four_bytes_tl } { 0 }
\tl_use:N \l__guid_four_bytes_tl
}
}
\NewDocumentCommand{\GUIDgenerate}{om}
{
\tl_new:c { g_guid_user_#2_tl }
\group_begin:
\IfValueT { #1 } { \int_gset:Nn \g_guid_seed_int { #1 } } % the seed
% Data 1 (eight bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 2 (four bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 3 (four bytes)
\__guid_generate:n { 4095 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 4a (three bytes)
\__guid_generate:n { 16383 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 4b (twelve bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\group_end:
}
\DeclareExpandableDocumentCommand{\GUID}{m}
{
\tl_use:c { g_guid_user_#1_tl }
}
\ExplSyntaxOff
\GUIDgenerate{fooA}
\GUIDgenerate{fooB}
\GUIDgenerate{fooC}
\begin{document}
\ttfamily
\GUID{fooA}\par
\GUID{fooB}\par
\GUID{fooC}\par
\GUIDgenerate[2]{fooD}
\GUIDgenerate[3]{fooE}
\GUIDgenerate[42]{fooF}
\GUID{fooD}\par
\GUID{fooE}\par
\GUID{fooF}\par
\end{document}
如果您多次编译它,您将看到前三个 GUID 会改变,而后三个 GUID 不会改变,因为它们是用固定种子定义的。
2024 年更新
现在所有引擎都有一个伪随机数生成器。
\documentclass{article}
\ExplSyntaxOn
% keep the initial (random) seed
\int_const:Nn \c_guid_seed_int { \sys_rand_seed: }
\tl_new:N \l__guid_four_bytes_tl
\int_new:N \l__guid_random_int
\cs_new_protected:Nn \__guid_generate:n
{
\int_set:Nn \l__guid_random_int { \int_rand:nn { 0 } { #1 } }
\int_case:nn { #1 }
{
{ 4095 }{ \int_add:Nn \l__guid_random_int { "4000 } }
{ 16383 }{ \int_add:Nn \l__guid_random_int { 32768 } }
}
\tl_set:Ne \l__guid_four_bytes_tl { \int_to_Hex:n { \l__guid_random_int } }
\tl_put_left:Ne \l__guid_four_bytes_tl
{
\prg_replicate:nn { 4 - \tl_count:N \l__guid_four_bytes_tl } { 0 }
}
}
\NewDocumentCommand{\GUIDgenerate}{om}
{
\tl_new:c { g_guid_user_#2_tl }
% if with optional argument, use it a seed
\IfValueT { #1 } { \sys_gset_rand_seed:n { #1 } } % the seed
% Data 1 (eight bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:ce { g_guid_user_#2_tl } { - }
% Data 2 (four bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:ce { g_guid_user_#2_tl } { - }
% Data 3 (four bytes)
\__guid_generate:n { 4095 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:ce { g_guid_user_#2_tl } { - }
% Data 4a (three bytes)
\__guid_generate:n { 16383 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:ce { g_guid_user_#2_tl } { - }
% Data 4b (twelve bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:ce { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% reset the seed to the initial (random) value
\IfValueT { #1 } { \sys_gset_rand_seed:n { \c_guid_seed_int } } % the seed
}
\NewExpandableDocumentCommand{\GUID}{m}
{
\tl_use:c { g_guid_user_#1_tl }
}
\ExplSyntaxOff
\GUIDgenerate{fooA}
\GUIDgenerate{fooB}
\GUIDgenerate{fooC}
\begin{document}
\ttfamily
\GUID{fooA}\par
\GUID{fooB}\par
\GUID{fooC}\par
\GUIDgenerate[2]{fooD}
\GUIDgenerate[3]{fooE}
\GUIDgenerate[42]{fooF}
\GUID{fooD}\par
\GUID{fooE}\par
\GUID{fooF}\par
\end{document}
答案2
使用 PDFTeX 和包中的随机生成器xintbinhex
进行十六进制转换。
编辑:我添加了一个更简单的无包装版本。仍在使用pdfuniformdeviate
。
我忘了说大写字母是用 catcode 字母生成的,它们是常见的字母。(如果有关系的话……)
编辑2:不知为何我误读了说明,结果只产生了 28 位十六进制数字。已修复。
\documentclass{article}
\usepackage{xintbinhex}
\makeatletter
% there is a complication as we need to avoid stripping leading zeros...
% ... but I am going to use \xintDecToHex which does that.
%
\begingroup\catcode0 12
\gdef\@expand@and@gob {\expandafter\@gobble\romannumeral`^^@}
\endgroup
% this one is expandable
\newcommand*{\GUID}
{%
% Data 1: (8 Hex Digits)
% \pdfuniformdeviate will refuse higher than "7FFFFFFF
% \xintDecToHex wants tokens, hence \the\numexpr needed.
% generate five hex digits and gobble the first one (which will be 1)
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}%
% again for a total of 8 Hex Digits
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}-%
% Data 2: (4 Hex Digits)
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}-%
% Data 3: (4 Hex Digits, the first one a 4)
\xintDecToHex {\the\numexpr "4000+\pdfuniformdeviate "FFF\relax}-%
% Data 4: (16=4+12 Hex Digits, the first one 8, 9 , A or B)
\xintDecToHex {\the\numexpr "8000+\pdfuniformdeviate "4000\relax}-%
\@expand@and@gob
\xintDecToHex {\the\numexpr "1000000+\pdfuniformdeviate "FFFFFF\relax}%
\@expand@and@gob
\xintDecToHex {\the\numexpr "1000000+\pdfuniformdeviate "FFFFFF\relax}%
}
% side-note: if we used directly \pdfuniformdeviate in \xintDecToHex,
% we could do it this way
% \xintDecToHex {\pdfuniformdeviate "FFFF }% <-- space needed
% we don't do this above as \xintDecToHex trims leading zeros
\makeatother
% this one is not expandable
\newcommand*{\GUIDnx}[1][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \GUID\endgroup}
% this one is not expandable
\newcommand*{\GUIDset}[2][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \xdef#2{\GUID}\endgroup}
\begin{document}
\ttfamily
\GUID\par
\GUID\par
\GUID\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\GUIDnx [0]\par
\GUIDset[0]\foo
\meaning\foo<-- end of macro\par
\GUIDnx [123456789]\par
\GUIDset[123456789]\foo
\meaning\foo<-- end of macro\par
\end{document}
%No package version
\documentclass{article}
\def\GUIDonedigit {\ifcase\pdfuniformdeviate 16\space\space
0\or 1\or 2\or 3%
\or 4\or 5\or 6\or 7%
\or 8\or 9\or A\or B%
\or C\or D\or E\else F\fi}
\def\GUIDonespecialdigit {\ifcase\pdfuniformdeviate 4\space\space
8\or 9\or A\else B\fi }
% this one is expandable
\newcommand*{\GUID}
{%
% Data 1: (8 Hex Digits)
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 2: (4 Hex Digits)
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 3 : (4 Hex Digits, the first one a 4)
4\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 4: (16=4+12 Hex Digits, the first one 8, 9 , A or B)
\GUIDonespecialdigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
}
\makeatother
% this one is not expandable
\newcommand*{\GUIDnx}[1][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \GUID\endgroup}
% this one is not expandable
\newcommand*{\GUIDset}[2][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \xdef#2{\GUID}\endgroup}
\begin{document}
\ttfamily
\GUID\par
\GUID\par
\GUID\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\GUIDnx [0]\par
\GUIDset[0]\foo
\meaning\foo<-- end of macro\par
\GUIDnx [123456789]\par
\GUIDset[123456789]\foo
\meaning\foo<-- end of macro\par
\end{document}
答案3
答案4
还有更多!这个使用了 md5sum,所以从技术上讲,它在 uuid v4 意义上并不是“随机的”,但没有人会注意到。
此代码提供了一个特定的 documentID,除非您执行某些操作来更改它,否则该 documentID 不会改变。instanceID 基于时间,因此始终会改变。
警告:如果您不愿意根据需要更改 documentID 生成器中的“某些内容”,那么请不要使用它。
\documentclass{article}
% My own usage already has these packages loaded for other reasons,
% so I use them here. No doubt the code can be re-written so that
% the mdfivesum and string manipulations can be done directly.
\RequirePackage{pdftexcmds}
\RequirePackage{xstring}
% Farther down, \tempstr will be defined. It begins as an mdfivesum with
% uppercase hex, no hyphens, and no particular hexcodes in two locations
% where uuid version 4 expects particular hexcodes. This tweak puts the
% letters in lowercase (via egreg code elsewhere), then adds the hyphens and
% specific hexcodes:
\def\tweaktempstr{
\lowercase\expandafter{% Per 'egreg' tex.stackexchange.com q.351065.
\expandafter\def\expandafter\tempstr\expandafter{\tempstr}%
}
\StrLeft{\tempstr}{8}[\tempn]
\StrRight{\tempstr}{24}[\tempd]
\edef\tempstr{\tempn-\tempd}
\StrLeft{\tempstr}{13}[\tempn]
\StrRight{\tempstr}{19}[\tempd] % Omit character, becomes 4.
\edef\tempstr{\tempn-4\tempd}
\StrLeft{\tempstr}{18}[\tempn]
\StrRight{\tempstr}{15}[\tempd] % Omit character, becomes 8.
\edef\tempstr{\tempn-8\tempd}
\StrLeft{\tempstr}{23}[\tempn]
\StrRight{\tempstr}{12}[\tempd]
\edef\tempstr{\tempn-\tempd}
}
% Change "somthing" so that documents with same jobname are distinguished:
\makeatletter
\edef\tempstr{\pdf@mdfivesum{\jobname something}}
\tweaktempstr
\edef\documentID{uuid:\tempstr}
\ifdefined\pdffeedback % lualatex
\edef\tempstr{\pdf@mdfivesum{\pdffeedback creationdate}}
\else % pdflatex
\edef\tempstr{\pdf@mdfivesum{\pdfcreationdate}}
\fi
\tweaktempstr
\edef\instanceID{uuid:\tempstr}
\makeatother
%%
\begin{document}
\ttfamily % Only so that strings are easier to see:
documentID = \documentID\par
instanceID = \instanceID\par
\end{document}