# 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

"and it seems way easier to remove dots automatically but to add them automatically"

Tex is, by nature, already iterating through the tokens.

Inserting a dot then becomes easy.

Using David Carlisle's famous zz macro,

MWE

\documentclass{article}

\def\zz#1{\def\zzsep{}\zzz#1\relax}
\def\zzz#1{\ifx\relax#1\else\def\zzsep{.}#1\zzsep\expandafter\zzz\fi}

\begin{document}

DLR

\zz{DLR}
\end{document}

I set zzep to \- and hyphenchar to blank to do quick package-less line-breaks on long Chinese and Japanese text blocks, where otherwise the material would run off the edge of the page.

\documentclass[12pt]{article}

\def\zz#1{\def\zzsep{}\zzz#1\relax}
\def\zzz#1{\ifx\relax#1\else\def\zzsep{\-}#1\zzsep\expandafter\zzz\fi}

\newenvironment{hyphblnk}{%
\let\oldhyphenchar\hyphenchar%
\hyphenchar\font=\string"20}{\let\hyphenchar\oldhyphenchar}

\usepackage{fontspec}
\setmainfont{Noto Serif CJK SC}
\begin{document}

From Wikipedia (article about the solar system):

\vskip2em
With line-breaks:

\begin{hyphblnk}
\zz{太陽系是一个受太阳引力约束在一起的行星系统，包括太阳以及直接或间接围绕太阳运动的天体。在直接围绕太阳运动的天体中，最大的八颗被称为行星，其余的天体要比行星小很多，比如矮行星、太阳系小行星和彗星。軌道間接围绕太陽运动的天體是衛星，其中有兩顆比最小的行星水星還要大。}
\end{hyphblnk}

\end{document}

To expand on the comment about using options:

An illustration may help of what I mean about using option settings.

In these citations:

the cases (fully in footnotes) are set to dotless italic v, upright party names, just for fun, and case year last, US-style, bib setting (default setting, footnote 10; override setting, 11), and then entry-level setting overriding that with case year first (12). Independently, the SCC favoured MNC style of a bare (bracketless) year (16), compared with the non-bare override version (17). And so on with other elements, with secondary materials, bibliography item grouping and labelling, whether the citation prints the case name inline and reference in a footnote, or all inline, or all footnote. Etc.

It's turning out to be very flexible. And also open-ended.

The options for the above look like this:

lawrefstyle=caseallbelow, %name and ref in footnote: caseallabove, caseabovebelow, caseallbelow
party-names-italic=false,%default true
statute-name-italic=false,%default true
party-separator-italic=true, %default true
party-separator-dotted=false, %defau--lt false
printbib-legal=true, %print a McGill bib: default false; print command = \printbibliographylc
printbib-aglc=true, %print an AGLC bib: default false; print command = \printbibliographylc
%   print-toc-tos=false, %table of cases and statutes: default true
allbib-mnc-bare=false, %print MNC squareless: default false = not bare = with square brackets
allbib-mnc-override=false, %bib option for MNC has priority: default true
case-year-last=true,
• Yes, it is easy to write a macro that automatically inserts dots after every letter or every capital letter. That is not what I meant. What is difficult is implementing the logic when a period is inserted and when it isn't. D.L.R. but SCC, Int'l L. Rev., etc. – jjbornheim Apr 28 at 10:13
• @jjbornheim Yes, completely agree. I tried to start coding a styleset as an experiment, with Modern Law Review requiring an upright dotted v, and McGill a dotless italic v. It got too complex too quickly, even with just two, let alone many, so I've switched over to user options, party-separator-italic=true, and party-separator-dotted=false, and case-year-last=true, and so on, with bib-level and entry-level overrides for cross-jurisdictional matter. Then it just becomes a mix-and-match choice for the user. And the style becomes a set of on/off switches. Then it's just one cite command. – Cicada May 14 at 12:07
• @Phelype Oleinik Edit done! – Cicada May 14 at 12:57