Recherche de Regex pour trouver des nouvelles lignes entre guillemets dans une grande chaîne (pour C#)

StackOverflow https://stackoverflow.com/questions/33063

  •  09-06-2019
  •  | 
  •  

Question

J'ai une grosse chaîne (appelons-la un fichier CSV, même si ce n'en est pas réellement un, ce sera juste plus facile pour le moment) que je dois analyser en code C#.
La première étape du processus d'analyse divise le fichier en lignes individuelles en utilisant simplement un StreamReader objet et appel ReadLine jusqu'à ce que ce soit à travers le fichier.Cependant, n'importe quelle ligne donnée peut contenir un littéral entre guillemets simples avec des nouvelles lignes intégrées.Je dois trouver ces nouvelles lignes et les convertir temporairement en un autre type de jeton ou de séquence d'échappement jusqu'à ce que j'aie divisé le fichier en un tableau de lignes... puis je peux les modifier.

Exemples de données d'entrée :

1,2,10,99,'Some text without a newline', true, false, 90
2,1,11,98,'This text has an embedded newline 
                and continues here', true, true, 90

Je pourrais écrire tout le code C# nécessaire pour ce faire en utilisant string.IndexOf pour trouver les sections citées et y rechercher des nouvelles lignes, mais je pense qu'une Regex pourrait être un meilleur choix (c'est-à-dire maintenant j'ai deux problèmes)

Était-ce utile?

La solution

Puisqu'il ne s'agit pas d'un véritable fichier CSV, a-t-il une sorte de schéma ?

D'après votre exemple, il semble que vous ayez :int, int, int, int, chaîne, bool, bool, int

Avec cela constituant votre enregistrement/objet.

En supposant que vos données sont bien formées (je ne connais pas suffisamment votre source pour savoir dans quelle mesure cette hypothèse est valable) ;vous pourriez:

  1. Lisez votre ligne.
  2. Utilisez une machine à états pour analyser vos données.
  3. Si votre ligne se termine et que vous analysez une chaîne, lisez la ligne suivante... et continuez l'analyse.

J'éviterais d'utiliser une regex si possible.

Autres conseils

Les machines à états permettant d'effectuer un tel travail sont simplifiées à l'aide des itérateurs C# 2.0.Voici, espérons-le, le dernier analyseur CSV que j'écrirai.L'ensemble du fichier est traité comme un groupe énumérable de chaînes énumérables, c'est-à-direrangées colonnes.IEnumerable est génial car il peut ensuite être traité par les opérateurs LINQ.

public class CsvParser
{
    public char FieldDelimiter { get; set; }

    public CsvParser()
        : this(',')
    {
    }

    public CsvParser(char fieldDelimiter)
    {
        FieldDelimiter = fieldDelimiter;
    }

    public IEnumerable<IEnumerable<string>> Parse(string text)
    {
        return Parse(new StringReader(text));
    }
    public IEnumerable<IEnumerable<string>> Parse(TextReader reader)
    {
        while (reader.Peek() != -1)
            yield return parseLine(reader);
    }

    IEnumerable<string> parseLine(TextReader reader)
    {
        bool insideQuotes = false;
        StringBuilder item = new StringBuilder();

        while (reader.Peek() != -1)
        {
            char ch = (char)reader.Read();
            char? nextCh = reader.Peek() > -1 ? (char)reader.Peek() : (char?)null;

            if (!insideQuotes && ch == FieldDelimiter)
            {
                yield return item.ToString();
                item.Length = 0;
            }
            else if (!insideQuotes && ch == '\r' && nextCh == '\n') //CRLF
            {
                reader.Read(); // skip LF
                break;
            }
            else if (!insideQuotes && ch == '\n') //LF for *nix-style line endings
                break;
            else if (ch == '"' && nextCh == '"') // escaped quotes ""
            {
                item.Append('"');
                reader.Read(); // skip next "
            }
            else if (ch == '"')
                insideQuotes = !insideQuotes;
            else
                item.Append(ch);
        }
        // last one
        yield return item.ToString();
    }

}

Notez que le fichier est lu caractère par caractère, le code décidant quand les nouvelles lignes doivent être traitées comme des délimiteurs de lignes ou comme une partie d'une chaîne entre guillemets.

Et si vous mettiez le fichier entier dans une variable, puis le divisiez en fonction de nouvelles lignes non citées ?

MODIFIER: Désolé, j'ai mal interprété votre message.Si vous recherchez une regex, en voici une :

content = Regex.Replace(content, "'([^']*)\n([^']*)'", "'\1TOKEN\2'");

Il peut y avoir des cas extrêmes et ces deux problèmes, mais je pense que ça devrait aller la plupart du temps.Ce que fait l'expression régulière, c'est qu'elle trouve d'abord toute paire de guillemets simples qui ont entre eux et remplace ce par TOKEN en préservant tout texte intermédiaire.

Mais quand même, j'irais vers une machine à états comme ce que @bryansh a expliqué ci-dessous.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top