12
votes

Traiter avec des champs contenant des guillemets doubles non évalués avec TextfieldParser

J'essaie d'importer un fichier CSV à l'aide de TextFieldParser . Un fichier CSV particulier me cause des problèmes en raison de sa mise en forme non standard. La CSV en question a ses champs enfermés dans des guillemets doubles. Le problème apparaît lorsqu'il y a un ensemble supplémentaire de citations doubles non évaluées dans un champ particulier.

Voici un cas de test simplifié qui met en évidence le problème. Les fichiers CSV réels que je traite ne sont pas tous formatés et que des dizaines de champs, qui peuvent contenir ces problèmes de formatage délicats éventuels. P>

TextReader reader = new StringReader("\"Row\",\"Test String\"\n" +
    "\"1\",\"This is a test string.  It is parsed correctly.\"\n" +
    "\"2\",\"This is a test string with a comma,  which is parsed correctly\"\n" +
    "\"3\",\"This is a test string with double \"\"double quotes\"\". It is parsed correctly\"\n" +
    "\"4\",\"This is a test string with 'single quotes'. It is parsed correctly\"\n" +
    "5,This is a test string with fields that aren't enclosed in double quotes.  It is parsed correctly.\n" +
    "\"6\",\"This is a test string with single \"double quotes\".  It can't be parsed.\"");

using (TextFieldParser parser = new TextFieldParser(reader))
{
    parser.Delimiters = new[] { "," };
    while (!parser.EndOfData)
    {
        string[] fields= parser.ReadFields();
        Console.WriteLine("This line was parsed as:\n{0},{1}",
            fields[0], fields[1]);
    }
}


2 commentaires

Il est assez important que vous n'essayez pas de le réparer. Cela vous rendra responsable des mauvaises données pendant une longue période. Rejeter le fichier pour être mal formaté. S'ils vous tracent, soulignez-vous que ce n'est pas compatible RFC-4180. Il y a un autre programmeur quelque part qui peut facilement résoudre ce problème.


@Hanspassant tandis que c'est le plan d'action idéal et «approprié», à plusieurs reprises, nous n'avons pas de choix, par exemple lors de la consommation de fichiers d'une API que nous n'avons aucun contrôle, ni un client important et que nous devons juste "faire fonctionner".


6 Réponses :


0
votes

Il peut être plus facile de le faire manuellement, et cela vous donnerait certainement plus de contrôle:

Edit: Pour votre exemple clarifié, je suggère toujours de manipuler manuellement l'analyse: p> xxx pré>

pour un CSV générique qui ne permet pas des virgules dans les champs: P>

using System.IO;

string[] csvFile = File.ReadAllLines(pathToCsv);
foreach (string line in csvFile)
{
    string[] fields = line.Split(',');
    Console.WriteLine("This line was parsed as:\n{0},{1}",
            fields[0], fields[1]);
}


3 commentaires

On dirait que j'aurais pu simplifier mon exemple au point que ce n'est pas clair pourquoi je voudrais utiliser le TextFieldParser. Une division simple sur une virgule finira par introduire une variété de problèmes différents qui ne sont pas présents lors de l'utilisation du TextFieldParser. L'exemple principal est l'existence d'une virgule dans une valeur de texte spécifique. Je mettrai à jour la question avec une chaîne de test plus complexe pour mettre en évidence les avantages de l'utilisation du TextFieldParser.


@sglantz: échantillon de code mis à jour. Je suggère toujours que vous obtiendrez un contrôle maximal pour le faire manuellement. J'ai trouvé le problème avec le CSV et d'autres analyseurs est qu'il est facile d'avoir des données qui ne fonctionnent pas avec eux même lorsqu'elles sont programmées pour être assez génériques. Je pense que le nouvel échantillon de code est plus facile à lire, mais peut être accompli également en utilisant des expressions régulières C # et la classe de match.


Il semble toujours que l'exemple ne transmet toujours pas la complexité des fichiers CSV que je traite. Ils ne sont pas systématiquement formatés. Les virgules et les citations peuvent apparaître dans l'un des 20 champs et plus dans le fichier. TextfieldParser est bon pour la manipulation de cette incohérence lors de la division manuelle et même de la regex deviennent très complexes très rapidement lorsqu'il s'agit de la variété de formats différents.



-1
votes

