在.NET中解析分隔的CSV

在.NET中解析分隔的CSV,.net,vb.net,parsing,csv,.net,Vb.net,Parsing,Csv,我有一个逗号分隔格式的文本文件,在大多数字段上用分隔符分隔。例如,我正试图通过泛型集合来列举它。我无法控制文件的输出方式,也无法控制它用作分隔符的字符 在这种情况下,字段用逗号分隔,文本字段用标记括起。我遇到的问题是,一些字段中有引号,例如8托盘,并且意外地被选为下一个字段。对于数字字段,它们周围没有引号,但它们以+或-符号开头,表示正数/负数 我在考虑一个正则表达式,但我的技能不是很好,所以希望有人能想出一些想法,我可以试试。这个文件中大约有19000条记录,所以我正在尽可能高效地完成它。以下

我有一个逗号分隔格式的文本文件,在大多数字段上用分隔符分隔。例如,我正试图通过泛型集合来列举它。我无法控制文件的输出方式,也无法控制它用作分隔符的字符

在这种情况下,字段用逗号分隔,文本字段用标记括起。我遇到的问题是,一些字段中有引号,例如8托盘,并且意外地被选为下一个字段。对于数字字段,它们周围没有引号,但它们以+或-符号开头,表示正数/负数

我在考虑一个正则表达式,但我的技能不是很好,所以希望有人能想出一些想法,我可以试试。这个文件中大约有19000条记录,所以我正在尽可能高效地完成它。以下是两个数据行示例:

"00","000000112260   ","Pie Pumpkin                             ","RET","6.99 ","     ","ea ",+0000000006.99000
"00","000000304078   ","Pie Apple caramel                       ","RET","9.99 ","     ","ea ",+0000000009.99000
"00","StringValue here","8" Tray of Food                             ","RET","6.99 ","     ","ea ",-00000000005.3200
有更多的字段,但你可以得到图片

我正在使用VB.NET,我有一个通用列表设置来接受数据。我试过使用它,它似乎工作得很好,直到你找到一个记录,如第三个在文本字段中引用。如果我能以某种方式让它处理额外的报价,那么CSVReader选项将非常有效


谢谢

至少有用于CSV文件的ODBC驱动程序。但是CSV有不同的风格


是什么产生了这些文件?根据源应用程序的要求,不太可能有匹配的驱动程序。

请看一看。

CSVReader的问题是,第三条记录中的引号没有用另一个引号(也称为双引号)转义。如果你不逃避它们,那么在文本字段的中间,你希望如何处理?

我最终不得不使用具有不同分隔符的文件,但文本值中的引号字符没有转义,我最终编写了自己的自定义解析器。我不知道这是否绝对必要。

来自:


我建议您查看.Net中的。你需要包括

Imports Microsoft.VisualBasic.FileIO.TextFieldParser
下面是一个快速示例:

        Dim afile As FileIO.TextFieldParser = New FileIO.TextFieldParser(FileName)
        Dim CurrentRecord As String() ' this array will hold each line of data
        afile.TextFieldType = FileIO.FieldType.Delimited
        afile.Delimiters = New String() {","}
        afile.HasFieldsEnclosedInQuotes = True

        ' parse the actual file
        Do While Not afile.EndOfData
            Try
                CurrentRecord = afile.ReadFields
            Catch ex As FileIO.MalformedLineException
                Stop
            End Try
        Loop

这种自定义方法的逻辑是:一次读取一行文件,分割逗号上的每一行,删除第一个和最后一个字符,删除外部引号,但不影响任何内部引号,然后将数据添加到通用列表中。它很短,很容易阅读和使用

        Dim fr As StreamReader = Nothing
        Dim FileString As String = ""
        Dim LineItemsArr() as String

        Dim FilePath As String = HttpContext.Current.Request.MapPath("YourFile.csv")

        fr = New System.IO.StreamReader(FilePath)

        While fr.Peek <> -1
            FileString = fr.ReadLine.Trim

            If String.IsNullOrEmpty(FileString) Then Continue While 'Empty Line

            LineItemsArr = FileString.Split(",")

            For Each Item as String In LineItemsArr
                'If every item will have a beginning and closing " (quote) then you can just
                'cut the first and last characters of the string here.
                'i.e.  UpdatedItems = Item. remove first and last character

                'Then stick the data into your Generic List (Of String()?)
            Next
        End While

我将此作为一个答案发布,以便我可以解释我是如何做到这一点以及为什么。。。。米奇·麦特(Mitch Wheat)的回答为我提供了这个案例的最佳解决方案,由于数据导出的格式,我只需稍微修改一下

下面是VB代码:

Dim fixedContents As String = Regex.Replace(
                            File.ReadAllText(csvFile, fileEncoding),
                            "(?<!,)("")(?!,)", 
                            AddressOf ReplaceQuotes)
我需要更改使用的正则表达式,因为某些字段中有非转义引号,并且提供的正则表达式似乎不能在所有示例中使用。这一个使用“向前看”和“向后看”来查看引号是在逗号之后还是在逗号之前。在这种情况下,它们都是否定的,意思是告诉我双引号不在逗号之前或之后。这意味着引用在字符串的中间。 在本例中,我不是直接进行替换,而是使用函数ReplaceQuotes来处理这个问题。我之所以使用它,是因为我需要一点额外的逻辑来检测它是否在一行的开头。如果我花更多的时间在它上面,我肯定我可以调整正则表达式以考虑行的开头使用多行等,但是当我快速尝试时,它似乎根本不起作用

