# Replace characters in string based on preceding and following character

The following two strings are the result of the expansion of a biblatex citation command:

Unicom Computer Corp. (in re), 13 F.3d 321, 30 Collier Bankr. Cas. 2d 655, 25 Bankr. Ct. Dec. 152 (9th Cir. 1994)

Personal Property Security Act (P.E.I.), R.S.P.E.I. 1988, c. P-3.1

What I want to do is wrap the biblatex citation command in a macro that will replace all dots, with a replacement character determined by the following condition set:

1. If the character to the right of the dot is not a number, the dot should be removed entirely.

2. If the character to the right of the dot is a number, and the character to the left of the dot is a number, the dot should be replaced by a dot, that is the dot should be kept in place.

3. If the character to the right of the dot is a number, and the character to the left of the dot is not a number, the dot should be replaced by a space.

Applying the conditions on the example strings above, the macro should yield the following results:

Unicom Computer Corp (in re), 13 F 3d 321, 30 Collier Bankr Cas 2d 655, 25 Bankr Ct Dec 152 (9th Cir 1994)

Personal Property Security Act (PEI), RSPEI 1988, c P-3.1

It should be noted that the dots in the examples are already in the entries in the bibtex file. The reason that the dots are in there are that certain legal citation styles require the punctuations, whereas others require they are not present, the biblatex style I am writing should have an option to turn punctuation on or off, and it seems way easier to remove dots automatically but to add them automatically.

I have looked into the xstring package, but I was not able to find an appropriate macro in there. Furthermore, I would prefer being able to do that in plain TeX, so that the resulting style can be used without too many dependencies.

-
That sounds like the perfect kind of job for the l3regex package. However, it does require the whole expl3 bundle, which is a non-trivial dependency. Otherwise, you may be able to approach the problem in several passes. In order to look behind the dots, you will need to have a function to reverse the string of characters. – Bruno Le Floch Oct 20 '11 at 14:48

With a simple loop, xstring is also able to do such things:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xstring}
\def\removedot#1{%
\def\citation{#1}%
\loop
\StrPosition\citation.[\dotpos]%
\ifnum\dotpos>0
\StrMid\citation1{\number\numexpr\dotpos-1}%
\StrChar\citation{\number\numexpr\dotpos-1}[\antechar]%
\StrChar\citation{\number\numexpr\dotpos+1}[\postchar]%
\antechar
\IfInteger\postchar{\IfInteger\antechar.{\unless\ifx\postchar\space\space\fi}}{}%
\postchar
\StrGobbleLeft\citation{\number\numexpr\dotpos+1}[\citation]%
\repeat
\citation
}
\begin{document}
\removedot{Unicom Computer Corp. (in re), 13 F.3d 321, 30 Collier Bankr. Cas. 2d 655, 25 Bankr. Ct. Dec. 152 (9th Cir. 1994)\endgraf
Personal Property Security Act (P.E.I.), R.S.P.E.I. 1988, c. P-3.1}
\end{document}


EDIT: sorry for the bug. Here is a code in which the bug is fixed and the result is stored in the macro \newcitation:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xstring}
\expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
#1\expandafter\expandafter\expandafter{\expandafter#1#2}}
\def\removedot#1{%
\def\citation{#1}\def\newcitation{}%
\loop
\StrPosition\citation.[\dotpos]%
\ifnum\dotpos>0
\StrGobbleLeft\citation{\number\numexpr\dotpos-2}[\citation]%
\StrChar\citation3[\postchar]%
\IfInteger\postchar
\relax
\StrGobbleLeft\citation2[\citation]%
\repeat
\newcitation% use the \newcitation macro
}
\begin{document}
\removedot{Unicom Computer Corp. (in re), 13 F.3d 321, 30 Collier Bankr. Cas. 2d 655, 25 Bankr. Ct. Dec. 152 (9th Cir. 1994)\endgraf
Personal Property Security Act (P.E.I.), R.S.P.E.I. 1988, c. P-3.1}
\end{document}


For sure, none of the codes shown in this thread are written with pure plain-TeX macros. It would be so hard that it is much better to use a package, either latex3 parser or xstring, both written in plain-teX.

-
it mostly works, but some characters appear twice (e.g., R.S.P.E.I. becomes RRSPEI). And that solution has the same flaw as mine, namely, it is not done directly in TeX primitives. – Bruno Le Floch Oct 22 '11 at 9:01
Thank you both. I tend to prefer unbonpetit's approach, as it only uses one other package. I will have to do some testing, but I think I am going to accept this answer. Two questions though: 1. Is there a specific reason you named the one macro citation? citationis in fact also a macro name used by the biblatex package, and I noticed that calling the new macro citation as well adds a lot of meaningless entries to the bibliography when using certain biblatex styles (including mine). 2. What is the advantage of storing the result in the \newcitation macro? – jjbornheim Oct 22 '11 at 17:45
I acknoledge that \citation is a bad choice for a macro name. Sorry for that. Of course, you have to rename it: \foobar or anything else will be much better than \citation! For the second point, I could have simply displayed the result. But storing it in a macro makes possible to do whatever you want with it. – unbonpetit Oct 22 '11 at 18:34

Here is a solution using LaTeX packages (works with l3regex, xparse and the expl3 bundle from 2011-10-09 onwards).

\RequirePackage{l3regex,xparse}
\ExplSyntaxOn
\tl_new:N \l_removedots_tl
\tl_new:N \l_removedots_str
\DeclareDocumentCommand{\removedots}{+v}
{
% Store the verbatim argument into a macro.
\str_set:Nn \l_removedots_str {#1}

% Remove all dots followed by a non-digit.
\regex_replace_all:nnN { \.(\D) } { \1 } \l_removedots_str

% Replace "non-digit--dot--digit" by "non-digit--space-digit".
\regex_replace_all:nnN { (\D)\.(\d) } { \1\ \2 } \l_removedots_str

% Re-tokenize the result, storing it into \l_removedots_tl
\tl_set_rescan:Nno \l_removedots_tl
{
\int_set:Nn \tex_newlinechar:D { \^^M }
\int_set:Nn \tex_endlinechar:D { \^^M }
}
\l_removedots_str

% Then simply typeset that, but you could do other things to it.
\tl_use:N \l_removedots_tl
}
\ExplSyntaxOff

\documentclass{article}
\begin{document}

\removedots |Unicom Computer Corp. (in re), 13 F.3d 321, 30 Collier Bankr. Cas. 2d 655, 25 Bankr. Ct. Dec. 152 (9th Cir. 1994)

Personal Property Security Act (P.E.I.), R.S.P.E.I. 1988, c. P-3.1|

\end{document}


Under the hood, this is doing a lot of work, because TeX is not well suited to regular expression parsing. It will be difficult to do the same directly in TeX primitives.

-
Fantastic answer, Bruno! :) – Paulo Cereda Oct 22 '11 at 8:53
@Paulo: Interestingly, unbonpetit's approach and mine have roughly the same performance. They do very different things internally. – Bruno Le Floch Oct 22 '11 at 9:10