【问题标题】:How can I deal with parsing bad csv data?如何处理解析错误的 csv 数据?
【发布时间】:2016-08-29 19:55:56
【问题描述】:

我知道数据应该是正确的。我无法控制数据,我的老板只会告诉我,我需要想办法处理别人的错误。所以请不要告诉我数据不好不是我的问题,因为它是。

任何人,这就是我正在看的:

"Words","email@email.com","","4253","57574","FirstName","","LastName, MD","","","576JFJD","","1971","","Words","Address","SUITE "A"","City","State","Zip","Phone","",""

出于保密原因,数据已被清除。

如您所见,数据包含引号,并且其中一些引用字段中有逗号。所以我不能删除它们。但是“Suite A”“”正在抛弃解析器。引号太多了。 >.

我正在使用 Microsoft.VisualBasic.FileIO 命名空间中的 TextFieldParser 和以下设置:

            parser.HasFieldsEnclosedInQuotes = true;
            parser.SetDelimiters(",");
            parser.TextFieldType = FieldType.Delimited;

错误是

MalformedLineException:无法使用当前解析行 9871 分隔符。

我想以某种方式清理数据以解决此问题,但我不知道该怎么做。或者也许有办法跳过这条线?尽管我怀疑我的上级不会批准我只是跳过我们可能需要的数据。

【问题讨论】:

  • 你试过转义问题引用吗?
  • @itsme86 以什么方式逃避它们?
  • 跳过坏行,将它们记录到一个文件中,该文件会定期手动更正并重新运行。希望不会有太多糟糕的台词,以至于变得乏味。
  • @itsme86 对不起,我不明白你在说什么。在我的解析代码中的某处放置反斜杠如何解决任何问题?我不提前知道问题报价会在哪里。对不起,如果我误解了你的 cmets
  • @eddie_cat 我的意思是避开行为不端的引号。如果你试图神奇地解析坏数据,你会非常失望。一旦找到它,我会告诉你如何修复它。

标签: c# csv parsing malformed textfieldparser


【解决方案1】:

如果您只是想摆脱 csv 中的杂散 " 标记,您可以使用以下正则表达式找到它们并将它们替换为 '

String sourcestring = "source string to match with pattern";
String matchpattern = @"(?<!^|,)""(?!(,|$))";
String replacementpattern = @"$1'";
Console.WriteLine(Regex.Replace(sourcestring,matchpattern,replacementpattern,RegexOptions.Multiline));

说明:

@"(?&lt;!^|,)""(?!(,|$))"; 将找到任何" 前面没有字符串开头或, 并且后面没有字符串结尾或,

【讨论】:

  • 谢谢,这正是我需要的。
【解决方案2】:

我不熟悉TextFieldParser。但是使用CsvHelper,您可以为无效数据添加自定义处理程序:

var config = new CsvConfiguration();
config.IgnoreReadingExceptions = true;
config.ReadingExceptionCallback += (e, row) =>
{
    // you can add some custom patching here if possible
    // or, save the line numbers and add/edit them manually later.
};

using(var file = File.OpenRead(".csv"))
using(var reader = new CsvReader(reader, config))
{
    reader.GetRecords<YourDtoClass>();
}