有了这个功能,在一个32MB的CSV文件上使用CSV读取器大约19000行,读取文件、执行正则表达式、加载到CSV读取器、将所有数据添加到我的泛型类并完成大约需要2秒钟。真快

试试这个网站

我一直在寻找一个很好的实用程序,这是我找到的最好的工具,并且可以正常工作。不要浪费时间尝试其他东西,这是免费的,而且很有效。

正如此链接所述


按照Avi的建议使用TextFieldParser。Microsoft已经为您完成了这项工作。如果你写了一个,发现其中有一个bug,考虑替换它,而不是修复bug。我最近就这么做了,这节省了我很多时间。

排除第一个和最后一个报价的正则表达式是?。当然,您需要使用RegexOptions.Multiline

        public static Encoding GetFileEncoding(String fileName)
    {
        Encoding Result = null;
        FileInfo FI = new FileInfo(fileName);
        FileStream FS = null;

        try
        {
            FS = FI.OpenRead();
            Encoding[] UnicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8 };
            for (int i = 0; Result == null && i < UnicodeEncodings.Length; i++)
            {
                FS.Position = 0;
                byte[] Preamble = UnicodeEncodings[i].GetPreamble();
                bool PreamblesAreEqual = true;
                for (int j = 0; PreamblesAreEqual && j < Preamble.Length; j++)
                {
                    PreamblesAreEqual = Preamble[j] == FS.ReadByte();
                }
                if (PreamblesAreEqual)
                {
                    Result = UnicodeEncodings[i];
                }
            }
        }
        catch (System.IO.IOException)
        {
        }
        finally
        {
            if (FS != null)
            {
                FS.Close();
            }
        }

        if (Result == null)
        {
            Result = Encoding.Default;
        }

        return Result;
    }
这样就不需要计算器功能。我的代码将不需要的双引号替换为单引号

完整的C代码如下所示

string fixedCSV = Regex.Replace(
            File.ReadAllText(fileName),
            @"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);
你可以给我维护的图书馆一个尝试,它可以通过。它遵循CSV的标准。它将能够处理字段内的任何内容,包括逗号、引号和新行

CsvHelper使用简单,但配置它以处理许多不同类型的分隔文件也很容易

CsvReader csv = new CsvReader( streamToFile );
IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>();

这是一个基于DOS的旧帐户

ing称为商业愿景三角洲的软件包。不幸的是,公司已经卖给了新的供应商,他们不再支持旧的DOS。这是我提取数据以集成到更新软件中的唯一方法。您能告诉我它使用的是什么类型的数据表吗?也许是dbfs?另外,尝试使用Excel、Access或其他任何可以导入CSV的应用程序打开CSV文件。尽量避免将编写软件作为第一选择。这是我的问题。。。我逃不过他们。我无法控制文件的导出方式。我试图避免编写一个语法分析器,它逐个字符检查引号后是否有逗号,等等。但它可能会归结到这一点。好吧,如果你选择自己的方法,我仍然相信有一个解决方案可以处理这种情况,只要确保尽可能地验证字段计数和数据。我会发布我的,但我是在工作中发布的。这将有助于正确格式化CSV文件。引用字符串中的双引号应该通过加倍转义。因此,8盘食物是不允许的格式。这样,像hi这样的字符串就可以存在了。转义和引用,它变成hi,那里。如果不加倍,它会变成hi,看起来像两个字符串。我同意上面的两条评论,但不幸的是,我无法控制文件的导出方式。这就是它从软件中产生的方式。请,请,请不要使用你自己的CSV解析器,当然也不要使用正则表达式。使用免费、开源、经过战斗测试的FileHelpers库。filehelpers源代码不再可用:这很有效,但由于某些原因,它在名称上出错,例如:Product a name我确信它与RegEx有关,但我似乎无法正确地理解它。请参阅下面我的答案,了解我是如何实现这一点的。这是我使用的一个很好的解决方案,但GetFileEncoding函数不可用。如果有人需要,我会稍后发布。或者在剥离外部引号之前,将其用作测试来执行字符串处理,或者如果需要,使用数字处理。这不会处理数据中分隔符所在的数据看起来不错,但我发现使用它非常令人沮丧。缺少对自动属性而不是私有字段的支持是非常笨拙的。这不是最初问题的一个因素,但该页面说FileHelpers使用动态代码生成。这意味着它在某些受约束的环境中对我来说并不有用。仅供参考:TextFieldParser实现IDisposable,应该包装在“using”语句中或显式释放。如果引用字段中有换行符,这似乎不起作用。Bummer.有人知道为什么这样一个通用类会存在于VisualBasic名称空间中吗?2021检入-Avi的答案仍然是我找到的最好的解决方案,可以使用Framework 4.6解析一些字段嵌入逗号的CSV。我来这里是因为其他方法不那么容易使用。另外,它是迄今为止我见过的最快的通用csv解析器。
CsvReader csv = new CsvReader( streamToFile );
IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>();
var parser = new CsvParser( myTextReader );
while( true )
{
    string[] line = parser.ReadLine();
    if( line == null )
    {
        break;
    }
}