12
votes

C # - fractionnement sur un tuyau avec un tuyau évasé dans les données?

J'ai un fichier délimité de tuyau que je voudrais diviser (j'utilise C #). Par exemple: xxx

Cependant, certaines des données peuvent contenir un tuyau dedans. Si tel est le cas, il sera échappé avec une barre oblique inverse: xxx

Je me demande s'il y a une réégycle ou une autre méthode pour diviser cette séparation sur le "pur" Tuyaux (c'est-à-dire des tuyaux qui n'ont pas de barre oblique inverse devant eux). Ma méthode actuelle consiste à remplacer les tuyaux évasés avec un bit de texte personnalisé, divisé sur des tuyaux, puis remplacez mon texte personnalisé avec un tuyau. Pas très élégant et je ne peux pas m'empêcher de penser qu'il y a une meilleure façon. Merci pour toute aide.


2 commentaires

Avez-vous vu thread this (monstre) . Pas une réponse directe, mais j'espère une poussée dans la bonne direction.


Et si vous voulez une barre oblique inverse littérale à la fin de l'une des pièces?


6 Réponses :


9
votes

Il suffit d'utiliser string.indexof () pour trouver le tuyau suivant. Si le caractère précédent n'est pas une barre oblique inverse, utilisez ensuite string.Substring () pour extraire le mot. Vous pouvez également utiliser string.indexofany () pour trouver la prochaine occurrence de la tuyau ou de la barre oblique inverse.

Je fais beaucoup d'analgésique comme ça, et c'est vraiment assez simple. Prenant mon approche, si cela est fait correctement aura également tendance à courir plus vite.

edit

en fait, peut-être quelque chose comme ça. Il serait intéressant de voir comment cela se compare à la performance à une solution de regex. xxx


7 commentaires

Oui, c'est mieux, analysant chaîne à votre manière que d'utiliser regex . Cela fonctionne plus vite. +1


Si vous n'ajoutez pas les mots à une liste et renvoyez-la, la méthode manuelle analyse entre environ 5 fois plus rapide que la méthode de la regex. Si vous ajoutez la surcharge de la gestion d'une liste , il est environ 3 fois plus rapide, sur ma machine quand même.


Consultez ma mise à jour ... J'ai changé mon test et j'ai obtenu la mise en œuvre des regex à environ 1,6 fois plus lent, mais vous gagnez toujours!


Je pense que cela a un problème si le dernier "mot" est vide / vide. J'ai un fichier avec 37 noms de colonne d'en-tête, mais le dernier élément de chaque ligne est vide, de sorte que les lignes se terminent par le tuyau "|" mais pas d'espace vide supplémentaire; les mots dans ce cas ne représentent que 36


Je pense que cela peut également rencontrer des problèmes lorsqu'il y a une barre oblique inverse à la fin d'un champ .. comme "Data \\ | plus de données |" .. Traiter avec ce mal de tête des données client>.


@Adam: Il y a huit ans, mais on pourrait affirmer que si un champ est vide, cela ne devrait pas être compté. Donc, je dis simplement que la manière dont cette affaire est traitée dépend des exigences. Ne devrait pas être difficile de modifier le code pour le gérer différemment. (Et je serais heureux de le personnaliser à différentes exigences, mais ce serait un consultant rémunéré.


Merci @jonathanwood :) J'ai été capable de le modifier légèrement pour répondre à mes besoins - très appréciés!



5
votes

Ceci devrait le faire:

string test = @"This|is|a|pip\|ed|test (this is a pip|ed test)";
string[] parts = Regex.Split(test, @"(?<!(?<!\\)*\\)\|");


2 commentaires

En supposant que les backslashes puissent également être échappées (par exemple "ceci | est | A | Pip \\ | ed | test (ceci est un test PIP | ED)" ), cela ne fonctionne pas. Vous devrez utiliser l'intégralité de la poste mentionnée.


@ Vous avez raison. C'est la première chose que j'ai pensée quand j'ai décidé d'écrire du code à ce sujet :)



0
votes

La solution de Cory est plutôt bonne. Mais, je préfère ne pas travailler avec regex, alors vous pouvez simplement faire quelque chose à la recherche de "\ |" et le remplacer par un autre caractère, puis faites votre scission, puis remplacez-la à nouveau avec le "\ |".

Une autre option est de faire la scission, puis d'examiner toutes les chaînes et si le dernier caractère est un \, puis rejoignez-le avec la chaîne suivante.

Bien sûr, tout cela ignore ce qui se passe si vous avez besoin d'une glash en arrière évasée avant un tuyau ... comme "\\ |".

Dans l'ensemble, je me penche vers la regex.

Franchement, je préfère utiliser FichiersHelPers car, même si ce n'est pas une virgule délimiée, c'est fondamentalement le même chose. Et ils ont une excellente histoire sur Pourquoi vous ne devriez pas écrire cela vous-même .


