One can always take existing code and re-work. Here, I've used some ideas from expl3 but slightly simplified (at the cost of a little robustness)
\def\zCountDigits#1{%
\number\numexpr0\zCountDigitsAux#1\zCountDigitsEnd\relax
}
\def\zCountDigitsAux#1{%
\ifx\zCountDigitsEnd#1\else+1\expandafter\zCountDigitsAux\fi
}
\def\zCountDigitsEnd{\zCountDigitsEnd}
\zCountDigits{}
\zCountDigits{123}
\zCountDigits{abC}
\def\zGetDigit#1#2{%
\zGetDigitLoop{1}{#2}#1\zGetDigitEnd
}
\def\zGetDigitLoop#1#2#3{%
\ifx\zGetDigitEnd#3\else
\ifnum #1 = #2 %
\expandafter\expandafter\expandafter\zGetDigitCleanup
\expandafter\expandafter\expandafter#3%
\else
\zGetDigitLoopStep{#1}{#2}%
\fi
\fi
}
\def\zGetDigitCleanup#1#2\zGetDigitEnd{#1}
\def\zGetDigitLoopStep#1#2\fi\fi{\fi\fi
\expandafter\zGetDigitLoop\expandafter{\number\numexpr#1 + 1\relax}{#2}%
}
\def\zGetDigitEnd{\zGetDigitEnd}
\zGetDigit{abc}{1}
\zGetDigit{abc}{3}
\zGetDigit{abc}{4}
\zGetDigit{abc}{-2}
\bye
(I'm not 100% from the spec on whether you want space handling, brace groups preserved, etc.: I've gone for the simple approach, but they could be included).
The way that the loop works is that \zCountDigitsAux grabs one token at a time: if you look at the set up, there are no braces around #1 in \zCountDigits. Thus \ifx\zCountDigitsEnd#1 is comparing exactly one token to the end marker.
I've used a private 'end of loop' token rather than say \relax. That's to ensure that we can still count an input sequence that includes \relax: the counting approach here is valid not only for character tokens but also macros, \chardef tokens, etc.
Assuming the loop has not finished, we insert +1 then need to close the conditional. The \zCountDigitsAux macro needs to see what is next in the input stream, and we need to avoid opening more and more conditional levels. Hence I use
\expandafter\zCountDigitsAux\fi. This expands the \fi, ending the conditional, before \zCountDigitsAux gets expanded. (Without this, \zCountDigitsAux would grab the \fi, and we'd run out of stack.) There's no parameter passed to \zCountDigitsAux here as the rest of the original argument to \zCountDigits is still in the input stream.
expl3for that, but what's the point of not using packages not maintained by the LaTeX team? For example,xstringprovides both\StrLenfor the length of a string and\StrCharto get the n-th character. – siracusa Jul 27 '19 at 04:14expl3is maintained by the LaTeX Team.expl3is just a (really, really large) bunch of TeX macros written with a funny catcode setting. The macros you want are\tl_count:nand\tl_item:nn(or\str...): you can always copy them and remove the_and:. Will it or will it not beexpl3code then? Your requirements seem a bit pointless to me... – Phelype Oleinik Jul 27 '19 at 06:12expl3should not be used as well as packages not authored/maintained by LaTeX Team sounds like OP is perfectly aware thatexpl3is maintained by the LaTeX team.) – sgf Jul 27 '19 at 19:54