Подтвердить что ты не робот

Разделимый разделитель CSV в .NET.

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

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

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

Спасибо!

4b9b3361

Ответ 1

Из здесь:

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

Ответ 2

Я рекомендую посмотреть 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

Ответ 3

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

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

Ответ 4

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

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

Ответ 6

Вы можете дать 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;
    }
}

Ответ 7

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

Вот код VB:

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

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

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

С помощью этого метода, используя CSV-ридер в 32-мегабайтном CSV файле (около 19000 строк), требуется около 2 секунд, чтобы прочитать файл, выполнить регулярное выражение, загрузить его в CSV-Reader, добавить все данные в мои общие класса и отделки. Настоящий быстро!

Ответ 8

Существуют, по крайней мере, драйверы ODBC для файлов CSV. Но есть разные вкусы CSV.

Что создавали эти файлы? Не исключено, что существует соответствующий драйвер, основанный на требованиях исходного приложения.

Ответ 9

Ваша проблема с CSVReader заключается в том, что цитата в третьей записи не сбежала с другой цитатой (также называемой двойной кавычкой). Если вы не избежите их, то как вы ожидаете обработать ", в середине текстового поля?

http://en.wikipedia.org/wiki/Comma-separated_values

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

Ответ 10

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

        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

Ответ 11

        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;
    }

Ответ 12

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

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

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

string fixedCSV = Regex.Replace(
            File.ReadAllText(fileName),
            @"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);