Ищем Regex, чтобы найти цитируемые символы новой строки в большой строке (для C #) - PullRequest
2 голосов
/ 28 августа 2008

У меня есть большая строка (назовем это CSV-файлом, хотя на самом деле это не тот файл, сейчас это будет проще), который мне нужно проанализировать в коде C #.
Первый шаг процесса анализа разбивает файл на отдельные строки, просто используя объект StreamReader и вызывая ReadLine, пока он не пройдет через файл. Однако любая строка может содержать заключенный в одинарные кавычки литерал со встроенными символами новой строки. Мне нужно найти эти новые строки и временно преобразовать их в какой-нибудь токен или escape-последовательность, пока я не разделю файл на массив строк ... затем я смогу изменить их обратно.

Пример входных данных:

1,2,10,99,'Some text without a newline', true, false, 90
2,1,11,98,'This text has an embedded newline 
                and continues here', true, true, 90

Я мог бы написать весь код C #, необходимый для этого, используя string.IndexOf, чтобы найти цитируемые разделы и искать в них новые строки, но я думаю, что Regex может быть лучшим выбором (то есть сейчас У меня две проблемы )

Ответы [ 4 ]

3 голосов
/ 28 августа 2008

Конечные автоматы для выполнения такой работы упрощаются с помощью итераторов C # 2.0. Вот, надеюсь, последний парсер CSV, который я когда-либо напишу. Весь файл обрабатывается как перечисляемая группа перечислимых строк, то есть строк / столбцов. IEnumerable хорош, потому что он может быть обработан операторами LINQ.

public class CsvParser
{
    public char FieldDelimiter { get; set; }

    public CsvParser()
        : this(',')
    {
    }

    public CsvParser(char fieldDelimiter)
    {
        FieldDelimiter = fieldDelimiter;
    }

    public IEnumerable<IEnumerable<string>> Parse(string text)
    {
        return Parse(new StringReader(text));
    }
    public IEnumerable<IEnumerable<string>> Parse(TextReader reader)
    {
        while (reader.Peek() != -1)
            yield return parseLine(reader);
    }

    IEnumerable<string> parseLine(TextReader reader)
    {
        bool insideQuotes = false;
        StringBuilder item = new StringBuilder();

        while (reader.Peek() != -1)
        {
            char ch = (char)reader.Read();
            char? nextCh = reader.Peek() > -1 ? (char)reader.Peek() : (char?)null;

            if (!insideQuotes && ch == FieldDelimiter)
            {
                yield return item.ToString();
                item.Length = 0;
            }
            else if (!insideQuotes && ch == '\r' && nextCh == '\n') //CRLF
            {
                reader.Read(); // skip LF
                break;
            }
            else if (!insideQuotes && ch == '\n') //LF for *nix-style line endings
                break;
            else if (ch == '"' && nextCh == '"') // escaped quotes ""
            {
                item.Append('"');
                reader.Read(); // skip next "
            }
            else if (ch == '"')
                insideQuotes = !insideQuotes;
            else
                item.Append(ch);
        }
        // last one
        yield return item.ToString();
    }

}

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

3 голосов
/ 28 августа 2008

Поскольку это не настоящий CSV-файл, имеет ли он какую-либо схему?

Из вашего примера это выглядит так: int, int, int, int, строка, bool, bool, int

С этим составляет вашу запись / объект.

Предполагая, что ваши данные правильно сформированы (я не знаю достаточно о вашем источнике, чтобы знать, насколько обоснованным является это предположение); Вы могли бы:

  1. Читайте вашу строку.
  2. Используйте конечный автомат для анализа ваших данных.
  3. Если ваша строка заканчивается, и вы анализируете строку, прочитайте следующую строку ... и продолжайте анализ.

Я бы по возможности избегал использования регулярных выражений.

1 голос
/ 28 августа 2008

Что, если вы получили весь файл в переменную, а затем разбили ее на основе строк без кавычек?

0 голосов
/ 28 августа 2008

РЕДАКТИРОВАТЬ: Извините, я неверно истолковал ваш пост. Если вы ищете регулярное выражение, то вот одно:

content = Regex.Replace(content, "'([^']*)\n([^']*)'", "'\1TOKEN\2'");

Могут быть крайние случаи и две проблемы, но я думаю, что в большинстве случаев все будет хорошо. Регулярное выражение делает то, что сначала находит любую пару одинарных кавычек, между которыми находится \ n, и заменяет \ n на TOKEN, сохраняя любой промежуточный текст.

Но, тем не менее, я бы использовал конечный автомат, как описано ниже @bryansh.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...