2011-07-21 2 views
2

J'ai une chaîne dans le format suivant dans un fichier délimité par des virgules:Utilisation d'expressions régulières pour la recherche modèle avec Remplacer

someText, "Text with, delimiter", moreText, "Text Again" 

Ce que je dois faire est de créer une méthode qui va chercher dans la chaîne, et sera remplacez les virgules à l'intérieur du texte cité par un signe dollar ($).

Après la méthode, la chaîne sera:

someText, "Text with$ delimiter", moreText, "Text Again" 

Je ne suis pas tout à fait bien avec RegEx, mais je voudrais savoir comment je peux utiliser des expressions régulières pour rechercher un motif (trouver une virgule entre guillemets), puis remplacez cette virgule par le signe dollar.

+3

Cela ressemble à CSV. Est-ce juste une coïncidence? S'il s'agit d'un fichier CSV, sachez que CSV n'est pas un langage "régulier" et ne peut donc pas être analysé complètement et correctement via une expression régulière dans tous les cas. Voir les commentaires et les réponses à cette question: http://stackoverflow.com/questions/1189416/c-regular-expressions-how-to-parse-comma-separated-values-where-some-values ​​ –

+0

Si c'est juste un hack sur le chemin de 'Split (',')', vous devriez certainement utiliser un analyseur CSV. Que feriez-vous si la chaîne contenait un '$', en passant ('1,2 ', $ 5,4', 6')? – Kobi

+0

@Daniel - En fait, le CSV * valide est * une langue normale (tant que vous ne comptez pas toutes les lignes ont le même nombre de colonnes inconnues). Il ne contient aucune imbrication, ni aucun contexte à prendre en compte. – Kobi

Répondre

3

Personnellement, j'éviter regexes ici - en supposant qu'il n'y a pas de guillemets imbriqués, est assez simple d'écrire comme une boucle for, que je pense que ce sera plus efficace:

var inQuotes = false; 
var sb = new StringBuilder(someText.Length); 

for (var i = 0; i < someText.Length; ++i) 
{ 
    if (someText[i] == '"') 
    { 
     inQuotes = !inQuotes; 
    } 

    if (inQuotes && someText[i] == ',') 
    { 
     sb.Append('$'); 
    } 
    else 
    { 
     sb.Append(someText[i]); 
    } 
} 
+0

Oui, je pensais qu'en raison de l'énorme quantité de possibilités de correspondance de motifs sont grandes, que j'avais peur que les expressions rationnelles ne seraient pas une possibilité. Cependant, c'est un très bon algorithme pour passer à travers la chaîne elle-même. – 5StringRyan

+0

@Hans Gruber - C'est en fait assez facile avec une expression régulière. 'RegEx.Replace' vous permet de fournir un délégué pour faire le remplacement une fois que vous avez trouvé la correspondance, comme indiqué dans ma réponse. – Ergwun

0

Si vous souhaitez aller la route regex est ici ce que vous cherchez:

var result = Regex.Replace(text, "(\"[^,]*),([^,]*\")", "$1$$$2"); 

le problème avec regex dans ce cas est que ce ne sera pas attraper « cela, a, deux virgules ».

voir travailler à http://refiddle.com/1ab

+0

Cela ne fonctionnera pas pour: someText, "" Texte avec, délimiteur "", "" texte, virgule "", plusText, "" Text Again "", "" texte, virgule "" – eulerfx

-2

Pouvez-vous donner cette solution: "[\ w] , [\ w]" (guillemets doubles inclus)? Et soyez prudent avec le remplacement parce que le remplacement direct enlèvera la chaîne entière incluse dans les guillemets doubles.

+0

-1 Pas encore de fermeture :( – Ergwun

1

Ce type de problème est là Regex échoue, faire ceci:

var sb = new StringBuilder(str); 

    var insideQuotes = false; 

    for (var i = 0; i < sb.Length; i++) 
    { 
     switch (sb[i]) 
     { 
      case '"': 
       insideQuotes = !insideQuotes; 
       break; 
      case ',': 
       if (insideQuotes) 
        sb.Replace(',', '$', i, 1); 
       break; 
     }    
    } 

    str = sb.ToString(); 

Vous pouvez également utiliser un CSV parser pour analyser la chaîne et l'écrire à nouveau avec des colonnes remplacées.

1

Voici comment faire avec Regex.Replace:

 string output = Regex.Replace(
      input, 
      "\".*?\"", 
      m => m.ToString().Replace(',', '$')); 

Bien sûr, si vous voulez ignorer les guillemets doubles échappées cela devient plus compliqué. Surtout quand le personnage d'échappement peut lui-même être échappé.

En supposant que le caractère d'échappement est \, alors lorsque vous essayez de faire correspondre les guillemets doubles, vous ne voulez faire correspondre que les guillemets précédés d'un nombre pair de caractères d'échappement (y compris zéro). Le schéma suivant fera pour vous:

string pattern = @"(?<=((^|[^\\])(\\\\){0,}))"".*?(?<=([^\\](\\\\){0,}))""";

A ce stade, vous pouvez préférer abandonner des expressions régulières;)

MISE À JOUR:

En réponse à votre commentaire, il est facile de rendre l'opération configurable pour différents guillemets, délimiteurs et espaces réservés.

 string quote = "\""; 
     string delimiter = ","; 
     string placeholder = "$"; 

     string output = Regex.Replace(
      input, 
      quote + ".*?" + quote, 
      m => m.ToString().Replace(delimiter, placeholder)); 
+0

Hmm .... disons que je voulais permettre à l'utilisateur de spécifier le délimiteur du fichier (n'importe quoi, autre qu'une virgule), et aussi spécifier la citation Comment est-ce que je changerais cette expression Regex pour être dynamique? – 5StringRyan

+0

@ Hans Gruber - Voir ma mise à jour pour une version configurable. – Ergwun

Questions connexes