C#, регулярные выражения: как проанализировать разделенные от запятой значения, где некоторые значения могли бы быть заключены в кавычки сами строки, содержащие запятые

Может быть лучшим алгоритмом для задачи является алгоритм быстрого целочисленного квадратного корня https://stackoverflow.com/a/51585204/5191852

Там @Kde утверждает, что три итерации метода Ньютона было бы достаточно для точности ± 1 для 32-битных целых чисел. Конечно, для 64-битных целых чисел требуется больше итераций, может быть 6 или 7.

9
задан JaysonFix 27 July 2009 в 17:11
поделиться

6 ответов

Попробуйте использовать это регулярное выражение:

"[^"\r\n]*"|'[^'\r\n]*'|[^,\r\n]*

    Regex regexObj = new Regex(@"""[^""\r\n]*""|'[^'\r\n]*'|[^,\r\n]*");
    Match matchResults = regexObj.Match(input);
    while (matchResults.Success) 
    {
        Console.WriteLine(matchResults.Value);
        matchResults = matchResults.NextMatch();
    }

Выводы:

23
ответ дан 4 December 2019 в 05:52
поделиться

Почему бы не прислушаться к советам экспертов и Не использовать собственный парсер CSV .

Ваша первая мысль: «Мне нужно обрабатывать запятые внутри кавычки. "

Ваша следующая мысль будет:" О, дерьмо, мне нужно обрабатывать кавычки внутри кавычек. Экранированные кавычки. Двойные кавычки. Одинарные кавычки ... "

Это путь к безумию. Не пишите свои собственные. Найдите библиотеку с обширным охватом модульного тестирования, которая затрагивает все сложные моменты и прошла через ад за вас. Для .NET используйте бесплатную библиотеку с открытым исходным кодом FileHelpers .

21
ответ дан 4 December 2019 в 05:52
поделиться

это не регулярное выражение, но я использовал Microsoft.VisualBasic.FileIO.TextFieldParser для выполнения этого для файлов csv. да, может показаться немного странным добавление ссылки на Microsoft.VisualBasic в приложении C #, может быть, даже немного грязным, но эй, это работает.

7
ответ дан 4 December 2019 в 05:52
поделиться

А, RegEx. Теперь у вас две проблемы. ;)

Я бы использовал токенизатор / синтаксический анализатор, поскольку он довольно прост и, что более важно, его гораздо легче читать для дальнейшего обслуживания.

Это работает, например:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        string myString = "cat,dog,\"0 = OFF, 1 = ON\",lion,tiger,'R = red, G = green,     B = blue',bear"; 
        Console.WriteLine("\nmyString is ...\n\t" + myString + "\n");
        CsvParser parser = new CsvParser(myString);

        Int32 lineNumber = 0;
        foreach (string s in parser)
        {
            Console.WriteLine(lineNumber + ": " + s);
        }

        Console.ReadKey();
    }
}

internal enum TokenType
{
    Comma,
    Quote,
    Value
}

internal class Token
{
    public Token(TokenType type, string value)
    {
        Value = value;
        Type = type;
    }

    public String Value { get; private set; }
    public TokenType Type { get; private set; }
}

internal class StreamTokenizer : IEnumerable<Token>
{
    private TextReader _reader;

    public StreamTokenizer(TextReader reader)
    {
        _reader = reader;    
    }

    public IEnumerator<Token> GetEnumerator()
    {
        String line;
        StringBuilder value = new StringBuilder();

        while ((line = _reader.ReadLine()) != null)
        {
            foreach (Char c in line)
            {
                switch (c)
                {
                    case '\'':
                    case '"':
                        if (value.Length > 0)
                        {
                            yield return new Token(TokenType.Value, value.ToString());
                            value.Length = 0;
                        }
                        yield return new Token(TokenType.Quote, c.ToString());
                        break;
                    case ',':
                       if (value.Length > 0)
                        {
                            yield return new Token(TokenType.Value, value.ToString());
                            value.Length = 0;
                        }
                        yield return new Token(TokenType.Comma, c.ToString());
                        break;
                    default:
                        value.Append(c);
                        break;
                }
            }

            // Thanks, dpan
            if (value.Length > 0) 
            {
                yield return new Token(TokenType.Value, value.ToString()); 
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

internal class CsvParser : IEnumerable<String>
{
    private StreamTokenizer _tokenizer;

    public CsvParser(Stream data)
    {
        _tokenizer = new StreamTokenizer(new StreamReader(data));
    }

    public CsvParser(String data)
    {
        _tokenizer = new StreamTokenizer(new StringReader(data));
    }

    public IEnumerator<string> GetEnumerator()
    {
        Boolean inQuote = false;
        StringBuilder result = new StringBuilder();

        foreach (Token token in _tokenizer)
        {
            switch (token.Type)
            {
                case TokenType.Comma:
                    if (inQuote)
                    {
                        result.Append(token.Value);
                    }
                    else
                    {
                        yield return result.ToString();
                        result.Length = 0;
                    }
                    break;
                case TokenType.Quote:
                    // Toggle quote state
                    inQuote = !inQuote;
                    break;
                case TokenType.Value:
                    result.Append(token.Value);
                    break;
                default:
                    throw new InvalidOperationException("Unknown token type: " +    token.Type);
            }
        }

        if (result.Length > 0)
        {
            yield return result.ToString();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
7
ответ дан 4 December 2019 в 05:52
поделиться

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

Чтение CSV не так сложно для написания в качестве конечного автомата, поскольку грамматика проста, но даже в этом случае вы должны учитывать: кавычки, запятые в кавычках, новые строки в кавычках, пустые поля.

Таким образом, вам, вероятно, следует просто воспользуйтесь чужим парсером CSV. Я рекомендую CSVReader для .Net

4
ответ дан 4 December 2019 в 05:52
поделиться

Функция:

    private List<string> ParseDelimitedString (string arguments, char delim = ',')
    {
        bool inQuotes = false;
        bool inNonQuotes = false; //used to trim leading WhiteSpace

        List<string> strings = new List<string>();

        StringBuilder sb = new StringBuilder();
        foreach (char c in arguments)
        {
            if (c == '\'' || c == '"')
            {
                if (!inQuotes)
                    inQuotes = true;
                else
                    inQuotes = false;
            }else if (c == delim)
            {
                if (!inQuotes)
                {
                    strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());
                    sb.Remove(0, sb.Length);
                    inNonQuotes = false;
                }
                else
                {
                    sb.Append(c);
                }
            }
            else if ( !char.IsWhiteSpace(c) && !inQuotes && !inNonQuotes)  
            {
                if (!inNonQuotes) inNonQuotes = true;
                sb.Append(c);
            }
        }
        strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());


        return strings;
    }

Использование

    string myString = "cat,dog,\"0 = OFF, 1 = ON\",lion,tiger,'R = red, G = green, B = blue',bear,         text";
    List<string> strings = ParseDelimitedString(myString);

    foreach( string s in strings )
            Console.WriteLine( s );

Вывод:

cat
dog
0 = OFF, 1 = ON
lion
tiger
R = red, G = green, B = blue
bear
text
2
ответ дан 4 December 2019 в 05:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: