Pulling numbers out of a file name
A long time ago, I wanted to solve the problem where I would typeset my homework assignments, but copy the file "hw1.tex" to make "hw2.tex", and forget to change the title "Homework 1" at the top of the page.
So I wrote a function (that now I no longer 100% remember how it works, or maybe I got parts of it from a place like StackExchange and never did fully understand it):
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
Now I can make Homework hwnum
the title. When I compile "hw1.tex", the title is "Homework 1", and when I compile "hw2.tex", the title becomes "Homework 2".
Minimal working example (assuming you first save it as a file with the appropriate name):
documentclass{article}
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
begin{document}
This is Homework hwnum.
end{document}
These days, I have to typeset more than just homework assignments, so I'd like to have this be a bit more flexible.
- It would be nicer, though not very important, if I didn't have to change the prefix "hw" in the command if I want to deal with filenames such as "day1.tex", "day2.tex", and so on.
- What I really want is to extract more than one number: for example, "Lecture 7" and "Chapter 3" (or whatever) from a filename such as "ch3lec7.tex".
- Putting these together, an ideal function would just be able to locate all the numbers in the filename, no matter which non-numbers they're separated by. The same function could pull out 3 and 7 whether it's given the filename "ch3lec7.tex" or "week3day7.tex".
What is a way for me to do at least #2, but also if possible #1 or #3?
strings jobname
add a comment |
A long time ago, I wanted to solve the problem where I would typeset my homework assignments, but copy the file "hw1.tex" to make "hw2.tex", and forget to change the title "Homework 1" at the top of the page.
So I wrote a function (that now I no longer 100% remember how it works, or maybe I got parts of it from a place like StackExchange and never did fully understand it):
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
Now I can make Homework hwnum
the title. When I compile "hw1.tex", the title is "Homework 1", and when I compile "hw2.tex", the title becomes "Homework 2".
Minimal working example (assuming you first save it as a file with the appropriate name):
documentclass{article}
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
begin{document}
This is Homework hwnum.
end{document}
These days, I have to typeset more than just homework assignments, so I'd like to have this be a bit more flexible.
- It would be nicer, though not very important, if I didn't have to change the prefix "hw" in the command if I want to deal with filenames such as "day1.tex", "day2.tex", and so on.
- What I really want is to extract more than one number: for example, "Lecture 7" and "Chapter 3" (or whatever) from a filename such as "ch3lec7.tex".
- Putting these together, an ideal function would just be able to locate all the numbers in the filename, no matter which non-numbers they're separated by. The same function could pull out 3 and 7 whether it's given the filename "ch3lec7.tex" or "week3day7.tex".
What is a way for me to do at least #2, but also if possible #1 or #3?
strings jobname
add a comment |
A long time ago, I wanted to solve the problem where I would typeset my homework assignments, but copy the file "hw1.tex" to make "hw2.tex", and forget to change the title "Homework 1" at the top of the page.
So I wrote a function (that now I no longer 100% remember how it works, or maybe I got parts of it from a place like StackExchange and never did fully understand it):
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
Now I can make Homework hwnum
the title. When I compile "hw1.tex", the title is "Homework 1", and when I compile "hw2.tex", the title becomes "Homework 2".
Minimal working example (assuming you first save it as a file with the appropriate name):
documentclass{article}
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
begin{document}
This is Homework hwnum.
end{document}
These days, I have to typeset more than just homework assignments, so I'd like to have this be a bit more flexible.
- It would be nicer, though not very important, if I didn't have to change the prefix "hw" in the command if I want to deal with filenames such as "day1.tex", "day2.tex", and so on.
- What I really want is to extract more than one number: for example, "Lecture 7" and "Chapter 3" (or whatever) from a filename such as "ch3lec7.tex".
- Putting these together, an ideal function would just be able to locate all the numbers in the filename, no matter which non-numbers they're separated by. The same function could pull out 3 and 7 whether it's given the filename "ch3lec7.tex" or "week3day7.tex".
What is a way for me to do at least #2, but also if possible #1 or #3?
strings jobname
A long time ago, I wanted to solve the problem where I would typeset my homework assignments, but copy the file "hw1.tex" to make "hw2.tex", and forget to change the title "Homework 1" at the top of the page.
So I wrote a function (that now I no longer 100% remember how it works, or maybe I got parts of it from a place like StackExchange and never did fully understand it):
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
Now I can make Homework hwnum
the title. When I compile "hw1.tex", the title is "Homework 1", and when I compile "hw2.tex", the title becomes "Homework 2".
Minimal working example (assuming you first save it as a file with the appropriate name):
documentclass{article}
usepackage{substr}
newcommand{hwnum}{BehindSubString{hw}{scantokensexpandafter{jobnamenoexpand}}}
begin{document}
This is Homework hwnum.
end{document}
These days, I have to typeset more than just homework assignments, so I'd like to have this be a bit more flexible.
- It would be nicer, though not very important, if I didn't have to change the prefix "hw" in the command if I want to deal with filenames such as "day1.tex", "day2.tex", and so on.
- What I really want is to extract more than one number: for example, "Lecture 7" and "Chapter 3" (or whatever) from a filename such as "ch3lec7.tex".
- Putting these together, an ideal function would just be able to locate all the numbers in the filename, no matter which non-numbers they're separated by. The same function could pull out 3 and 7 whether it's given the filename "ch3lec7.tex" or "week3day7.tex".
What is a way for me to do at least #2, but also if possible #1 or #3?
strings jobname
strings jobname
edited Dec 10 at 0:05
asked Dec 9 at 21:06
Misha Lavrov
1336
1336
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
The code below uses regular expressions from LaTeX3 to extract all of the numbers in the filename and then makes them available as misha{1}
, misha{2}
, .... There is no error checking so, for example, if you have misha{100}
in your document then this command will fail silently, doing nothing.
If you save the code below as the file ch3lec7.tex
then run it you will get the output:
Here is the code:
documentclass{article}
usepackage{expl3}
ExplSyntaxOn
cs_generate_variant:Nn regex_extract_all:nnN {nVN}
seq_new:N l_misha_seq
regex_extract_all:nVN {d+} c_sys_jobname_str l_misha_seq
newcommandmisha[1]{seq_item:Nn l_misha_seq {#1}}
ExplSyntaxOff
begin{document}
Chapter misha{1}, lecture misha{2}.
end{document}
The work is all done by the command regex_extract_all:nVN
, which puts all of the numbers in jobname
into an internal LaTeX3 sequence. (As egreg pointed out, LaTeX3 stores the filename in the string constant c_sys_jobname_str
.) The command misha{k}
prints the k
th element of this sequence.
When I try to compile this, I get the errorControl sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am usingTeX Live 2018
.
– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
add a comment |
Off the cuff I can offer four macros:
UD@ExtractDigitSequences{<arbitrary token sequence>}
This macro extracts all catcode-12-digit-sequences from<arbitrary token sequence>
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
This macro extracts the K-th catcode-12-digit-sequence from<arbitrary token sequence>
.
UD@ExtractDigitSequencesFromJobname
This macro extracts all catcode-12-digit-sequences from the expansion ofjobname
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequenceFromJobname{<number K>}
This macro extracts the K-th catcode-12-digit-sequence from the expansion ofjobname
.
Basically UD@ExtractDigitSequences{<arbitrary token sequence>}
and UD@ExtractDigitSequencesFromJobname
are wrappers for a recursive loop formed by the macro UD@extractDigitSequencesLoop
.
The gist of that recursive loop is:
UD@extractDigitSequencesLoop
processes three arguments:
The first argument denotes the (remaining) <arbitrary token-sequence>
.
The second argument denotes the collection of brace-nested digit-sequences collected so far.
The third argument denotes the collection of digits collected so far for the current digit-sequence.
First the loop checks whether the (remaining) <arbitrary token-sequence>
is empty.
If so, you are done and the second argument will be delivered and in case the third argument is not empty, it will also be delivered, nested in braces.
If not so, the loop will look at the first token of the (remaining) <arbitrary token-sequence>
, hereby taking into account braces and spaces as cases that need special treatment.
In case of the first token of the (remaining) <arbitrary token-sequence>
being both a non-brace-token and a non-digit-token, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
In case of the first token of the (remaining) <arbitrary token-sequence>
being an opening-brace, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2. In this case we also need to attach to argument 2 the result of applying the entire routine on the brace-nested first component/on the leading undelimited argument of the (remaining) <arbitrary token-sequence>
before calling the loop again with that undelimited argument removed from the (remaining) <arbitrary token-sequence>
.
(This case is important only as long as it is about extracting from arbitrary token sequences: The expansion of the jobname-primitive in any case does contain neither curly opening braces of category-code 1 nor closing braces of category code 2 but a collection of explicit character tokens that may contain explicit character tokens of category code 12(other but not with character code 32(space) and explicit character tokens of category code 10(space) and character code 32(space)=explicit non-funny space-tokens.)
In case of the first token of the (remaining) <arbitrary token-sequence>
being a digit-token, it needs to be attached to the current digit sequence in argument 3 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
Of course you also need routines for checking
- whether an argument is empty.
- whether an argument's first token is an opening brace.
- whether an argument's first token is a space token.
- whether an argument's first token is a digit-token.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
and UD@ExtractKthDigitSequenceFromJobname{<number K>}
"feed" the result of carrying out that loop to another macro which is called UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
which in turn is a routine for delivering the K-th undelimited argument from a list of undelimited/brace-nested arguments.
Everything is implemented so that it will also work in expansion-contexts like csname..endcsname
.
Neither do you need extensions like eTeX or LuaTeX, nor do you need any additional LaTeX2e packages like expl3 or substr or the like.
documentclass{article}
makeatletter
%%=============================================================================
%% Paraphernalia
%%.............................................................................
newcommandUD@firstoftwo[2]{#1}%
newcommandUD@secondoftwo[2]{#2}%
newcommandUD@Exchange[2]{#2#1}%
newcommandUD@gobblespace{}UD@firstoftwo{defUD@gobblespace}{} {}%
%%=============================================================================
%% Check whether argument is empty:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Crank out the cases of string "hitting"
%% - an opening-brace -> argument is not empty
%% - a non-brace-token -> argument is not empty
%% - a closing-brace -> argument is empty
%%.............................................................................
newcommandUD@CheckWhetherNull[1]{%
romannumeral0expandafterUD@secondoftwostring{expandafter
UD@secondoftwoexpandafter{expandafter{string#1}expandafter
UD@secondoftwostring}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@secondoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% Basically this is a variation of UD@CheckWhetherNull where non-emptiness
%% is ensured so that you need to only crank out the cases of string "hitting"
%% an opening-brace or a non-brace-token.
%%.............................................................................
newcommandUD@CheckWhetherBrace[1]{%
romannumeral0expandafterUD@secondoftwoexpandafter{expandafter{%
string#1.}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@firstoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
}%
%%=============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherLeadingSpace[1]{%
romannumeral0UD@CheckWhetherNull{#1}%
{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
{expandafterUD@secondoftwostring{UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
newcommandUD@CheckWhetherLeadingSpaceB{}%
longdefUD@CheckWhetherLeadingSpaceB#1 {%
expandafterUD@CheckWhetherNullexpandafter{UD@secondoftwo#1{}}%
{UD@Exchange{UD@firstoftwo}}{UD@Exchange{UD@secondoftwo}}%
{UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
expandafterexpandafterexpandafter}expandafterexpandafter
expandafter}expandafterUD@secondoftwoexpandafter{string}%
}%
%%=============================================================================
%% Check whether argument does not contain "!" (unless nested in braces):
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does not contain !>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does contain !>
%%.............................................................................
newcommandUD@GobbleToExclam{}%
longdefUD@GobbleToExclam#1!{}%
newcommandUD@CheckWhetherNoExclam[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@GobbleToExclam#1!}%
}%
%%=============================================================================
%% Check whether argument is a single explicit character-token of
%% category code 12 (other) that denotes digit:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherdigit{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is a single catcode-12-digit>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is not a single catcode-12-digit>
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherdigitfork{}%
longdefUD@CheckWhetherdigitfork#1!0!1!2!3!4!5!6!7!8!9!#2#3!!!!{#2}%
newcommandUD@CheckWhetherdigit[1]{%
romannumeral0%
UD@CheckWhetherNoExclam{#1}{%
UD@CheckWhetherdigitfork
!#1!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!#1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!#1!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!#1!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!#1!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!#1!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!#1!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!#1!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!#1!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!#1!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@secondoftwo}%
!!!!%
}{UD@firstoftwoexpandafter{} UD@secondoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% UD@ExtractKthArg{0}{ABCDE} yields: <nothing>
%%
%% UD@ExtractKthArg{3}{ABCDE} yields: C
%%
%% UD@ExtractKthArg{3}{AB{CD}E} yields: CD
%%
%% UD@ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%%
%% UD@ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthArg[1]{%
romannumeral0%
% #1: <integer number K>
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
newcommandUD@ExtractKthArgCheck[2]{%
UD@CheckWhetherNull{#1}{ }{%
expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}{#2}%
}%
}%
newcommandUD@ExtractKthArgLoop[2]{%
UD@CheckWhetherNull{#2}{ }{%
UD@CheckWhetherNull{#1}{%
UD@ExtractFirstArgLoop{#2UD@SelDOm}%
}{%
expandafterUD@Exchangeexpandafter{expandafter{UD@firstoftwo{}#2}}%
{expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}}%
}%
}%
}%
newcommandUD@RemoveTillUD@SelDOm{}%
longdefUD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
newcommandUD@ExtractFirstArgLoop[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@firstoftwo{}#1}%
{UD@firstoftwo{expandafter}{} UD@secondoftwo{}#1}%
{expandafterUD@ExtractFirstArgLoopexpandafter{UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% UD@ExtractDigitSequences{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from <token sequence>, nests each such sequence
%% into curly braces:
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66}
%% yields: {00}{78}{66}%
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66Baz543BaT954}
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequences[1]{%
romannumeral0UD@extractDigitSequencesLoop{#1}{}{}%
}%
newcommandUD@extractDigitSequencesLoop[3]{%
UD@CheckWhetherNull{#1}{%
UD@CheckWhetherNull{#3}{ #2}{ #2{#3}}%
}{%
UD@CheckWhetherBrace{#1}{%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoop
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}%
}{}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}{%
UD@CheckWhetherLeadingSpace{#1}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@gobblespace#1}}%
{}%
}{%
expandafterUD@CheckWhetherdigit
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{%
expandafterexpandafterexpandafterUD@Exchange
expandafterexpandafterexpandafter{%
expandafterexpandafterexpandafter{%
expandafterUD@Exchange
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{#3}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}{#2}}%
}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}%
}%
}%
}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequence{<integer K>}{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from <token sequence> if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequence[2]{%
romannumeral0%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0UD@extractDigitSequencesLoop{#2}{}{}}%
}{%
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
}%
%%=============================================================================
%% UD@ExtractDigitSequencesFromJobname
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from the top-level-expansion of the control-word-
%% token jobname, nests each such sequence into curly braces:
%%
%% E.g., if jobname = 00foo78Bar66, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}%
%%
%% E.g., if jobname = 00foo78Bar66Baz543BaT954, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequencesFromJobname{%
romannumeral0%
expandafterUD@extractDigitSequencesLoopexpandafter{jobname}{}{}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequenceFromJobname{<integer K>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from the top-level-expansion of the
%% control-word-token jobname if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequenceFromJobname[1]{%
romannumeral
expandafterUD@Exchangeexpandafter{expandafter{jobname}}%
{expandafterUD@secondoftwoUD@ExtractKthDigitSequence{#1}}%
}%
makeatother
begin{document}
makeatletter
defbraceshowloop#1{%
ifxrelax#1expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
{}{{#1}braceshowloop}%
}%
noindent
verb|UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}relax\
verb|UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}\
verb|jobname|:
jobname\
verb|UD@ExtractDigitSequencesFromJobname|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequencesFromJobnamerelax\
verb|UD@ExtractKthDigitSequenceFromJobname{-1}|:
UD@ExtractKthDigitSequenceFromJobname{-1}\
verb|UD@ExtractKthDigitSequenceFromJobname{0}|:
UD@ExtractKthDigitSequenceFromJobname{0}\
verb|UD@ExtractKthDigitSequenceFromJobname{1}|:
UD@ExtractKthDigitSequenceFromJobname{1}\
verb|UD@ExtractKthDigitSequenceFromJobname{2}|:
UD@ExtractKthDigitSequenceFromJobname{2}\
verb|UD@ExtractKthDigitSequenceFromJobname{3}|:
UD@ExtractKthDigitSequenceFromJobname{3}\
verb|UD@ExtractKthDigitSequenceFromJobname{4}|:
UD@ExtractKthDigitSequenceFromJobname{4}\
verb|UD@ExtractKthDigitSequenceFromJobname{5}|:
UD@ExtractKthDigitSequenceFromJobname{5}\
verb|UD@ExtractKthDigitSequenceFromJobname{6}|:
UD@ExtractKthDigitSequenceFromJobname{6}\
end{document}
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f464025%2fpulling-numbers-out-of-a-file-name%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
The code below uses regular expressions from LaTeX3 to extract all of the numbers in the filename and then makes them available as misha{1}
, misha{2}
, .... There is no error checking so, for example, if you have misha{100}
in your document then this command will fail silently, doing nothing.
If you save the code below as the file ch3lec7.tex
then run it you will get the output:
Here is the code:
documentclass{article}
usepackage{expl3}
ExplSyntaxOn
cs_generate_variant:Nn regex_extract_all:nnN {nVN}
seq_new:N l_misha_seq
regex_extract_all:nVN {d+} c_sys_jobname_str l_misha_seq
newcommandmisha[1]{seq_item:Nn l_misha_seq {#1}}
ExplSyntaxOff
begin{document}
Chapter misha{1}, lecture misha{2}.
end{document}
The work is all done by the command regex_extract_all:nVN
, which puts all of the numbers in jobname
into an internal LaTeX3 sequence. (As egreg pointed out, LaTeX3 stores the filename in the string constant c_sys_jobname_str
.) The command misha{k}
prints the k
th element of this sequence.
When I try to compile this, I get the errorControl sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am usingTeX Live 2018
.
– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
add a comment |
The code below uses regular expressions from LaTeX3 to extract all of the numbers in the filename and then makes them available as misha{1}
, misha{2}
, .... There is no error checking so, for example, if you have misha{100}
in your document then this command will fail silently, doing nothing.
If you save the code below as the file ch3lec7.tex
then run it you will get the output:
Here is the code:
documentclass{article}
usepackage{expl3}
ExplSyntaxOn
cs_generate_variant:Nn regex_extract_all:nnN {nVN}
seq_new:N l_misha_seq
regex_extract_all:nVN {d+} c_sys_jobname_str l_misha_seq
newcommandmisha[1]{seq_item:Nn l_misha_seq {#1}}
ExplSyntaxOff
begin{document}
Chapter misha{1}, lecture misha{2}.
end{document}
The work is all done by the command regex_extract_all:nVN
, which puts all of the numbers in jobname
into an internal LaTeX3 sequence. (As egreg pointed out, LaTeX3 stores the filename in the string constant c_sys_jobname_str
.) The command misha{k}
prints the k
th element of this sequence.
When I try to compile this, I get the errorControl sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am usingTeX Live 2018
.
– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
add a comment |
The code below uses regular expressions from LaTeX3 to extract all of the numbers in the filename and then makes them available as misha{1}
, misha{2}
, .... There is no error checking so, for example, if you have misha{100}
in your document then this command will fail silently, doing nothing.
If you save the code below as the file ch3lec7.tex
then run it you will get the output:
Here is the code:
documentclass{article}
usepackage{expl3}
ExplSyntaxOn
cs_generate_variant:Nn regex_extract_all:nnN {nVN}
seq_new:N l_misha_seq
regex_extract_all:nVN {d+} c_sys_jobname_str l_misha_seq
newcommandmisha[1]{seq_item:Nn l_misha_seq {#1}}
ExplSyntaxOff
begin{document}
Chapter misha{1}, lecture misha{2}.
end{document}
The work is all done by the command regex_extract_all:nVN
, which puts all of the numbers in jobname
into an internal LaTeX3 sequence. (As egreg pointed out, LaTeX3 stores the filename in the string constant c_sys_jobname_str
.) The command misha{k}
prints the k
th element of this sequence.
The code below uses regular expressions from LaTeX3 to extract all of the numbers in the filename and then makes them available as misha{1}
, misha{2}
, .... There is no error checking so, for example, if you have misha{100}
in your document then this command will fail silently, doing nothing.
If you save the code below as the file ch3lec7.tex
then run it you will get the output:
Here is the code:
documentclass{article}
usepackage{expl3}
ExplSyntaxOn
cs_generate_variant:Nn regex_extract_all:nnN {nVN}
seq_new:N l_misha_seq
regex_extract_all:nVN {d+} c_sys_jobname_str l_misha_seq
newcommandmisha[1]{seq_item:Nn l_misha_seq {#1}}
ExplSyntaxOff
begin{document}
Chapter misha{1}, lecture misha{2}.
end{document}
The work is all done by the command regex_extract_all:nVN
, which puts all of the numbers in jobname
into an internal LaTeX3 sequence. (As egreg pointed out, LaTeX3 stores the filename in the string constant c_sys_jobname_str
.) The command misha{k}
prints the k
th element of this sequence.
edited Dec 10 at 1:17
answered Dec 10 at 0:03
Andrew
30.4k34380
30.4k34380
When I try to compile this, I get the errorControl sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am usingTeX Live 2018
.
– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
add a comment |
When I try to compile this, I get the errorControl sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am usingTeX Live 2018
.
– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
When I try to compile this, I get the error
Control sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
When I try to compile this, I get the error
Control sequence regex_extract_all:nnN undefined.
– Misha Lavrov
Dec 10 at 3:37
2
2
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am using
TeX Live 2018
.– Andrew
Dec 10 at 4:01
@MishaLavrov I suspect that you need to update your latex distribution. I think that the regular expression machinery was added to LaTeX in mid 2017. The code above compiles fine for me. I am using
TeX Live 2018
.– Andrew
Dec 10 at 4:01
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
Updating my LaTeX distribution was something of an adventure, but I got there in the end and now everything works. Thanks for the quick answer that does everything I hoped for!
– Misha Lavrov
Dec 10 at 5:33
add a comment |
Off the cuff I can offer four macros:
UD@ExtractDigitSequences{<arbitrary token sequence>}
This macro extracts all catcode-12-digit-sequences from<arbitrary token sequence>
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
This macro extracts the K-th catcode-12-digit-sequence from<arbitrary token sequence>
.
UD@ExtractDigitSequencesFromJobname
This macro extracts all catcode-12-digit-sequences from the expansion ofjobname
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequenceFromJobname{<number K>}
This macro extracts the K-th catcode-12-digit-sequence from the expansion ofjobname
.
Basically UD@ExtractDigitSequences{<arbitrary token sequence>}
and UD@ExtractDigitSequencesFromJobname
are wrappers for a recursive loop formed by the macro UD@extractDigitSequencesLoop
.
The gist of that recursive loop is:
UD@extractDigitSequencesLoop
processes three arguments:
The first argument denotes the (remaining) <arbitrary token-sequence>
.
The second argument denotes the collection of brace-nested digit-sequences collected so far.
The third argument denotes the collection of digits collected so far for the current digit-sequence.
First the loop checks whether the (remaining) <arbitrary token-sequence>
is empty.
If so, you are done and the second argument will be delivered and in case the third argument is not empty, it will also be delivered, nested in braces.
If not so, the loop will look at the first token of the (remaining) <arbitrary token-sequence>
, hereby taking into account braces and spaces as cases that need special treatment.
In case of the first token of the (remaining) <arbitrary token-sequence>
being both a non-brace-token and a non-digit-token, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
In case of the first token of the (remaining) <arbitrary token-sequence>
being an opening-brace, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2. In this case we also need to attach to argument 2 the result of applying the entire routine on the brace-nested first component/on the leading undelimited argument of the (remaining) <arbitrary token-sequence>
before calling the loop again with that undelimited argument removed from the (remaining) <arbitrary token-sequence>
.
(This case is important only as long as it is about extracting from arbitrary token sequences: The expansion of the jobname-primitive in any case does contain neither curly opening braces of category-code 1 nor closing braces of category code 2 but a collection of explicit character tokens that may contain explicit character tokens of category code 12(other but not with character code 32(space) and explicit character tokens of category code 10(space) and character code 32(space)=explicit non-funny space-tokens.)
In case of the first token of the (remaining) <arbitrary token-sequence>
being a digit-token, it needs to be attached to the current digit sequence in argument 3 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
Of course you also need routines for checking
- whether an argument is empty.
- whether an argument's first token is an opening brace.
- whether an argument's first token is a space token.
- whether an argument's first token is a digit-token.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
and UD@ExtractKthDigitSequenceFromJobname{<number K>}
"feed" the result of carrying out that loop to another macro which is called UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
which in turn is a routine for delivering the K-th undelimited argument from a list of undelimited/brace-nested arguments.
Everything is implemented so that it will also work in expansion-contexts like csname..endcsname
.
Neither do you need extensions like eTeX or LuaTeX, nor do you need any additional LaTeX2e packages like expl3 or substr or the like.
documentclass{article}
makeatletter
%%=============================================================================
%% Paraphernalia
%%.............................................................................
newcommandUD@firstoftwo[2]{#1}%
newcommandUD@secondoftwo[2]{#2}%
newcommandUD@Exchange[2]{#2#1}%
newcommandUD@gobblespace{}UD@firstoftwo{defUD@gobblespace}{} {}%
%%=============================================================================
%% Check whether argument is empty:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Crank out the cases of string "hitting"
%% - an opening-brace -> argument is not empty
%% - a non-brace-token -> argument is not empty
%% - a closing-brace -> argument is empty
%%.............................................................................
newcommandUD@CheckWhetherNull[1]{%
romannumeral0expandafterUD@secondoftwostring{expandafter
UD@secondoftwoexpandafter{expandafter{string#1}expandafter
UD@secondoftwostring}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@secondoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% Basically this is a variation of UD@CheckWhetherNull where non-emptiness
%% is ensured so that you need to only crank out the cases of string "hitting"
%% an opening-brace or a non-brace-token.
%%.............................................................................
newcommandUD@CheckWhetherBrace[1]{%
romannumeral0expandafterUD@secondoftwoexpandafter{expandafter{%
string#1.}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@firstoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
}%
%%=============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherLeadingSpace[1]{%
romannumeral0UD@CheckWhetherNull{#1}%
{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
{expandafterUD@secondoftwostring{UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
newcommandUD@CheckWhetherLeadingSpaceB{}%
longdefUD@CheckWhetherLeadingSpaceB#1 {%
expandafterUD@CheckWhetherNullexpandafter{UD@secondoftwo#1{}}%
{UD@Exchange{UD@firstoftwo}}{UD@Exchange{UD@secondoftwo}}%
{UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
expandafterexpandafterexpandafter}expandafterexpandafter
expandafter}expandafterUD@secondoftwoexpandafter{string}%
}%
%%=============================================================================
%% Check whether argument does not contain "!" (unless nested in braces):
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does not contain !>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does contain !>
%%.............................................................................
newcommandUD@GobbleToExclam{}%
longdefUD@GobbleToExclam#1!{}%
newcommandUD@CheckWhetherNoExclam[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@GobbleToExclam#1!}%
}%
%%=============================================================================
%% Check whether argument is a single explicit character-token of
%% category code 12 (other) that denotes digit:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherdigit{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is a single catcode-12-digit>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is not a single catcode-12-digit>
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherdigitfork{}%
longdefUD@CheckWhetherdigitfork#1!0!1!2!3!4!5!6!7!8!9!#2#3!!!!{#2}%
newcommandUD@CheckWhetherdigit[1]{%
romannumeral0%
UD@CheckWhetherNoExclam{#1}{%
UD@CheckWhetherdigitfork
!#1!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!#1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!#1!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!#1!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!#1!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!#1!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!#1!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!#1!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!#1!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!#1!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@secondoftwo}%
!!!!%
}{UD@firstoftwoexpandafter{} UD@secondoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% UD@ExtractKthArg{0}{ABCDE} yields: <nothing>
%%
%% UD@ExtractKthArg{3}{ABCDE} yields: C
%%
%% UD@ExtractKthArg{3}{AB{CD}E} yields: CD
%%
%% UD@ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%%
%% UD@ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthArg[1]{%
romannumeral0%
% #1: <integer number K>
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
newcommandUD@ExtractKthArgCheck[2]{%
UD@CheckWhetherNull{#1}{ }{%
expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}{#2}%
}%
}%
newcommandUD@ExtractKthArgLoop[2]{%
UD@CheckWhetherNull{#2}{ }{%
UD@CheckWhetherNull{#1}{%
UD@ExtractFirstArgLoop{#2UD@SelDOm}%
}{%
expandafterUD@Exchangeexpandafter{expandafter{UD@firstoftwo{}#2}}%
{expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}}%
}%
}%
}%
newcommandUD@RemoveTillUD@SelDOm{}%
longdefUD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
newcommandUD@ExtractFirstArgLoop[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@firstoftwo{}#1}%
{UD@firstoftwo{expandafter}{} UD@secondoftwo{}#1}%
{expandafterUD@ExtractFirstArgLoopexpandafter{UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% UD@ExtractDigitSequences{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from <token sequence>, nests each such sequence
%% into curly braces:
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66}
%% yields: {00}{78}{66}%
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66Baz543BaT954}
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequences[1]{%
romannumeral0UD@extractDigitSequencesLoop{#1}{}{}%
}%
newcommandUD@extractDigitSequencesLoop[3]{%
UD@CheckWhetherNull{#1}{%
UD@CheckWhetherNull{#3}{ #2}{ #2{#3}}%
}{%
UD@CheckWhetherBrace{#1}{%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoop
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}%
}{}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}{%
UD@CheckWhetherLeadingSpace{#1}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@gobblespace#1}}%
{}%
}{%
expandafterUD@CheckWhetherdigit
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{%
expandafterexpandafterexpandafterUD@Exchange
expandafterexpandafterexpandafter{%
expandafterexpandafterexpandafter{%
expandafterUD@Exchange
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{#3}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}{#2}}%
}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}%
}%
}%
}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequence{<integer K>}{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from <token sequence> if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequence[2]{%
romannumeral0%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0UD@extractDigitSequencesLoop{#2}{}{}}%
}{%
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
}%
%%=============================================================================
%% UD@ExtractDigitSequencesFromJobname
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from the top-level-expansion of the control-word-
%% token jobname, nests each such sequence into curly braces:
%%
%% E.g., if jobname = 00foo78Bar66, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}%
%%
%% E.g., if jobname = 00foo78Bar66Baz543BaT954, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequencesFromJobname{%
romannumeral0%
expandafterUD@extractDigitSequencesLoopexpandafter{jobname}{}{}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequenceFromJobname{<integer K>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from the top-level-expansion of the
%% control-word-token jobname if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequenceFromJobname[1]{%
romannumeral
expandafterUD@Exchangeexpandafter{expandafter{jobname}}%
{expandafterUD@secondoftwoUD@ExtractKthDigitSequence{#1}}%
}%
makeatother
begin{document}
makeatletter
defbraceshowloop#1{%
ifxrelax#1expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
{}{{#1}braceshowloop}%
}%
noindent
verb|UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}relax\
verb|UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}\
verb|jobname|:
jobname\
verb|UD@ExtractDigitSequencesFromJobname|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequencesFromJobnamerelax\
verb|UD@ExtractKthDigitSequenceFromJobname{-1}|:
UD@ExtractKthDigitSequenceFromJobname{-1}\
verb|UD@ExtractKthDigitSequenceFromJobname{0}|:
UD@ExtractKthDigitSequenceFromJobname{0}\
verb|UD@ExtractKthDigitSequenceFromJobname{1}|:
UD@ExtractKthDigitSequenceFromJobname{1}\
verb|UD@ExtractKthDigitSequenceFromJobname{2}|:
UD@ExtractKthDigitSequenceFromJobname{2}\
verb|UD@ExtractKthDigitSequenceFromJobname{3}|:
UD@ExtractKthDigitSequenceFromJobname{3}\
verb|UD@ExtractKthDigitSequenceFromJobname{4}|:
UD@ExtractKthDigitSequenceFromJobname{4}\
verb|UD@ExtractKthDigitSequenceFromJobname{5}|:
UD@ExtractKthDigitSequenceFromJobname{5}\
verb|UD@ExtractKthDigitSequenceFromJobname{6}|:
UD@ExtractKthDigitSequenceFromJobname{6}\
end{document}
add a comment |
Off the cuff I can offer four macros:
UD@ExtractDigitSequences{<arbitrary token sequence>}
This macro extracts all catcode-12-digit-sequences from<arbitrary token sequence>
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
This macro extracts the K-th catcode-12-digit-sequence from<arbitrary token sequence>
.
UD@ExtractDigitSequencesFromJobname
This macro extracts all catcode-12-digit-sequences from the expansion ofjobname
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequenceFromJobname{<number K>}
This macro extracts the K-th catcode-12-digit-sequence from the expansion ofjobname
.
Basically UD@ExtractDigitSequences{<arbitrary token sequence>}
and UD@ExtractDigitSequencesFromJobname
are wrappers for a recursive loop formed by the macro UD@extractDigitSequencesLoop
.
The gist of that recursive loop is:
UD@extractDigitSequencesLoop
processes three arguments:
The first argument denotes the (remaining) <arbitrary token-sequence>
.
The second argument denotes the collection of brace-nested digit-sequences collected so far.
The third argument denotes the collection of digits collected so far for the current digit-sequence.
First the loop checks whether the (remaining) <arbitrary token-sequence>
is empty.
If so, you are done and the second argument will be delivered and in case the third argument is not empty, it will also be delivered, nested in braces.
If not so, the loop will look at the first token of the (remaining) <arbitrary token-sequence>
, hereby taking into account braces and spaces as cases that need special treatment.
In case of the first token of the (remaining) <arbitrary token-sequence>
being both a non-brace-token and a non-digit-token, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
In case of the first token of the (remaining) <arbitrary token-sequence>
being an opening-brace, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2. In this case we also need to attach to argument 2 the result of applying the entire routine on the brace-nested first component/on the leading undelimited argument of the (remaining) <arbitrary token-sequence>
before calling the loop again with that undelimited argument removed from the (remaining) <arbitrary token-sequence>
.
(This case is important only as long as it is about extracting from arbitrary token sequences: The expansion of the jobname-primitive in any case does contain neither curly opening braces of category-code 1 nor closing braces of category code 2 but a collection of explicit character tokens that may contain explicit character tokens of category code 12(other but not with character code 32(space) and explicit character tokens of category code 10(space) and character code 32(space)=explicit non-funny space-tokens.)
In case of the first token of the (remaining) <arbitrary token-sequence>
being a digit-token, it needs to be attached to the current digit sequence in argument 3 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
Of course you also need routines for checking
- whether an argument is empty.
- whether an argument's first token is an opening brace.
- whether an argument's first token is a space token.
- whether an argument's first token is a digit-token.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
and UD@ExtractKthDigitSequenceFromJobname{<number K>}
"feed" the result of carrying out that loop to another macro which is called UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
which in turn is a routine for delivering the K-th undelimited argument from a list of undelimited/brace-nested arguments.
Everything is implemented so that it will also work in expansion-contexts like csname..endcsname
.
Neither do you need extensions like eTeX or LuaTeX, nor do you need any additional LaTeX2e packages like expl3 or substr or the like.
documentclass{article}
makeatletter
%%=============================================================================
%% Paraphernalia
%%.............................................................................
newcommandUD@firstoftwo[2]{#1}%
newcommandUD@secondoftwo[2]{#2}%
newcommandUD@Exchange[2]{#2#1}%
newcommandUD@gobblespace{}UD@firstoftwo{defUD@gobblespace}{} {}%
%%=============================================================================
%% Check whether argument is empty:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Crank out the cases of string "hitting"
%% - an opening-brace -> argument is not empty
%% - a non-brace-token -> argument is not empty
%% - a closing-brace -> argument is empty
%%.............................................................................
newcommandUD@CheckWhetherNull[1]{%
romannumeral0expandafterUD@secondoftwostring{expandafter
UD@secondoftwoexpandafter{expandafter{string#1}expandafter
UD@secondoftwostring}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@secondoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% Basically this is a variation of UD@CheckWhetherNull where non-emptiness
%% is ensured so that you need to only crank out the cases of string "hitting"
%% an opening-brace or a non-brace-token.
%%.............................................................................
newcommandUD@CheckWhetherBrace[1]{%
romannumeral0expandafterUD@secondoftwoexpandafter{expandafter{%
string#1.}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@firstoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
}%
%%=============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherLeadingSpace[1]{%
romannumeral0UD@CheckWhetherNull{#1}%
{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
{expandafterUD@secondoftwostring{UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
newcommandUD@CheckWhetherLeadingSpaceB{}%
longdefUD@CheckWhetherLeadingSpaceB#1 {%
expandafterUD@CheckWhetherNullexpandafter{UD@secondoftwo#1{}}%
{UD@Exchange{UD@firstoftwo}}{UD@Exchange{UD@secondoftwo}}%
{UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
expandafterexpandafterexpandafter}expandafterexpandafter
expandafter}expandafterUD@secondoftwoexpandafter{string}%
}%
%%=============================================================================
%% Check whether argument does not contain "!" (unless nested in braces):
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does not contain !>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does contain !>
%%.............................................................................
newcommandUD@GobbleToExclam{}%
longdefUD@GobbleToExclam#1!{}%
newcommandUD@CheckWhetherNoExclam[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@GobbleToExclam#1!}%
}%
%%=============================================================================
%% Check whether argument is a single explicit character-token of
%% category code 12 (other) that denotes digit:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherdigit{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is a single catcode-12-digit>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is not a single catcode-12-digit>
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherdigitfork{}%
longdefUD@CheckWhetherdigitfork#1!0!1!2!3!4!5!6!7!8!9!#2#3!!!!{#2}%
newcommandUD@CheckWhetherdigit[1]{%
romannumeral0%
UD@CheckWhetherNoExclam{#1}{%
UD@CheckWhetherdigitfork
!#1!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!#1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!#1!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!#1!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!#1!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!#1!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!#1!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!#1!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!#1!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!#1!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@secondoftwo}%
!!!!%
}{UD@firstoftwoexpandafter{} UD@secondoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% UD@ExtractKthArg{0}{ABCDE} yields: <nothing>
%%
%% UD@ExtractKthArg{3}{ABCDE} yields: C
%%
%% UD@ExtractKthArg{3}{AB{CD}E} yields: CD
%%
%% UD@ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%%
%% UD@ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthArg[1]{%
romannumeral0%
% #1: <integer number K>
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
newcommandUD@ExtractKthArgCheck[2]{%
UD@CheckWhetherNull{#1}{ }{%
expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}{#2}%
}%
}%
newcommandUD@ExtractKthArgLoop[2]{%
UD@CheckWhetherNull{#2}{ }{%
UD@CheckWhetherNull{#1}{%
UD@ExtractFirstArgLoop{#2UD@SelDOm}%
}{%
expandafterUD@Exchangeexpandafter{expandafter{UD@firstoftwo{}#2}}%
{expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}}%
}%
}%
}%
newcommandUD@RemoveTillUD@SelDOm{}%
longdefUD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
newcommandUD@ExtractFirstArgLoop[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@firstoftwo{}#1}%
{UD@firstoftwo{expandafter}{} UD@secondoftwo{}#1}%
{expandafterUD@ExtractFirstArgLoopexpandafter{UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% UD@ExtractDigitSequences{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from <token sequence>, nests each such sequence
%% into curly braces:
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66}
%% yields: {00}{78}{66}%
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66Baz543BaT954}
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequences[1]{%
romannumeral0UD@extractDigitSequencesLoop{#1}{}{}%
}%
newcommandUD@extractDigitSequencesLoop[3]{%
UD@CheckWhetherNull{#1}{%
UD@CheckWhetherNull{#3}{ #2}{ #2{#3}}%
}{%
UD@CheckWhetherBrace{#1}{%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoop
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}%
}{}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}{%
UD@CheckWhetherLeadingSpace{#1}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@gobblespace#1}}%
{}%
}{%
expandafterUD@CheckWhetherdigit
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{%
expandafterexpandafterexpandafterUD@Exchange
expandafterexpandafterexpandafter{%
expandafterexpandafterexpandafter{%
expandafterUD@Exchange
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{#3}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}{#2}}%
}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}%
}%
}%
}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequence{<integer K>}{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from <token sequence> if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequence[2]{%
romannumeral0%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0UD@extractDigitSequencesLoop{#2}{}{}}%
}{%
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
}%
%%=============================================================================
%% UD@ExtractDigitSequencesFromJobname
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from the top-level-expansion of the control-word-
%% token jobname, nests each such sequence into curly braces:
%%
%% E.g., if jobname = 00foo78Bar66, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}%
%%
%% E.g., if jobname = 00foo78Bar66Baz543BaT954, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequencesFromJobname{%
romannumeral0%
expandafterUD@extractDigitSequencesLoopexpandafter{jobname}{}{}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequenceFromJobname{<integer K>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from the top-level-expansion of the
%% control-word-token jobname if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequenceFromJobname[1]{%
romannumeral
expandafterUD@Exchangeexpandafter{expandafter{jobname}}%
{expandafterUD@secondoftwoUD@ExtractKthDigitSequence{#1}}%
}%
makeatother
begin{document}
makeatletter
defbraceshowloop#1{%
ifxrelax#1expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
{}{{#1}braceshowloop}%
}%
noindent
verb|UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}relax\
verb|UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}\
verb|jobname|:
jobname\
verb|UD@ExtractDigitSequencesFromJobname|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequencesFromJobnamerelax\
verb|UD@ExtractKthDigitSequenceFromJobname{-1}|:
UD@ExtractKthDigitSequenceFromJobname{-1}\
verb|UD@ExtractKthDigitSequenceFromJobname{0}|:
UD@ExtractKthDigitSequenceFromJobname{0}\
verb|UD@ExtractKthDigitSequenceFromJobname{1}|:
UD@ExtractKthDigitSequenceFromJobname{1}\
verb|UD@ExtractKthDigitSequenceFromJobname{2}|:
UD@ExtractKthDigitSequenceFromJobname{2}\
verb|UD@ExtractKthDigitSequenceFromJobname{3}|:
UD@ExtractKthDigitSequenceFromJobname{3}\
verb|UD@ExtractKthDigitSequenceFromJobname{4}|:
UD@ExtractKthDigitSequenceFromJobname{4}\
verb|UD@ExtractKthDigitSequenceFromJobname{5}|:
UD@ExtractKthDigitSequenceFromJobname{5}\
verb|UD@ExtractKthDigitSequenceFromJobname{6}|:
UD@ExtractKthDigitSequenceFromJobname{6}\
end{document}
add a comment |
Off the cuff I can offer four macros:
UD@ExtractDigitSequences{<arbitrary token sequence>}
This macro extracts all catcode-12-digit-sequences from<arbitrary token sequence>
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
This macro extracts the K-th catcode-12-digit-sequence from<arbitrary token sequence>
.
UD@ExtractDigitSequencesFromJobname
This macro extracts all catcode-12-digit-sequences from the expansion ofjobname
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequenceFromJobname{<number K>}
This macro extracts the K-th catcode-12-digit-sequence from the expansion ofjobname
.
Basically UD@ExtractDigitSequences{<arbitrary token sequence>}
and UD@ExtractDigitSequencesFromJobname
are wrappers for a recursive loop formed by the macro UD@extractDigitSequencesLoop
.
The gist of that recursive loop is:
UD@extractDigitSequencesLoop
processes three arguments:
The first argument denotes the (remaining) <arbitrary token-sequence>
.
The second argument denotes the collection of brace-nested digit-sequences collected so far.
The third argument denotes the collection of digits collected so far for the current digit-sequence.
First the loop checks whether the (remaining) <arbitrary token-sequence>
is empty.
If so, you are done and the second argument will be delivered and in case the third argument is not empty, it will also be delivered, nested in braces.
If not so, the loop will look at the first token of the (remaining) <arbitrary token-sequence>
, hereby taking into account braces and spaces as cases that need special treatment.
In case of the first token of the (remaining) <arbitrary token-sequence>
being both a non-brace-token and a non-digit-token, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
In case of the first token of the (remaining) <arbitrary token-sequence>
being an opening-brace, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2. In this case we also need to attach to argument 2 the result of applying the entire routine on the brace-nested first component/on the leading undelimited argument of the (remaining) <arbitrary token-sequence>
before calling the loop again with that undelimited argument removed from the (remaining) <arbitrary token-sequence>
.
(This case is important only as long as it is about extracting from arbitrary token sequences: The expansion of the jobname-primitive in any case does contain neither curly opening braces of category-code 1 nor closing braces of category code 2 but a collection of explicit character tokens that may contain explicit character tokens of category code 12(other but not with character code 32(space) and explicit character tokens of category code 10(space) and character code 32(space)=explicit non-funny space-tokens.)
In case of the first token of the (remaining) <arbitrary token-sequence>
being a digit-token, it needs to be attached to the current digit sequence in argument 3 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
Of course you also need routines for checking
- whether an argument is empty.
- whether an argument's first token is an opening brace.
- whether an argument's first token is a space token.
- whether an argument's first token is a digit-token.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
and UD@ExtractKthDigitSequenceFromJobname{<number K>}
"feed" the result of carrying out that loop to another macro which is called UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
which in turn is a routine for delivering the K-th undelimited argument from a list of undelimited/brace-nested arguments.
Everything is implemented so that it will also work in expansion-contexts like csname..endcsname
.
Neither do you need extensions like eTeX or LuaTeX, nor do you need any additional LaTeX2e packages like expl3 or substr or the like.
documentclass{article}
makeatletter
%%=============================================================================
%% Paraphernalia
%%.............................................................................
newcommandUD@firstoftwo[2]{#1}%
newcommandUD@secondoftwo[2]{#2}%
newcommandUD@Exchange[2]{#2#1}%
newcommandUD@gobblespace{}UD@firstoftwo{defUD@gobblespace}{} {}%
%%=============================================================================
%% Check whether argument is empty:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Crank out the cases of string "hitting"
%% - an opening-brace -> argument is not empty
%% - a non-brace-token -> argument is not empty
%% - a closing-brace -> argument is empty
%%.............................................................................
newcommandUD@CheckWhetherNull[1]{%
romannumeral0expandafterUD@secondoftwostring{expandafter
UD@secondoftwoexpandafter{expandafter{string#1}expandafter
UD@secondoftwostring}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@secondoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% Basically this is a variation of UD@CheckWhetherNull where non-emptiness
%% is ensured so that you need to only crank out the cases of string "hitting"
%% an opening-brace or a non-brace-token.
%%.............................................................................
newcommandUD@CheckWhetherBrace[1]{%
romannumeral0expandafterUD@secondoftwoexpandafter{expandafter{%
string#1.}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@firstoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
}%
%%=============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherLeadingSpace[1]{%
romannumeral0UD@CheckWhetherNull{#1}%
{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
{expandafterUD@secondoftwostring{UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
newcommandUD@CheckWhetherLeadingSpaceB{}%
longdefUD@CheckWhetherLeadingSpaceB#1 {%
expandafterUD@CheckWhetherNullexpandafter{UD@secondoftwo#1{}}%
{UD@Exchange{UD@firstoftwo}}{UD@Exchange{UD@secondoftwo}}%
{UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
expandafterexpandafterexpandafter}expandafterexpandafter
expandafter}expandafterUD@secondoftwoexpandafter{string}%
}%
%%=============================================================================
%% Check whether argument does not contain "!" (unless nested in braces):
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does not contain !>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does contain !>
%%.............................................................................
newcommandUD@GobbleToExclam{}%
longdefUD@GobbleToExclam#1!{}%
newcommandUD@CheckWhetherNoExclam[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@GobbleToExclam#1!}%
}%
%%=============================================================================
%% Check whether argument is a single explicit character-token of
%% category code 12 (other) that denotes digit:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherdigit{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is a single catcode-12-digit>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is not a single catcode-12-digit>
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherdigitfork{}%
longdefUD@CheckWhetherdigitfork#1!0!1!2!3!4!5!6!7!8!9!#2#3!!!!{#2}%
newcommandUD@CheckWhetherdigit[1]{%
romannumeral0%
UD@CheckWhetherNoExclam{#1}{%
UD@CheckWhetherdigitfork
!#1!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!#1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!#1!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!#1!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!#1!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!#1!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!#1!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!#1!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!#1!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!#1!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@secondoftwo}%
!!!!%
}{UD@firstoftwoexpandafter{} UD@secondoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% UD@ExtractKthArg{0}{ABCDE} yields: <nothing>
%%
%% UD@ExtractKthArg{3}{ABCDE} yields: C
%%
%% UD@ExtractKthArg{3}{AB{CD}E} yields: CD
%%
%% UD@ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%%
%% UD@ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthArg[1]{%
romannumeral0%
% #1: <integer number K>
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
newcommandUD@ExtractKthArgCheck[2]{%
UD@CheckWhetherNull{#1}{ }{%
expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}{#2}%
}%
}%
newcommandUD@ExtractKthArgLoop[2]{%
UD@CheckWhetherNull{#2}{ }{%
UD@CheckWhetherNull{#1}{%
UD@ExtractFirstArgLoop{#2UD@SelDOm}%
}{%
expandafterUD@Exchangeexpandafter{expandafter{UD@firstoftwo{}#2}}%
{expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}}%
}%
}%
}%
newcommandUD@RemoveTillUD@SelDOm{}%
longdefUD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
newcommandUD@ExtractFirstArgLoop[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@firstoftwo{}#1}%
{UD@firstoftwo{expandafter}{} UD@secondoftwo{}#1}%
{expandafterUD@ExtractFirstArgLoopexpandafter{UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% UD@ExtractDigitSequences{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from <token sequence>, nests each such sequence
%% into curly braces:
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66}
%% yields: {00}{78}{66}%
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66Baz543BaT954}
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequences[1]{%
romannumeral0UD@extractDigitSequencesLoop{#1}{}{}%
}%
newcommandUD@extractDigitSequencesLoop[3]{%
UD@CheckWhetherNull{#1}{%
UD@CheckWhetherNull{#3}{ #2}{ #2{#3}}%
}{%
UD@CheckWhetherBrace{#1}{%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoop
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}%
}{}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}{%
UD@CheckWhetherLeadingSpace{#1}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@gobblespace#1}}%
{}%
}{%
expandafterUD@CheckWhetherdigit
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{%
expandafterexpandafterexpandafterUD@Exchange
expandafterexpandafterexpandafter{%
expandafterexpandafterexpandafter{%
expandafterUD@Exchange
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{#3}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}{#2}}%
}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}%
}%
}%
}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequence{<integer K>}{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from <token sequence> if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequence[2]{%
romannumeral0%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0UD@extractDigitSequencesLoop{#2}{}{}}%
}{%
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
}%
%%=============================================================================
%% UD@ExtractDigitSequencesFromJobname
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from the top-level-expansion of the control-word-
%% token jobname, nests each such sequence into curly braces:
%%
%% E.g., if jobname = 00foo78Bar66, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}%
%%
%% E.g., if jobname = 00foo78Bar66Baz543BaT954, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequencesFromJobname{%
romannumeral0%
expandafterUD@extractDigitSequencesLoopexpandafter{jobname}{}{}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequenceFromJobname{<integer K>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from the top-level-expansion of the
%% control-word-token jobname if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequenceFromJobname[1]{%
romannumeral
expandafterUD@Exchangeexpandafter{expandafter{jobname}}%
{expandafterUD@secondoftwoUD@ExtractKthDigitSequence{#1}}%
}%
makeatother
begin{document}
makeatletter
defbraceshowloop#1{%
ifxrelax#1expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
{}{{#1}braceshowloop}%
}%
noindent
verb|UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}relax\
verb|UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}\
verb|jobname|:
jobname\
verb|UD@ExtractDigitSequencesFromJobname|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequencesFromJobnamerelax\
verb|UD@ExtractKthDigitSequenceFromJobname{-1}|:
UD@ExtractKthDigitSequenceFromJobname{-1}\
verb|UD@ExtractKthDigitSequenceFromJobname{0}|:
UD@ExtractKthDigitSequenceFromJobname{0}\
verb|UD@ExtractKthDigitSequenceFromJobname{1}|:
UD@ExtractKthDigitSequenceFromJobname{1}\
verb|UD@ExtractKthDigitSequenceFromJobname{2}|:
UD@ExtractKthDigitSequenceFromJobname{2}\
verb|UD@ExtractKthDigitSequenceFromJobname{3}|:
UD@ExtractKthDigitSequenceFromJobname{3}\
verb|UD@ExtractKthDigitSequenceFromJobname{4}|:
UD@ExtractKthDigitSequenceFromJobname{4}\
verb|UD@ExtractKthDigitSequenceFromJobname{5}|:
UD@ExtractKthDigitSequenceFromJobname{5}\
verb|UD@ExtractKthDigitSequenceFromJobname{6}|:
UD@ExtractKthDigitSequenceFromJobname{6}\
end{document}
Off the cuff I can offer four macros:
UD@ExtractDigitSequences{<arbitrary token sequence>}
This macro extracts all catcode-12-digit-sequences from<arbitrary token sequence>
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
This macro extracts the K-th catcode-12-digit-sequence from<arbitrary token sequence>
.
UD@ExtractDigitSequencesFromJobname
This macro extracts all catcode-12-digit-sequences from the expansion ofjobname
, nests each catcode-12-digit-sequence into curly braces.
UD@ExtractKthDigitSequenceFromJobname{<number K>}
This macro extracts the K-th catcode-12-digit-sequence from the expansion ofjobname
.
Basically UD@ExtractDigitSequences{<arbitrary token sequence>}
and UD@ExtractDigitSequencesFromJobname
are wrappers for a recursive loop formed by the macro UD@extractDigitSequencesLoop
.
The gist of that recursive loop is:
UD@extractDigitSequencesLoop
processes three arguments:
The first argument denotes the (remaining) <arbitrary token-sequence>
.
The second argument denotes the collection of brace-nested digit-sequences collected so far.
The third argument denotes the collection of digits collected so far for the current digit-sequence.
First the loop checks whether the (remaining) <arbitrary token-sequence>
is empty.
If so, you are done and the second argument will be delivered and in case the third argument is not empty, it will also be delivered, nested in braces.
If not so, the loop will look at the first token of the (remaining) <arbitrary token-sequence>
, hereby taking into account braces and spaces as cases that need special treatment.
In case of the first token of the (remaining) <arbitrary token-sequence>
being both a non-brace-token and a non-digit-token, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
In case of the first token of the (remaining) <arbitrary token-sequence>
being an opening-brace, the current digit sequence in argument 3 is finished and, if not empty, can be nested in braces and attached to argument 2. In this case we also need to attach to argument 2 the result of applying the entire routine on the brace-nested first component/on the leading undelimited argument of the (remaining) <arbitrary token-sequence>
before calling the loop again with that undelimited argument removed from the (remaining) <arbitrary token-sequence>
.
(This case is important only as long as it is about extracting from arbitrary token sequences: The expansion of the jobname-primitive in any case does contain neither curly opening braces of category-code 1 nor closing braces of category code 2 but a collection of explicit character tokens that may contain explicit character tokens of category code 12(other but not with character code 32(space) and explicit character tokens of category code 10(space) and character code 32(space)=explicit non-funny space-tokens.)
In case of the first token of the (remaining) <arbitrary token-sequence>
being a digit-token, it needs to be attached to the current digit sequence in argument 3 before calling the loop again with that token removed from the (remaining) <arbitrary token-sequence>
.
Of course you also need routines for checking
- whether an argument is empty.
- whether an argument's first token is an opening brace.
- whether an argument's first token is a space token.
- whether an argument's first token is a digit-token.
UD@ExtractKthDigitSequence{<number K>}{<arbitrary token sequence>}
and UD@ExtractKthDigitSequenceFromJobname{<number K>}
"feed" the result of carrying out that loop to another macro which is called UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
which in turn is a routine for delivering the K-th undelimited argument from a list of undelimited/brace-nested arguments.
Everything is implemented so that it will also work in expansion-contexts like csname..endcsname
.
Neither do you need extensions like eTeX or LuaTeX, nor do you need any additional LaTeX2e packages like expl3 or substr or the like.
documentclass{article}
makeatletter
%%=============================================================================
%% Paraphernalia
%%.............................................................................
newcommandUD@firstoftwo[2]{#1}%
newcommandUD@secondoftwo[2]{#2}%
newcommandUD@Exchange[2]{#2#1}%
newcommandUD@gobblespace{}UD@firstoftwo{defUD@gobblespace}{} {}%
%%=============================================================================
%% Check whether argument is empty:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Crank out the cases of string "hitting"
%% - an opening-brace -> argument is not empty
%% - a non-brace-token -> argument is not empty
%% - a closing-brace -> argument is empty
%%.............................................................................
newcommandUD@CheckWhetherNull[1]{%
romannumeral0expandafterUD@secondoftwostring{expandafter
UD@secondoftwoexpandafter{expandafter{string#1}expandafter
UD@secondoftwostring}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@secondoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@firstoftwo}%
}%
%%=============================================================================
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%% Basically this is a variation of UD@CheckWhetherNull where non-emptiness
%% is ensured so that you need to only crank out the cases of string "hitting"
%% an opening-brace or a non-brace-token.
%%.............................................................................
newcommandUD@CheckWhetherBrace[1]{%
romannumeral0expandafterUD@secondoftwoexpandafter{expandafter{%
string#1.}expandafterUD@firstoftwoexpandafter{expandafter
UD@secondoftwostring}expandafterexpandafterUD@firstoftwo{ }{}%
UD@firstoftwo}{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
}%
%%=============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherLeadingSpace[1]{%
romannumeral0UD@CheckWhetherNull{#1}%
{expandafterexpandafterUD@firstoftwo{ }{}UD@secondoftwo}%
{expandafterUD@secondoftwostring{UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
newcommandUD@CheckWhetherLeadingSpaceB{}%
longdefUD@CheckWhetherLeadingSpaceB#1 {%
expandafterUD@CheckWhetherNullexpandafter{UD@secondoftwo#1{}}%
{UD@Exchange{UD@firstoftwo}}{UD@Exchange{UD@secondoftwo}}%
{UD@Exchange{ }{expandafterexpandafterexpandafterexpandafter
expandafterexpandafterexpandafter}expandafterexpandafter
expandafter}expandafterUD@secondoftwoexpandafter{string}%
}%
%%=============================================================================
%% Check whether argument does not contain "!" (unless nested in braces):
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does not contain !>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> does contain !>
%%.............................................................................
newcommandUD@GobbleToExclam{}%
longdefUD@GobbleToExclam#1!{}%
newcommandUD@CheckWhetherNoExclam[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@GobbleToExclam#1!}%
}%
%%=============================================================================
%% Check whether argument is a single explicit character-token of
%% category code 12 (other) that denotes digit:
%%-----------------------------------------------------------------------------
%% UD@CheckWhetherdigit{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is a single catcode-12-digit>}%
%% {<Tokens to be delivered in case <argument which is
%% to be checked> is not a single catcode-12-digit>
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@CheckWhetherdigitfork{}%
longdefUD@CheckWhetherdigitfork#1!0!1!2!3!4!5!6!7!8!9!#2#3!!!!{#2}%
newcommandUD@CheckWhetherdigit[1]{%
romannumeral0%
UD@CheckWhetherNoExclam{#1}{%
UD@CheckWhetherdigitfork
!#1!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!#1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!#1!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!#1!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!#1!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!#1!6!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!#1!7!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!#1!8!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!#1!9!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!#1!{UD@firstoftwoexpandafter{} UD@firstoftwo}%
!0!1!2!3!4!5!6!7!8!9!{UD@firstoftwoexpandafter{} UD@secondoftwo}%
!!!!%
}{UD@firstoftwoexpandafter{} UD@secondoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% UD@ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% UD@ExtractKthArg{0}{ABCDE} yields: <nothing>
%%
%% UD@ExtractKthArg{3}{ABCDE} yields: C
%%
%% UD@ExtractKthArg{3}{AB{CD}E} yields: CD
%%
%% UD@ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%%
%% UD@ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthArg[1]{%
romannumeral0%
% #1: <integer number K>
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
newcommandUD@ExtractKthArgCheck[2]{%
UD@CheckWhetherNull{#1}{ }{%
expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}{#2}%
}%
}%
newcommandUD@ExtractKthArgLoop[2]{%
UD@CheckWhetherNull{#2}{ }{%
UD@CheckWhetherNull{#1}{%
UD@ExtractFirstArgLoop{#2UD@SelDOm}%
}{%
expandafterUD@Exchangeexpandafter{expandafter{UD@firstoftwo{}#2}}%
{expandafterUD@ExtractKthArgLoopexpandafter{UD@firstoftwo{}#1}}%
}%
}%
}%
newcommandUD@RemoveTillUD@SelDOm{}%
longdefUD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
newcommandUD@ExtractFirstArgLoop[1]{%
expandafterUD@CheckWhetherNullexpandafter{UD@firstoftwo{}#1}%
{UD@firstoftwo{expandafter}{} UD@secondoftwo{}#1}%
{expandafterUD@ExtractFirstArgLoopexpandafter{UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% UD@ExtractDigitSequences{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from <token sequence>, nests each such sequence
%% into curly braces:
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66}
%% yields: {00}{78}{66}%
%%
%% E.g., UD@ExtractDigitSequences{00foo78Bar66Baz543BaT954}
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequences[1]{%
romannumeral0UD@extractDigitSequencesLoop{#1}{}{}%
}%
newcommandUD@extractDigitSequencesLoop[3]{%
UD@CheckWhetherNull{#1}{%
UD@CheckWhetherNull{#3}{ #2}{ #2{#3}}%
}{%
UD@CheckWhetherBrace{#1}{%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoop
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}%
}{}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}{%
UD@CheckWhetherLeadingSpace{#1}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@gobblespace#1}}%
{}%
}{%
expandafterUD@CheckWhetherdigit
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{%
expandafterexpandafterexpandafterUD@Exchange
expandafterexpandafterexpandafter{%
expandafterexpandafterexpandafter{%
expandafterUD@Exchange
expandafter{romannumeral0UD@ExtractKthArgCheck{m}{#1}}{#3}%
}%
}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}{#2}}%
}{%
UD@CheckWhetherNull{#3}{UD@Exchange{{#2}}}{UD@Exchange{{#2{#3}}}}%
{expandafterUD@extractDigitSequencesLoopexpandafter{UD@firstoftwo{}#1}}%
{}%
}%
}%
}%
}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequence{<integer K>}{<token sequence>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from <token sequence> if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequence[2]{%
romannumeral0%
expandafterUD@Exchangeexpandafter{%
expandafter{romannumeral0UD@extractDigitSequencesLoop{#2}{}{}}%
}{%
expandafterUD@ExtractKthArgCheck
expandafter{romannumeralnumbernumber#1 000}%
}%
}%
%%=============================================================================
%% UD@ExtractDigitSequencesFromJobname
%%-----------------------------------------------------------------------------
%% Extracts sequences of explicit character tokens of category code 12
%% that denote digits from the top-level-expansion of the control-word-
%% token jobname, nests each such sequence into curly braces:
%%
%% E.g., if jobname = 00foo78Bar66, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}%
%%
%% E.g., if jobname = 00foo78Bar66Baz543BaT954, then
%%
%% UD@ExtractDigitSequencesFromJobname
%%
%% yields: {00}{78}{66}{543}{954}%
%%
%% Does not deliver any token in case <token sequence> does not contain
%% explicit character-tokens of category code 12(other) that denote
%% digits.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractDigitSequencesFromJobname{%
romannumeral0%
expandafterUD@extractDigitSequencesLoopexpandafter{jobname}{}{}%
}%
%%=============================================================================
%% UD@ExtractKthDigitSequenceFromJobname{<integer K>}
%%-----------------------------------------------------------------------------
%% Extracts the K-th sequence of explicit character-tokens of category
%% code 12 (other) that denote digits from the top-level-expansion of the
%% control-word-token jobname if existent.
%% Otherwise doesn't deliver any token.
%%
%% Due to romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%.............................................................................
newcommandUD@ExtractKthDigitSequenceFromJobname[1]{%
romannumeral
expandafterUD@Exchangeexpandafter{expandafter{jobname}}%
{expandafterUD@secondoftwoUD@ExtractKthDigitSequence{#1}}%
}%
makeatother
begin{document}
makeatletter
defbraceshowloop#1{%
ifxrelax#1expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
{}{{#1}braceshowloop}%
}%
noindent
verb|UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequences{{{32}54{}t 65zk_+} 8}relax\
verb|UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{-1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{0}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{1}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{2}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{3}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{4}{{{32}54{}t 65zk_+} 8}\
verb|UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}|:
UD@ExtractKthDigitSequence{5}{{{32}54{}t 65zk_+} 8}\
verb|jobname|:
jobname\
verb|UD@ExtractDigitSequencesFromJobname|:
expandafterexpandafterexpandafterbraceshowloop
UD@ExtractDigitSequencesFromJobnamerelax\
verb|UD@ExtractKthDigitSequenceFromJobname{-1}|:
UD@ExtractKthDigitSequenceFromJobname{-1}\
verb|UD@ExtractKthDigitSequenceFromJobname{0}|:
UD@ExtractKthDigitSequenceFromJobname{0}\
verb|UD@ExtractKthDigitSequenceFromJobname{1}|:
UD@ExtractKthDigitSequenceFromJobname{1}\
verb|UD@ExtractKthDigitSequenceFromJobname{2}|:
UD@ExtractKthDigitSequenceFromJobname{2}\
verb|UD@ExtractKthDigitSequenceFromJobname{3}|:
UD@ExtractKthDigitSequenceFromJobname{3}\
verb|UD@ExtractKthDigitSequenceFromJobname{4}|:
UD@ExtractKthDigitSequenceFromJobname{4}\
verb|UD@ExtractKthDigitSequenceFromJobname{5}|:
UD@ExtractKthDigitSequenceFromJobname{5}\
verb|UD@ExtractKthDigitSequenceFromJobname{6}|:
UD@ExtractKthDigitSequenceFromJobname{6}\
end{document}
edited Dec 12 at 10:43
answered Dec 12 at 3:44
Ulrich Diez
4,035615
4,035615
add a comment |
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f464025%2fpulling-numbers-out-of-a-file-name%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown