2009-05-04 4 views
11

J'ai une chaîne lue d'une autre source telle que "\ b \ bfoo \ bx". Dans ce cas, il se traduirait par le mot "fox" car les 2 premiers \ b sont ignorés, et le dernier "o" est effacé, puis remplacé par "x". Un autre cas serait "patt \ b \ b \ b \ b \ b \ b \ b \ b \ bfoo" devrait être traduit en "foo"Existe-t-il un meilleur moyen que String.Replace pour supprimer les backspaces d'une chaîne?

J'ai trouvé quelque chose en utilisant String.Replace, mais c'est complexe et je m'inquiète qu'il ne fonctionne pas correctement, il crée aussi beaucoup de nouveaux objets string que je voudrais éviter.

Des idées?

+1

Avez-vous envisagé d'utiliser une expression régulière? – Jagd

+0

@Jagd Quelle regex recommanderiez-vous?Je suis à la recherche d'une solution plus élégante. J'utilise spécifiquement une combinaison de Bash et de Ruby pour écrire un script dans mon éditeur de texte. – Brandon

Répondre

12

Probablement le plus simple est de simplement itérer sur toute la chaîne. Compte tenu de vos entrées, le code suivant fait le tour en 1 passe

public string ReplaceBackspace(string hasBackspace) 
{ 
    if(string.IsNullOrEmpty(hasBackspace)) 
     return hasBackspace; 

    StringBuilder result = new StringBuilder(hasBackspace.Length); 
    foreach (char c in hasBackspace) 
    { 
     if (c == '\b') 
     { 
      if (result.Length > 0) 
       result.Length--; 
     } 
     else 
     { 
      result.Append(c); 
     } 
    } 
    return result.ToString(); 
} 
+1

Simple. Simple. Facile à comprendre. –

+0

Je ne savais pas à propos de la longueur-- tour, c'est propre. J'étais inquiet à propos de sb.Remove() étant cher. – esac

+0

Length-- est intelligent. – mquander

-1

Créez un StringBuilder et copiez tout sauf les caractères de retour arrière.

+0

Je dois aussi enlever les caractères de la chaîne si et seulement s'il y a un retour arrière correspondant, pas seulement le caractères de retour arrière. – esac

2

Vous pouvez parcourir la chaîne vers l'arrière pour créer un tableau de caractères au fur et à mesure. Chaque fois que vous frappez un retour arrière, incrémentez un compteur, et chaque fois que vous frappez un caractère normal, ignorez-le si votre compteur est différent de zéro et décrémentez le compteur. Je ne suis pas sûr de ce que la meilleure structure de données C# est de gérer cela et ensuite être en mesure d'obtenir la chaîne dans le bon ordre par la suite rapidement. StringBuilder a une méthode Insert mais je ne sais pas s'il sera performant de continuer à insérer des caractères au début ou non. Vous pourriez mettre les personnages dans une pile et frapper ToArray() à la fin - cela pourrait ou non être plus rapide.

6

La façon dont je le ferais est low-tech, mais facile à comprendre.

Créer une pile de caractères. Puis parcourez la chaîne du début à la fin. Si le caractère est un caractère normal (non-barre oblique), poussez-le sur la pile. Si c'est une barre oblique et que le prochain caractère est un 'b', placez le haut de la pile. Si la pile est vide, ignorez-la. A la fin, faites apparaître chaque caractère à tour de rôle, ajoutez-le à un StringBuilder et inversez le résultat.

+0

Ceci est plus propre que ma méthode. +1 – mquander

+0

C'est sympa, même si je voulais dire le caractère d'échappement littéral '\ b', donc je n'aurais pas besoin de faire la comparaison du prochain caractère étant un 'b', mais cela fonctionne toujours. En regardant cette méthode, le seul «problème» que j'ai avec elle est que je dois faire un Array.Reverse à la fin de la méthode .. pas une opération coûteuse, mais je voudrais pouvoir le faire sans avoir à inverser :) – esac

+0

Vous pouvez les sortir de la pile dans un tableau de caractères dans l'ordre inverse; c'est-à-dire char [] letters = new char [stack.Count]; pour (int i = pile.Count - 1; i> = 0; i--) lettres [i] = pile.Pop(); chaîne résultat = nouvelle chaîne (lettres); – mquander

0
String myString = "patt\b\b\b\b\b\b\b\b\b\bfoo"; 
     List<char> chars = myString.ToCharArray().ToList(); 
     int delCount = 0; 

     for (int i = chars.Count -1; i >= 0; i--) 
     { 
     if (chars[i] == '\b') 
     { 
      delCount++; 
      chars.RemoveAt(i); 
     } else { 
      if (delCount > 0 && chars[i] != null) { 
      chars.RemoveAt(i); 
      delCount--; 
      } 
     } 
     } 
0

j'irais comme ceci: Code est pas testé

char[] result = new char[input.Length()]; 
int r =0; 
for (i=0; i<input.Length(); i++){ 
if (input[i] == '\b' && r>0) r--; 
else result[r]=input[i]; 

} 

string resultsring = result.take(r); 
3

version expressions régulières:

var data = @"patt\b\b\b\b\b\b\b\b\b\bfoo"; 
var regex = new Regex(@"(^|[^\\b])\\b"); 

while (regex.IsMatch(data)) 
{ 
    data = regex.Replace(data, ""); 
} 

Version optimisée (et ce on travaille avec backspace '\ b' et non avec la chaîne "\ b"):

var data = "patt\b\b\b\b\b\b\b\b\b\bfoo"; 
var regex = new Regex(@"[^\x08]\x08", RegexOptions.Compiled); 

while (data.Contains('\b')) 
{ 
    data = regex.Replace(data.TrimStart('\b'), ""); 
} 
3
public static string ProcessBackspaces(string source) 
{ 
    char[] buffer = new char[source.Length]; 
    int idx = 0; 

    foreach (char c in source) 
    { 
     if (c != '\b') 
     { 
      buffer[idx] = c; 
      idx++; 
     } 
     else if (idx > 0) 
     { 
      idx--; 
     } 
    } 

    return new string(buffer, 0, idx); 
} 

EDIT

Je l'ai fait un rapide, rugueux référence du code affiché dans les réponses jusqu'à présent (traitement des deux exemples de chaînes de la question, un million de fois chacun):

ANSWER     | TIME (ms) 
------------------------|----------- 
Luke (this one)  |  318 
Alexander Taran  |  567 
Robert Paulson   |  683 
Markus Nigbur   |  2100 
Kamarey (new version) |  7075 
Kamarey (old version) |  30902 
+0

Votre code est rapide, mais légèrement incorrect. Il échoue pour le cas de test 'fox \ b \ b \ b \ bfor' qui devrait produire "pour" (Dieu merci pour les tests unitaires :)) parce que idx est = 0 sur le dernier \ b, donc il le met dans le char tampon. Voici la partie fixe: si (c == '\ b') { si (idx> 0) { idx--; } } sinon { buffer [idx] = c; idx ++; } – esac

+0

Juste un petit commentaire Luke, ce code a un bug de casse quand il y a un nombre impair de backspaces qui le rembobinent au début à cause de 'if ((c ==' \ b ') && (idx> 0)) 'laisse un caractère initial \ b. Lorsque idx = 0, vous ajoutez \ b à la sortie. –

+0

@esac, @Robert, bien repéré! J'ai mis à jour pour corriger ce bug. – LukeH

Questions connexes