0 commentaires

1
votes

Voici une autre solution.

L'une des plus belles choses à propos de la programmation est la différentes façons de donner une solution au même problème: xxx


4 commentaires

C'est assez slick, mais pas un très bon choix si vous craigniez des performances.


@Jonathan comme je l'ai dit, c'est juste une autre façon de le faire. Il n'a pas de sens de coller un code similaire à celui que vous avez fourni. Je suis d'accord avec vous, bien que la performance ne soit peut-être pas quelque chose de vraiment important dans ce problème.


Je ne vous critiquiez pas de la publier. En fait, j'ai mentionné que c'était slick. Je commençais juste sur la performance de cette approche.


Je pense que celui-ci et @ jonathan est à la fois assez slick, donc +1 aux deux.



0
votes

Vous pouvez le faire avec une regex. Une fois que vous avez décidé d'utiliser une barre oblique inverse comme votre caractère d'échappement, vous avez deux cas d'échappement pour prendre en compte:

  • échapper à un tuyau: \ | code> li>
  • échapper à une barre oblique inverse que vous voulez interpréter littéralement. li> ul>

    Les deux peuvent être effectués dans la même regex. Les backslashes évasés seront toujours deux caractères \ code> ensemble. Les backslashes évasés consécutives seront toujours des nombres même des caractères \ code>. Si vous trouvez une séquence impair numérotée de \ code> avant un tuyau, cela signifie que plusieurs backs-backs évasés, suivis d'un tuyau évasé. Donc, vous voulez utiliser quelque chose comme ceci: p> xxx pré>

    déroutant, peut-être, mais cela devrait fonctionner. Explication: P>

    ^              #The start of a line
    (?:...
        [^|\\]     #A character other than | or \ OR
        (?:\\{2})* #An even number of \ characters OR
        \\\|       #A literal \ followed by a literal |
    ...)+          #Repeat the preceding at least once
    (?:$|\|)       #Either a literal | or the end of a line
    


6 commentaires

@Justin Pour une raison quelconque, cela ne fonctionne pas sur mon ordinateur. En outre, un ) est manquant.


@Ocar - Il y avait tellement de parenthèses imbriquées, il était difficile de garder une piste. Essayez-le maintenant.


@Justin maintenant cela fonctionne, bien que cela se produise la même chose avec @Cory Solution: A \\ | B devrait devenir A \ | B au lieu d'un \\ et B . Le premier \\ est un personnage comme n'importe quel autre, et le second s'échappe le | , de sorte que la seconde sera supprimée et la phrase restera telle qu'elle est.


@Oscar - Si vous entrez A \\ | B , vous avez échappé au caractère de barre oblique intégré lui-même, il devrait donc être interprété comme a` plus B . Pour obtenir A \ | B , vous entrez A \\\ | B . C'est ainsi que je m'attendrais à ce que cela fonctionne, moi-même et c'est cohérent avec la plupart des schémas de secours que j'ai vus. En C #, par exemple, la chaîne \\\ n` serait un "` "et un retour de chariot.


@Justin cela dépend de la façon dont vous le prenez. Lorsque quelqu'un vous dit: Je veux analyser la chaîne ABC \ DE , vous devez supposer que \ est déjà échappé. Sinon, l'exemple original n'a pas de sens, car c # lui-même donnera une erreur si vous écrivez "\ |" Parce que tu n'essaçons rien ici. Pour reprendre, ce que je pense, c'est que la chaîne à analyser est littérale (déjà échappée).


@Oscar - Je vois ce que vous obtenez. D'autre part, si vous ne le faites pas de cette façon, il n'y aurait aucun moyen d'avoir une entrée se terminant dans une barre oblique inverse. Si vous vouliez "A \" et "B", ni a \ | b ni a \\ | b fonctionneraient. Déclarant \ En tant que caractère d'échappement, oblige l'utilisateur à s'échapper tout au long du texte, mais il autorise toutes les entrées possibles. Cela pourrait même ne pas être valable pour la situation du questionneur, mais j'ai décidé d'aller avec l'option la moins restrictive. BTW, on dirait que nous avons tous deux couru à la fois des règles d'échappement de pile de pile.



2
votes

Je suis tombé sur un scénario similaire, pour moi, le nombre de tuyaux a été corrigé (pas de tuyaux avec "\ |"). C'est comme ça que j'ai géré.

string sPipeSplit = "This|is|a|pip\\|ed|test (this is a pip|ed test)";
string sTempString = sPipeSplit.Replace("\\|", "¬"); //replace \| with non printable character
string[] sSplitString = sTempString.Split('|');
//string sFirstString = sSplitString[0].Replace("¬", "\\|"); //If you have fixed number of fields and you are copying to other field use replace while copying to other field.
/* Or you could use a loop to replace everything at once
foreach (string si in sSplitString)
{
    si.Replace("¬", "\\|");
}
*/


0 commentaires