Разобрать CSV в .NET - PullRequest
       52

Разобрать CSV в .NET

24 голосов
/ 10 апреля 2009

У меня есть текстовый файл в формате через запятую, разделенный " в большинстве полей. Я пытаюсь передать это во что-то, через что я могу перечислить (например, Generic Collection). У меня нет контроля ни над тем, как выводится файл, ни над символом, который он использует для разделителя.

В этом случае поля разделяются запятой, а текстовые поля заключаются в отметки ". Проблема, с которой я сталкиваюсь, заключается в том, что в некоторых полях есть кавычки (т. Е. 8 " Tray), и они случайно выбираются в качестве следующего поля. В случае числовых полей у них нет кавычек вокруг них, но они начинаются со знака + или - (обозначая положительное / отрицательное число).

Я думал о RegEx, но мои навыки не так уж и хороши, так что, надеюсь, кто-то может предложить некоторые идеи, которые я смогу попробовать. В этом файле содержится около 19 000 записей, поэтому я стараюсь сделать это максимально эффективно. Вот пара примеров строк данных:

"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, и у меня есть общие настройки List для приема данных. Я попытался использовать CSVReader , и, кажется, он работает хорошо, пока вы не нажмете на запись, подобную 3-й (с цитатой в текстовом поле). Если бы я мог как-то заставить его обрабатывать дополнительные кавычки, тогда опция CSVReader будет работать отлично.

Спасибо!

Ответы [ 12 ]

76 голосов
/ 10 апреля 2009

Я рекомендую взглянуть на TextFieldParserClass в .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
11 голосов
/ 05 сентября 2009

Попробуйте этот сайт. http://kbcsv.codeplex.com/

Я искал хорошую утилиту, и это лучшее из того, что я нашел и работает правильно. Не тратьте свое время на другие вещи, это бесплатно и работает.

7 голосов
/ 20 декабря 2009

Как эта ссылка говорит ... Не катите свой собственный анализатор CSV!

Используйте TextFieldParser, как предложил Avi. Microsoft уже сделала это для вас. Если вы в итоге написали одну и обнаружили в ней ошибку, рассмотрите возможность ее замены, а не исправления. Я сделал это недавно, и это сэкономило мне много времени.

7 голосов
/ 10 апреля 2009

С здесь :

Encoding fileEncoding = GetFileEncoding(csvFile);
// get rid of all doublequotes except those used as field delimiters
string fileContents = File.ReadAllText(csvFile, fileEncoding);
string fixedContents = Regex.Replace(fileContents, @"([^\^,\r\n])""([^$,\r\n])", @"$1$2");
using (CsvReader csv =
       new CsvReader(new StringReader(fixedContents), true))
{
       // ... parse the CSV
5 голосов
/ 12 января 2012

Вы можете попробовать CsvHelper (библиотека, которую я поддерживаю), и она доступна через NuGet . Он соответствует стандарту RFC 4180 для CSV. Он сможет обрабатывать любой контент внутри поля, включая запятые, кавычки и новые строки.

CsvHelper прост в использовании, но его также легко настроить для работы со многими различными типами файлов с разделителями.

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

Если вы хотите читать файлы CSV на более низком уровне, вы можете использовать синтаксический анализатор напрямую, который будет возвращать каждую строку в виде строкового массива.

var parser = new CsvParser( myTextReader );
while( true )
{
    string[] line = parser.ReadLine();
    if( line == null )
    {
        break;
    }
}
5 голосов
/ 10 апреля 2009

Посмотрите на библиотеку FileHelpers .

1 голос
/ 28 декабря 2011

RegEx для исключения первой и последней цитаты будет (?<!^)(?<!,)("")(?!,)(?!$). Конечно, вам нужно использовать RegexOptions.Multiline.

Таким образом, нет необходимости в функции оценщика. Мой код заменяет нежелательные двойные кавычки одинарными.

Полный код C # приведен ниже.

string fixedCSV = Regex.Replace(
            File.ReadAllText(fileName),
            @"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);
1 голос
/ 13 апреля 2009

Я публикую это как ответ, чтобы я мог объяснить, как я это сделал и почему .... Ответ от Митча Уитта был тем, который дал мне лучшее решение для этого случая, и мне просто пришлось немного изменить его из-за в формат, в котором эти данные были экспортированы.

Вот код VB:

Dim fixedContents As String = Regex.Replace(
                            File.ReadAllText(csvFile, fileEncoding),
                            "(?<!,)("")(?!,)", 
                            AddressOf ReplaceQuotes)

Используемый RegEx - это то, что мне нужно было изменить, потому что в некоторых полях содержались неэкранированные кавычки, а предоставленный RegEx, похоже, не работал на всех примерах. В этом случае используются «Look Ahead» и «Look Behind», чтобы узнать, идет ли кавычка сразу после запятой или непосредственно перед. В этом случае они оба отрицательны (то есть покажите мне, где двойная кавычка не до или после запятой). Это должно означать, что кавычка находится в середине строки.

В этом случае вместо прямой замены я использую функцию ReplaceQuotes, чтобы справиться с этим для меня. Я использую это потому, что мне нужно было немного дополнительной логики, чтобы определить, было ли это в начале строки. Если бы я потратил на это еще больше времени, я уверен, что мог бы настроить RegEx, чтобы учесть начало строки (используя MultiLine и т. Д.), Но когда я попробовал это быстро, это не сработало все.

Имея это в виду, используя CSV-ридер для CSV-файла 32 МБ (около 19000 строк), чтение файла, выполнение регулярного выражения, загрузка его в CSV-ридер и добавление всех данных в общий файл занимает около 2 секунд класс и финиш. Очень быстро !!

0 голосов
/ 17 января 2011
        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;
    }
0 голосов
/ 11 апреля 2009

Логика этого нестандартного подхода такова: прочитать файл по одной строке за раз, разбить каждую строку на запятую, удалить первый и последний символ (удалить внешние кавычки, но не повлиять на внутренние кавычки), затем добавить данные в ваш общий список. Он короткий и с ним легко читать и работать.

        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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...