1

I found an elegant solution for what I'm trying to execute, which is the replacement of emotes in a string with a URL (it'll end up being an img tag once I figure this out. This solution was found here: iterating over a dictionary of Regex in C#. My issue is that when it reiterates over the already affected content block (output) it replaces the :/ in http:// with an emote URL.

I am getting my emotes from the Twitch API, which provides a regex for each emote and a URL for the image.

I'd like to replace all of the emotes in my Dictionary without overwriting instances contained in URLs. Is this possible? I thought this would be a simple effort but it's ended up spiraling way out of my realm of knowledge at this point.

public static string Replacements(string text)
{

    string output = text;
    foreach (KeyValuePair<string, string> item in dict1)
    {
        //here replace output again
        output = Regex.Replace(output, item.Key, item.Value); 

    }


    return output;
}

Edit: Sorry I didn't give enough info; still new to posting here. So the Regexes are fetched from the Twitch API for the streamer's emotes. Here's an example: Twitch API. This is a JSON string that provides the emotes, each emote containing a Regex identifier for the emote and a URL for the image of the emote.

"emoticons":[
    {"width":24,"height":18,"regex":"\\:-?D","state":"active","subscriber_only":false,"url":"http://static-cdn.jtvnw.net/jtv_user_pictures/chansub-global-emoticon-9f2ac5d4b53913d7-24x18.png"}
    {"width":24,"height":18,"regex":"\\:-?[\\\\/]","state":"active","subscriber_only":false,"url":"http://static-cdn.jtvnw.net/jtv_user_pictures/chansub-global-emoticon-374120835234cb29-24x18.png"}
...]

I'm pulling out just the regex and the URL for the image into a dictionary; key is regex and value is the URL. Using the code provided above, it replaces the :/ in http:// with the emote for :/, so after I run the code I end up with the following:

httphttp://static-cdn.jtvnw.net/jtv_user_pictures/chansub-global-emoticon-374120835234cb29-24x18.png/static-cdn.jtvnw.net/jtv_user_pictures/chansub-global-emoticon-9f2ac5d4b53913d7-24x18.png

I hope this clarifies my predicament more.

Community
  • 1
  • 1
Zomgrei
  • 19
  • 3
  • 1
    Sorry, but there is not enough data in your question. Please add some examples. The best would be an [MVCE (minimal complete verifiable example)](http://stackoverflow.com/help/mcve). – Wiktor Stribiżew Sep 30 '15 at 13:39
  • @stribizhev: I think OP wants to prevent to translate the already replaced url's which might contain emoticon charaters. – Stefan Sep 30 '15 at 13:40
  • 2
    @Stefan: Yes, but that means there are patterns already. They are missing from the question. Certainly, a look-behind is necessary (`(?<!https?)` or similar), but I can't show it even in a comment without any sample that OP can re-use. – Wiktor Stribiżew Sep 30 '15 at 13:42
  • What kind of text is the input? (Plain text, HTML, XML, other) – theB Sep 30 '15 at 13:46
  • Because the Regex scans the whole string over again I don't think the Regex.Replace solution is most fit here. Better to store the match positions in a separate buffer and replace them when you have all the emoticon's positions. This would imply 2 loops though. Ofcource an alternative is to reverse the logic. Scan the string, by character, for a matching entry in the dictionary. But this won't work easely with a regex – Stefan Sep 30 '15 at 14:01
  • 1
    Thanks, @stribizhev, I've added more clarification. I hope it makes more sense what I'm trying to accomplish and how I'm getting there. – Zomgrei Sep 30 '15 at 14:35
  • If `:/` is the only problem case, you could avoid the problem by doing that replacement *first*. – Paul Roub Sep 30 '15 at 14:42
  • @PaulRoub While that's a fair point, I don't really control the order in which the Twitch API feeds me the emotes, so short of making some ugly hack to put that first manually and ignoring the one fed by Twitch, I'm unsure how to go about doing that. – Zomgrei Sep 30 '15 at 14:45
  • @Stefan That's what I was thinking, but I don't know if I have the knowledge on how to execute that. I'd be fine with a couple of loops through, as they're not outrageously high overhead, but I'm not sure how I'd go about storing positions in a buffer and then doing a full-on replace of those buffered positions. – Zomgrei Sep 30 '15 at 14:47
  • @Zomgrei Moving an item to the top of an array is a pretty well-solved problem. But, yeah, it's a hack - and doesn't help if another emote is added that *also* causes this problem. – Paul Roub Sep 30 '15 at 14:47
  • Thanks @PaulRoub, it'll be a valid workaround for the moment, but I hope to find something more elegant to resolve it. – Zomgrei Sep 30 '15 at 14:53

1 Answers1

0

Because the Regex scans the whole string over again I don't think the Regex.Replace solution is most fit here.

Here is an alternative; posted as method, better suggestions are welcome :)

public string ReplaceMethod(string input, Dictionary<string,string> dict1 )
{
    if (dict1.Any(c => string.IsNullOrEmpty(c.Key)))
                throw new ArgumentException("dictionary may not contain empty key's");

    StringBuilder output = new StringBuilder();

    //loop the string's characters
    for (int c = 0; c < input.Length; c++)
    {
       bool found = false;
       //order by length desc to ensure longest possible match is checked first
       foreach (KeyValuePair<string, string> item in
                                      dict1.OrderByDescending(x => x.Key.Length))
       {
           if (input.Substring(c).StartsWith(item.Key))
           {
               //match found
               found = true;
               //skip length of the key
               c+=item.Key.Length - 1;
               output.Append(item.Value);
               break;
            }
        }

        if (!found)
            output.Append(input[c]);
    }

    return output.ToString();
}

As Console app:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication6
{
    class Program
    {   
        public static string ReplaceMethod(string input, //... as above
                                             Dictionary<string,string> dict1) 
        static void Main(string[] args)
        {
            var dict1 = new Dictionary<string, string>();

            dict1.Add(":-)", "***http://smile.jpg***");
            dict1.Add(":/", "***http://hmmm.jpg***");
            dict1.Add(":(", "***http://sad.jpg***");
            dict1.Add("):(", "***http://strange? :)***");

            string input = ":-) This is just a quick solution:-" +
                           "Suggestions are welcome  :/  :( :)):(";
            string output = ReplaceMethod(input, dict1);

            Console.WriteLine("input");
            Console.WriteLine(input);
            Console.WriteLine();
            Console.WriteLine("output");            
            Console.WriteLine(output);

            Console.ReadLine();
        }
    }
}

Little disclaimer: I haven't really tested it.

Stefan
  • 17,448
  • 11
  • 60
  • 79