number money c# string-formatting

c# - money - string.format java



¿Cómo eliminar algunas palabras especiales de un contenido de cadena? (9)

Tengo algunas cadenas que contienen código para iconos emoji, como :grinning: , :kissing_heart: o :bouquet: Me gustaría procesarlos para eliminar los códigos emoji.

Por ejemplo, dado:

Hola: sonriendo: cómo estás?: Kissing_heart: ¿Estás bien?: Bouquet:

Quiero conseguir esto:

¿Hola como estas? ¿Estás bien?

Sé que puedo usar este código:

richTextBox2.Text = richTextBox1.Text.Replace(":kissing_heart:", "").Replace(":bouquet:", "").Replace(":grinning:", "").ToString();

Sin embargo, hay 856 iconos emoji diferentes que debo eliminar (que, con este método, recibirían 856 llamadas a Replace() ). ¿Hay alguna otra manera de lograr esto?


Dividiría el texto con el '':'' y luego crearía la cadena excluyendo los nombres de emoji encontrados.

const char marker = '':''; var textSections = text.Split(marker); var emojiRemovedText = string.Empty; var notMatchedCount = 0; textSections.ToList().ForEach(section => { if (emojiNames.Contains(section)) { notMatchedCount = 0; } else { if (notMatchedCount++ > 0) { emojiRemovedText += marker.ToString(); } emojiRemovedText += section; } });


Finalmente llegó a escribir algo. Estoy combinando un par de ideas mencionadas anteriormente, con el hecho de que solo deberíamos recorrer la cadena una vez. Basado en esos requisitos, este es el trabajo perfecto para Linq .

Probablemente debería HashSet caché el HashSet . Aparte de eso, esto tiene un rendimiento O (n) y solo repasa la lista una vez. Sería interesante realizar una evaluación comparativa, pero esta podría ser la solución más eficiente.

El enfoque es bastante directo hacia adelante.

  • Primero cargue todos los Emoij en un HashSet para que podamos buscarlos rápidamente.
  • Divida la cadena con input.Split('':'') en la :
  • Decidir si mantenemos el elemento actual.
    • Si el último elemento era una coincidencia, mantenga el elemento actual.
    • Si el último elemento no coincide, verifique si el elemento actual coincide.
      • Si lo hace, ignóralo. (Esto elimina efectivamente la subcadena de la salida).
      • Si no es así, agregue : retroceda y guárdelo.
  • Reconstruye nuestra cadena con un StringBuilder .

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { static class Program { static void Main(string[] args) { ISet<string> emojiList = new HashSet<string>(new[] { "kissing_heart", "bouquet", "grinning" }); Console.WriteLine("Hello:grinning: , ho:w: a::re you?:kissing_heart:kissing_heart: Are you fine?:bouquet:".RemoveEmoji('':'', emojiList)); Console.ReadLine(); } public static string RemoveEmoji(this string input, char delimiter, ISet<string> emojiList) { StringBuilder sb = new StringBuilder(); input.Split(delimiter).Aggregate(true, (prev, curr) => { if (prev) { sb.Append(curr); return false; } if (emojiList.Contains(curr)) { return true; } sb.Append(delimiter); sb.Append(curr); return false; }); return sb.ToString(); } } }

Edición: hice algo genial con la biblioteca de Rx , pero luego me di cuenta de que Aggregate es la contraparte de IEnumerable de Scan in Rx, lo que simplifica aún más el código.


No tienes que reemplazar todos los 856 emojis. Solo tienes que reemplazar los que aparecen en la cadena. Así que echa un vistazo a:

Encontrar una subcadena usando C # con un giro

Básicamente, se extraen todos los tokens, es decir, las cadenas entre: y: y luego se reemplazan con string.Empty ()

Si le preocupa que la búsqueda devuelva cadenas que no sean emojis como: algún otro texto: entonces podría hacer una búsqueda de tabla hash para asegurarse de que reemplazar dicho token encontrado sea apropiado.


