Regex replace decimal numbers that do not belong to a hex color value and are not followed by a certain string

Viewed 70

In the string below, I need All the decimal numbers are placed in a command like \textnormal{#} if they

  1. do not belong to a hex color (always in this format: \textcolor{#dddddd} where d can be 0-9, a-f, and/or A-F)
  2. are not followed by a pt

\documentclass[16.5pt]{article}\begin{document}$$\textcolor{#FF80C0}{E} \textcolor{#FF0027}{CR} = 4A + 0.031De^1.63 = \textcolor{#0000FF}{{CR}_0}$$\end{document}

So, it should be

\documentclass[16.5pt]{article}\begin{document}$$\textcolor{#FF80C0}{E} \textcolor{#FF0027}{CR} = \textnormal{4}A + \textnormal{0.031}De^\textnormal{1.63} = \textcolor{#0000FF}{{CR}_\textnormal{0}}$$\end{document}

I have tried

Regex.Replace(@"(\d+(?:\.\d+)?)(?![\d.])(?!pt)(?<!#[0-9a-f-A-F]{6})", n => $@"\textnormal{{{n.Value}}}");

But the digits belonging to hex colors are not excluded. Here is the link.

EDIT:

The following piece of code does what I need.

int ib = -1; // begining index of a decimal number
int ie = -1; // end index of a decimal number

for (int i = 0; i < S.Length; i++)
{
    if (ib == -1 && (char.IsDigit(S[i]) || S[i] == '.'))
    {
        if (S[i] == '.' && char.IsDigit(S[i - 1]) == false && char.IsDigit(S[i + 1]) == false)
        {
            ib = -1;
            continue; // The found '.' char is a single dot, not the decimal point of a number.
        }

        ib = i; // Here is the begining of a decimal number. Now the end of the number should be found.
    }
    else if (ib != -1 && !(char.IsDigit(S[i]) || S[i] == '.'))
    {
        ie = i - 1; // Here is the end of the decimal number.

        if (S.Substring(ie + 1, "pt".Length) == "pt")
        {
            i++;
            ib = -1;
            continue; // The decimal number is followed by a "pt"
        }
        else if (S.Substring(ib - "FFFFFF".Length, "FFFFFF".Length).Contains('#'))
        {
            ib = -1;
            continue; // The number belongs to a hex color code
        }
        else
        {
            S = S.Insert(ie + 1, "}");
            S = S.Insert(ib, @"\textnormal{");

            ib = -1;
            i += @"\textnormal{".Length;
        }
    }
}
1 Answers

You can use

Regex.Replace(text, @"(#[0-9a-fA-F]{6})|\d+(?:\.\d+)?(?![\d.]|pt)", n =>
    n.Groups[1].Success ? n.Groups[1].Value : $@"\textnormal{{{n.Value}}}"
)

Regex details:

  • (#[0-9a-fA-F]{6}) - Group 1: # and then six hex chars
  • | - or
  • \d+(?:\.\d+)? - one or more digits and then an optional sequence of a dot and one or more digits...
  • (?![\d.]|pt) - not immediately followed with a digit, or a ., or pt substring.

The replacement is a match evaluator lambda expression that replaces the found match either with the Group 1 captured value if Group 1 (hex value) was matched, otherwise, replaces with the match enclosed with \textnormal{ and }.

See the C# demo:

var text = @"\documentclass[16.5pt]{article}\begin{document}$$\textcolor{#FF80C0}{E} \textcolor{#FF0027}{CR} = 4A + 0.031De^1.63 = \textcolor{#0000FF}{{CR}_0}$$\end{document}";
Console.WriteLine(Regex.Replace(text, @"(#[0-9a-fA-F]{6})|\d+(?:\.\d+)?(?![\d.]|pt)", n =>
    n.Groups[1].Success ? n.Groups[1].Value : $@"\textnormal{{{n.Value}}}"
));
// => \documentclass[16.5pt]{article}\begin{document}$$\textcolor{#FF80C0}{E} \textcolor{#FF0027}{CR} = \textnormal{4}A + \textnormal{0.031}De^\textnormal{1.63} = \textcolor{#0000FF}{{CR}_\textnormal{0}}$$\end{document}
Related