Procurando Regex para encontrar novas linhas entre aspas em uma string grande (para C#)

StackOverflow https://stackoverflow.com/questions/33063

  •  09-06-2019
  •  | 
  •  

Pergunta

Eu tenho uma string grande (vamos chamá-la de arquivo CSV, embora na verdade não seja um, será mais fácil por enquanto) que preciso analisar em código C#.
A primeira etapa do processo de análise divide o arquivo em linhas individuais usando apenas um StreamReader objeto e chamada ReadLine até que passe pelo arquivo.No entanto, qualquer linha pode conter um literal entre aspas simples com novas linhas incorporadas.Preciso encontrar essas novas linhas e convertê-las temporariamente em algum outro tipo de token ou sequência de escape até dividir o arquivo em uma série de linhas... então posso alterá-las novamente.

Dados de entrada de exemplo:

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

Eu poderia escrever todo o código C# necessário para fazer isso usando string.IndexOf para encontrar as seções citadas e procurar novas linhas nelas, mas estou pensando que um Regex pode ser uma escolha melhor (ou seja, agora tenho dois problemas)

Foi útil?

Solução

Como este não é um arquivo CSV verdadeiro, ele possui algum tipo de esquema?

Pelo seu exemplo, parece que você tem:int, int, int, int, string, bool, bool, int

Com isso compondo seu registro/objeto.

Supondo que seus dados estejam bem formados (não sei o suficiente sobre sua fonte para saber quão válida é essa suposição);você poderia:

  1. Leia sua linha.
  2. Use uma máquina de estado para analisar seus dados.
  3. Se sua linha terminar e você estiver analisando uma string, leia a próxima linha... e continue analisando.

Eu evitaria usar um regex, se possível.

Outras dicas

Máquinas de estado para realizar esse trabalho são facilitadas com o uso de iteradores C# 2.0.Espero que este seja o último analisador CSV que escreverei.O arquivo inteiro é tratado como um grupo enumerável de strings enumeráveis, ou seja,linhas colunas.IEnumerable é ótimo porque pode ser processado por operadores 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();
    }

}

Observe que o arquivo é lido caractere por caractere, com o código decidindo quando as novas linhas devem ser tratadas como delimitadores de linha ou parte de uma string entre aspas.

E se você colocasse o arquivo inteiro em uma variável e depois dividisse isso com base em novas linhas não citadas?

EDITAR: Desculpe, interpretei mal sua postagem.Se você está procurando um regex, aqui está um:

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

Pode haver casos extremos e esses dois problemas, mas acho que deve ficar tudo bem na maioria das vezes.O que o Regex faz é primeiro encontrar qualquer par de aspas simples que tenha entre ele e substituí-lo por TOKEN preservando qualquer texto intermediário.

Mesmo assim, eu usaria uma máquina de estado como @bryansh explicou abaixo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top