Puedes usar Regex para unir la palabra entre :anything: Usando Replace con función puedes hacer otra validación.

string pattern = @":(.*?):"; string input = "Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet: Are you super fan, for example. :words not to replace:"; string output = Regex.Replace(input, pattern, (m) => { if (m.ToString().Split('' '').Count() > 1) // more than 1 word and other validations that will help preventing parsing the user text { return m.ToString(); } return String.Empty; }); // "Hello , how are you? Are you fine? Are you super fan, for example. :words not to replace:"

Si no desea utilizar Replace que haga uso de una expresión lambda, puede usar /w , como se menciona @ yorye-nathan, para hacer coincidir solo las palabras.

string pattern = @":(/w*):"; string input = "Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet: Are you super fan, for example. :words not to replace:"; string output = Regex.Replace(input, pattern, String.Empty); // "Hello , how are you? Are you fine? Are you super fan, for example. :words not to replace:"


Si la eficiencia es una preocupación y para evitar el procesamiento de "falsos positivos", considere reescribir la cadena usando un StringBuilder mientras omite los tokens de emoji especiales:

static HashSet<string> emojis = new HashSet<string>() { "grinning", "kissing_heart", "bouquet" }; static string RemoveEmojis(string input) { StringBuilder sb = new StringBuilder(); int length = input.Length; int startIndex = 0; int colonIndex = input.IndexOf('':''); while (colonIndex >= 0 && startIndex < length) { //Keep normal text int substringLength = colonIndex - startIndex; if (substringLength > 0) sb.Append(input.Substring(startIndex, substringLength)); //Advance the feed and get the next colon startIndex = colonIndex + 1; colonIndex = input.IndexOf('':'', startIndex); if (colonIndex < 0) //No more colons, so no more emojis { //Don''t forget that first colon we found sb.Append('':''); //Add the rest of the text sb.Append(input.Substring(startIndex)); break; } else //Possible emoji, let''s check { string token = input.Substring(startIndex, colonIndex - startIndex); if (emojis.Contains(token)) //It''s a match, so we skip this text { //Advance the feed startIndex = colonIndex + 1; colonIndex = input.IndexOf('':'', startIndex); } else //No match, so we keep the normal text { //Don''t forget the colon sb.Append('':''); //Instead of doing another substring next loop, let''s just use the one we already have sb.Append(token); startIndex = colonIndex; } } } return sb.ToString(); } static void Main(string[] args) { List<string> inputs = new List<string>() { "Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet:", "Tricky test:123:grinning:", "Hello:grinning: :imadethis:, how are you?:kissing_heart: Are you fine?:bouquet: This is:a:strange:thing :to type:, but valid :nonetheless:" }; foreach (string input in inputs) { Console.WriteLine("In <- " + input); Console.WriteLine("Out -> " + RemoveEmojis(input)); Console.WriteLine(); } Console.WriteLine("/r/n/r/nPress enter to exit..."); Console.ReadLine(); }

Salidas:

In <- Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet: Out -> Hello , how are you? Are you fine? In <- Tricky test:123:grinning: Out -> Tricky test:123 In <- Hello:grinning: :imadethis:, how are you?:kissing_heart: Are you fine?:bouquet: This is:a:strange:thing :to type:, but valid :nonetheless: Out -> Hello :imadethis:, how are you? Are you fine? This is:a:strange:thing :to type:, but valid :nonetheless:


Usaría un método de extensión como este:

public static class Helper { public static string MyReplace(this string dirty, char separator) { string newText = ""; bool replace = false; for (int i = 0; i < dirty.Length; i++) { if(dirty[i] == separator) { replace = !replace ; continue;} if(replace ) continue; newText += dirty[i]; } return newText; }

}

Uso:

richTextBox2.Text = richTextBox2.Text.MyReplace ('':'');

Este método muestra que es mejor en términos de rendimiento en comparación con uno con Regex


Use este código que coloco a continuación. Creo que al usar esta función, su problema se resolverá.

string s = "Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet:"; string rmv = ""; string remove = ""; int i = 0; int k = 0; A: rmv = ""; for (i = k; i < s.Length; i++) { if (Convert.ToString(s[i]) == ":") { for (int j = i + 1; j < s.Length; j++) { if (Convert.ToString(s[j]) != ":") { rmv += s[j]; } else { remove += rmv + ","; i = j; k = j + 1; goto A; } } } } string[] str = remove.Split('',''); for (int x = 0; x < str.Length-1; x++) { s = s.Replace(Convert.ToString(":" + str[x] + ":"), ""); } Console.WriteLine(s); Console.ReadKey();


Yo usaría una combinación de algunas de las técnicas ya sugeridas. En primer lugar, almacenaría las más de 800 cadenas de emojis en una base de datos y luego las cargué en tiempo de ejecución. Use un HashSet para almacenar estos en la memoria, de modo que tengamos un tiempo de búsqueda O (1) (muy rápido). Utilice Regex para extraer todas las posibles coincidencias de patrones de la entrada y luego compare cada uno con nuestro emoji hash, eliminando los válidos y dejando cualquier patrón no emoji en el que el usuario haya ingresado ...

public class Program { //hashset for in memory representation of emoji, //lookups are O(1), so very fast private HashSet<string> _emoji = null; public Program(IEnumerable<string> emojiFromDb) { //load emoji from datastore (db/file,etc) //into memory at startup _emoji = new HashSet<string>(emojiFromDb); } public string RemoveEmoji(string input) { //pattern to search for string pattern = @":(/w*):"; string output = input; //use regex to find all potential patterns in the input MatchCollection matches = Regex.Matches(input, pattern); //only do this if we actually find the //pattern in the input string... if (matches.Count > 0) { //refine this to a distinct list of unique patterns IEnumerable<string> distinct = matches.Cast<Match>().Select(m => m.Value).Distinct(); //then check each one against the hashset, only removing //registered emoji. This allows non-emoji versions //of the pattern to survive... foreach (string match in distinct) if (_emoji.Contains(match)) output = output.Replace(match, string.Empty); } return output; } } public class MainClass { static void Main(string[] args) { var program = new Program(new string[] { ":grinning:", ":kissing_heart:", ":bouquet:" }); string output = program.RemoveEmoji("Hello:grinning: :imadethis:, how are you?:kissing_heart: Are you fine?:bouquet: This is:a:strange:thing :to type:, but valid :nonetheless:"); Console.WriteLine(output); } }

Lo que resulta en:

Hola: imadethis: ¿cómo estás? ¿Estás bien? Esto es: a: extraño: cosa: escribir:, pero válido: no obstante:


string Text = "Hello:grinning: , how are you?:kissing_heart: Are you fine?:bouquet:";

Yo lo resolvería de esa manera

List<string> Emoj = new List<string>() { ":kissing_heart:", ":bouquet:", ":grinning:" }; Emoj.ForEach(x => Text = Text.Replace(x, string.Empty));

ACTUALIZACIÓN - refiriéndose al comentario de detalle

Otro enfoque: reemplazar solo Emojs existentes

List<string> Emoj = new List<string>() { ":kissing_heart:", ":bouquet:", ":grinning:" }; var Matches = Regex.Matches(Text, @":(/w*):").Cast<Match>().Select(x => x.Value); Emoj.Intersect(Matches).ToList().ForEach(x => Text = Text.Replace(x, string.Empty));

Pero no estoy seguro de si es esa gran diferencia para cadenas de chat tan cortas y es más importante tener un código que sea fácil de leer / mantener. La pregunta de OP fue sobre la reducción de la redundancia Text.Replace().Text.Replace() y no sobre la solución más eficiente.