Veuillez définir hasfieldencloseInquotes = true sur l'objet TextFieldParser avant de commencer à lire le fichier.


1 commentaires

Cette option peut être activée et que le texte de l'espace ne sera toujours pas en mesure d'analyser le texte. Le problème n'est pas que les champs sont enfermés dans des guillemets, mais dans les champs, il existe des citations qui ne sont pas correctement échappées en utilisant deux guillemets.



0
votes

Solution de travail:

using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
            {
                csvReader.SetDelimiters(new string[] { "," });
                csvReader.HasFieldsEnclosedInQuotes = false;
                string[] colFields = csvReader.ReadFields();

                while (!csvReader.EndOfData)
                {
                    string[] fieldData = csvReader.ReadFields();
                    for (i = 0; i < fieldData.Length; i++)
                    {
                        if (fieldData[i] == "")
                        {
                            fieldData[i] = null;
                        }
                        else
                        {
                            if (fieldData[i][0] == '"' && fieldData[i][fieldData[i].Length - 1] == '"')
                            {
                                fieldData[i] = fieldData[i].Substring(1, fieldData[i].Length - 2);
                            }
                        }
                    }
                    csvData.Rows.Add(fieldData);
                   }
            }


1 commentaires

Pour clarifier, les virgules à l'intérieur de la chaîne de test n ° 2 font que les champs soient fragés.



7
votes

Je suis d'accord avec les conseils de Hans Passant qu'il n'est pas de votre responsabilité d'analyser les données malformées. Cependant, conformément au Principe de robustesse , une personne confrontée à cette situation peut tenter de gérer des types spécifiques des données mal formées. Le code que j'ai écrit ci-dessous fonctionne sur le jeu de données spécifié dans la question. Fondamentalement, il détecte l'erreur d'analyse sur la ligne mal formée, détermine s'il s'agit d'une double citation enveloppée en fonction du premier caractère, puis se divise / des bandes toutes les citations de l'emballage manuellement.

using (TextFieldParser parser = new TextFieldParser(reader))
{
    parser.Delimiters = new[] { "," };

    while (!parser.EndOfData)
    {
        string[] fields = null;
        try
        {
            fields = parser.ReadFields();
        }
        catch (MalformedLineException ex)
        {
            if (parser.ErrorLine.StartsWith("\""))
            {
                var line = parser.ErrorLine.Substring(1, parser.ErrorLine.Length - 2);
                fields = line.Split(new string[] { "\",\"" }, StringSplitOptions.None);
            }
            else
            {
                throw;
            }
        }
        Console.WriteLine("This line was parsed as:\n{0},{1}", fields[0], fields[1]);
    }
}


2 commentaires

Considérant que cela a été presque exactement de deux ans que j'ai posté cette question, je ne suis pas sûr que cela aurait résolu mon problème initial. J'ai fini par aller avec la suggestion de Hans et demanda un fichier plus proche de la spécification. Puisque cela résout mes cas d'exemple et je n'ai jamais accepté de réponse, je vais aller de l'avant et accepter votre réponse. Merci, vous m'avez sauvé de devenir un autre Denvercoder9 - xkcd.com/979


Ah, le XKCD obligatoire :) Ouais, je savais que j'ouvrais une vieille question, mais j'avais un problème très similaire à la vôtre, et quand j'ai trouvé une solution, je pensais que ce serait bien de le partager.



0
votes

Si vous ne définissez pas hasfieldenclosedinquotes = true la liste résultante des colonnes sera davantage si les données contiennent (,) virgule. par exemple "Col1", "col2", "col3" "Test1", 100, "Test1, test2" "Test2", 200, "Test22" Ce fichier devrait avoir 3 colonnes mais tout en analysant que vous obtiendrez 4 champs qui ne va pas.


0 commentaires

1
votes

La solution de Jordan est assez bonne, mais elle fait une hypothèse incorrecte que la ligne d'erreur commencera toujours par une double citation. Ma ligne d'erreur était la suivante:

using(TextFieldParser parser = new TextFieldParser(new StringReader(csv))) {
 parser.Delimiters = new [] {","};

 while (!parser.EndOfData) {
  string[] fields = null;
  try {
   fields = parser.ReadFields();
  } catch (MalformedLineException ex) {
   string errorLine = SafeTrim(parser.ErrorLine);
   fields = errorLine.Split(',');
  }
 }
}


0 commentaires