【讨论】:

    【解决方案3】:

    我对每个人所说的唯一补充(因为我们都去过那里)是尝试纠正您在代码中遇到的每个新问题。有一些不错的正则表达式字符串https://www.google.com/?ion=1&espv=2#q=c-sharp+regex+csv+clean 或者您可以使用 String.Replace (String.Replace("\"\"\"","").Replace("\"\","") 手动修复问题.Replace("\",,","\",") 等)。最终,随着您发现并找到纠正越来越多错误的方法,您的手动恢复率将大大降低(您的大部分不良数据可能来自类似的错误)。干杯!

    PS - Idea-ish(已经有一段时间了 - 逻辑可能需要一些调整,因为我是凭记忆写的),但你会明白要点:

    public string[] parseCSVWithQuotes(string csvLine,int expectedNumberOfDataPoints)
        {
            string ret = "";
            string thisChar = "";
            string lastChar = "";
            bool needleDown = true;
            for(int i = 0; i < csvLine.Length; i++)
            {
                thisChar = csvLine.Substring(i, 1);
                if (thisChar == "'"&&lastChar!="'")
                    needleDown = needleDown == true ? false : true;//when needleDown = true, characters are treated literally
                if (thisChar == ","&&lastChar!=",") {
                    if (needleDown)
                    {
                        ret += "|";//convert literal comma to pipe so it doesn't cause another break on split
                    }else
                    {
                        ret += ",";//break on split is intended because the comma is outside the single quote
                    }
                }
                if (!needleDown && (thisChar == "\"" || thisChar == "*")) {//repeat for any undesired character or use RegEx
                                                                           //do not add -- this eliminates any undesired characters outside single quotes
                }
                else
                {
                    if ((lastChar == "'" || lastChar == "\"" || lastChar == ",") && thisChar == lastChar)
                    {
                        //do not add - this eliminates double characters
                    }else
                    {
                        ret += thisChar;
                        lastChar = thisChar;
                        //this character is not an undesired character, is no a double, is valid.
                    }
                }
            }
            //we've cleaned as best we can
            string[] parts = ret.Split(',');
            if(parts.Length==expectedNumberOfDataPoints){
            for(int i = 0; i < parts.Length; i++)
            {
                //go back and replace the temporary pipe with the literal comma AFTER split
                parts[i] = parts[i].Replace("|", ",");
            }
    
            return parts;
            }else{
                //save ret to bad CSV log
                return null;
            }
        }
    

    【讨论】:

    • 添加了我过去如何处理 CSV 解析的示例(尽我所能从记忆中回忆)。这有点糟糕,因为它一次通过一个角色,但如果你是一个好的 RegExer,你可能会完成更好的事情。它可能不漂亮,但它(或类似的东西)对我有用。祝你好运!
    【解决方案4】:

    我以前必须这样做,

    第一步是使用string.split(',')解析数据

    下一步是合并属于一起的段。

    我基本上做的是

    • 创建一个表示组合字符串的新列表
    • 如果字符串以引号开头,请将其推送到新列表中
    • 如果它不以引号开头,请将其附加到列表中的最后一个字符串
    • 奖励:当字符串以引号结尾但下一个字符串不以引号开头时抛出异常

    根据有关数据中实际出现的内容的规则,您可能需要更改代码以解决此问题。

    【讨论】:

      【解决方案5】:

      CSV's file format 的核心是,每一行是一行,该行中的每个单元格用逗号分隔。在您的情况下,您的格式还包含(非常不幸的)规定,即一对引号内的逗号不算作分隔符,而是数据的一部分。我说非常不幸,因为放错的引号会影响整个行的其余部分,并且由于标准 ASCII 中的引号不区分打开和关闭,因此在不知道原始意图的情况下,您真的无法从中恢复。

      当您以一种确实知道原始意图的人(提供数据的人)可以查看文件并更正错误的方式记录消息时:

      if (parse_line(line, &data)) {
         // save the data
      } else {
         // log the error
         fprintf(&stderr, "Bad line: %s", line);
      }
      

      并且由于您的引号没有转义换行符,因此您可以在遇到此错误后继续下一行。

      附录:如果您的公司可以选择(即您的数据正在被公司工具序列化),请不要使用 CSV。使用 XML 或 JSON 之类的具有更明确定义的解析机制。

      【讨论】:

        【解决方案6】:

        我也必须这样做一次。我的方法是通过一条线并跟踪我正在阅读的内容。 基本上,我编写了自己的扫描仪,从输入行中截断了标记,这让我可以完全控制我的错误 .csv 数据。

        这就是我所做的:

        For each character on a line of input.
         1. when outside of a string meeting a comma => all of the previous string (which can be empty) is a valid token.
         2. when outside of a sting meeting anything but a comma or a quote => now you have a real problem, unquoted tekst => handle as you see fit.
         3. when outside of a string meeing a quote => found a start of string.
         4. when inside of a string meeting a comma => accept the comma as part of the string.
         5. when inside of the string meeting a qoute => trouble starts here, mark this point.
           6. continue and when meeting a comma (skipping white space if desired) close the string, 'unread' the comma and continue. (than will bring you to point 1.)
           7. or continue and when meeting a quote -> obviously, what was read must be part of the string, add it to the string, 'unread' the quote and continue. (that will you bring to point 5)
           8. or continue and find an whitespace, then End Of Line ('\n') -> the last qoute must be the closing quote. accept the string as a value.
           9. or continue and fine non-whitespace, then End Of Line. -> now you have a real problem, you have the start of a string but it is not closed -> handle the error as you see fit.
        

        如果您的 .csv 文件中的字段数是固定的,您可以计算您识别为字段分隔符的逗号,当您看到行尾时,您就知道您是否还有其他问题。

        使用从输入行接收到的字符串流,您可以构建一个“干净”的 .csv 行,这样就可以构建一个已接受和已清除的输入缓冲区,您可以在现有代码中使用该缓冲区。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-10-09
          • 2022-12-09
          • 1970-01-01
          • 2011-03-11
          • 2011-09-18
          • 2019-07-28
          • 2021-05-12
          • 2015-03-12
          相关资源
          最近更新 更多