0

For instance. I have as input string "635783455831176452" and want to replace every 6 with K, every 7 with L, and every 1 with U. Can this be done within 1 single Regex.Replace. I know I can use nested replace statements like

Regex.Replace(
    Regex.Replace(
        Regex.Replace(
            "635783455831176452", "6+", "K"),
        "7+", "L"),
    "1+", "U");

However, the key is to use a single regex.replace. Is it possible, and if so what would be the syntax? Thank you much!

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Moxique
  • 23
  • 3
  • First question: your code contradicts your description in that it will replace *sequences* of `6` with one single `K`. Is this intentional? –  Sep 20 '15 at 15:57
  • @Eris yes, seems like a duplicate ... I was already working on my answer, but voting to close now –  Sep 20 '15 at 16:35
  • In theory, it may be possible with a single Regex, but as with most "clever" regexes, it would be a maintenance nightmare. – Eris Sep 20 '15 at 16:39
  • @FelixPalmen its not a duplicate. OP wants to do it with Regex – M.kazem Akhgary Sep 20 '15 at 16:54
  • @M.kazemAkhgary If you have a hammer, everything looks like a nail. –  Sep 20 '15 at 16:56
  • @Eris read the question. _the key is to use a single regex.replace. Is it possible, and if so what would be the syntax?_ No answer inside that link provides the answer to this question – M.kazem Akhgary Sep 20 '15 at 16:56
  • 2
    @M.kazemAkhgary it's most probably an *xy-problem* –  Sep 20 '15 at 16:57
  • @FelixPalmen i agree. there is no way to replace like that with single call to regex. – M.kazem Akhgary Sep 20 '15 at 17:01
  • It might be possible with the `MatchEvaluator` option on `Regex.Replace`: https://msdn.microsoft.com/en-us/library/ht1sxswy – Eris Sep 20 '15 at 17:11
  • @Eris I think you're right. It might be possible with MatchEvaluator. I will look into that. Thanks for pointing me in that directions! – Moxique Sep 21 '15 at 13:37
  • @M.kazemAkhgary you got great comprehensive reading skills...kudos! – Moxique Sep 21 '15 at 13:42
  • Related http://stackoverflow.com/questions/11389466/multiple-word-search-and-replace-in-notepad/16104946#16104946 – AdrianHHH Sep 22 '15 at 08:03

2 Answers2

1

According to your description, I'd say a regular expression is the wrong tool. Some custom extension method might do, e.g.:

static class StringExtensions
{
    public static string tr(this string st,
            string orig, string replacement)
    {
        var sb = new StringBuilder(st.Length);
        foreach (char c in st)
        {
            bool replaced = false;
            for (int i = 0; i < orig.Length; ++i)
            {
                if (replacement.Length > i && c == orig[i])
                {
                    sb.Append(replacement[i]);
                    replaced = true;
                    break;
                }
            }
            if (!replaced) sb.Append(c);
        }
        return sb.ToString();
    }
}

Usage:

static class Program
{
    static int Main()
    {
        Console.WriteLine("1234567890".tr("167", "UKL"));
        return 0;
    }
}
  • Good answer but this gets much more compact `tr(..) { var orgToRep = org.Zip(replacement, (x, y) => new { x, y }).ToDictionary(x => x.x, x => x.y); return new string(st.Select(x => { char rep; return ortToRep.TryGetValue(x, out rep) ? rep : x; })); }` (Not tested - only posted here.) If you've an extensions method that's named GetValueOrDefault (like ConcurrentDictionary has) you can replace the select statement by a very smaller one. – Sebastian Schumann Sep 20 '15 at 17:59
  • I recommend rewriting the answer using a `MatchEvaluator`, it could be done inline with a lambda instead of an extension method. – Eris Sep 20 '15 at 18:01
  • @Verarind Huh? Could you post this as an answer, so all this linq stuff gets somehow readable? ;) –  Sep 20 '15 at 18:01
  • @Eris, this would mean to do it with regular expressions, which I consider a *vast* overhead here ... –  Sep 20 '15 at 18:04
1

As requested the comment as answer.

This is the same answer as Felix Palmen already gave but it uses Linq.

public static string tr(this string st, string orig, string replacement)
{
    var orgToRep = orig
        .Zip(replacement, (x, y) => new { x, y })
        .ToDictionary(x => x.x, x => x.y);
    return new string(
        st
        .Select(
            x => 
            {
                char rep;
                return orgToRep.TryGetValue(x, out rep) ? rep : x;
            })
        .ToArray());
}

If you have an extension method for IDictionary<TKey, TValue> thats called GetValueOrDefault like Nullable has you could write it shorter:

public static string tr(this string st, string orig, string replacement)
{
    var orgToRep = orig
        .Zip(replacement, (x, y) => new { x, y })
        .ToDictionary(x => x.x, x => x.y);
    return new string(st.Select(x => orgToRep.GetValueOrDefault(x, x)).ToArray());
}

The extension method should be trivial.

Sebastian Schumann
  • 3,204
  • 19
  • 37
  • Ok, thanks, now it is readable, but what is this `Zip` method? It's not in `System.String`, so where is it defined? –  Sep 20 '15 at 20:40
  • @FelixPalmen [`Zip`](https://msdn.microsoft.com/en-us/library/dd267698(v=vs.110).aspx) is a linq function that allowes you to iterate over a sequence and get the corresponding element (the element of the same 'index') of a second sequence. – Sebastian Schumann Sep 21 '15 at 04:47
  • Ah that's cool ... new in 4.5? Never seen it in use before, but I'll remember that one. I guess for large input strings, your version would be even faster. –  Sep 21 '15 at 05:44