Qual é a maneira mais rápida de texto de análise com delimitadores personalizados e algumas muito, muito grandes valores de campo em C #?

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

Pergunta

Eu tenho tentado lidar com alguns arquivos de texto delimitados que têm delimitadores não padrão (não vírgula / quote ou delimitado por tabulações). Os delimitadores são caracteres ASCII aleatórios que não aparecem muitas vezes entre os delimitadores. Depois de procurar por aí, eu parecem ter apenas não encontrou soluções em .NET irá atender as minhas necessidades e as bibliotecas personalizadas que as pessoas têm escrito para este parecem ter algumas falhas quando se trata de entrada gigantesca (arquivo de 4GB com alguns valores de campo tendo muito facilmente vários milhões de caracteres).

Enquanto este parece ser um pouco exagerado, é na verdade um padrão na indústria de electrónica de documentos Discovery (EDD) para algum software de revisão para ter valores de campo que contêm o conteúdo completo de um documento. Para referência, eu já fiz isso em python usando o módulo csv sem problemas.

Aqui está uma entrada exemplo:

Field delimiter = 
quote character = þ

þFieldName1þþFieldName2þþFieldName3þþFieldName4þ
þValue1þþValue2þþValue3þþSomeVery,Very,Very,Large value(5MB or so)þ
...etc...

Edit: Então fui em frente e criou um analisador de arquivo delimitado a partir do zero. Eu sou tipo de cansado utilizar esta solução, uma vez que pode ser propenso a erros. Ele também não se sente "elegante" ou corrigir ter que escrever meu próprio analisador para uma tarefa como esta. Eu também tenho um sentimento que eu provavelmente não tem que escrever um parser a partir do zero para isso de qualquer maneira.

Foi útil?

Solução

Use a Helpers arquivo API. É .NET e de código aberto. É extremamente alto desempenho utilizando o código IL compilado para campos definidos em objetos fortemente digitadas, e suporta streaming.

Ele suporta todos os tipos de tipos de arquivo e delimitadores personalizados; Eu usei-o para ler arquivos maiores do que 4 GB.

Se por algum motivo que não fazê-lo para você, tente apenas ler linha por linha com um string.split:

public IEnumerable<string[]> CreateEnumerable(StreamReader input)
{
    string line;
    while ((line = input.ReadLine()) != null)
    {
        yield return line.Split('þ');
    }
}

Isso vai dar-lhe matrizes de cadeia simples que representam as linhas de uma forma streamy que você pode até Linq em;) Lembre-se, porém, que o IEnumerable é preguiçoso carregado, por isso não fechar ou alterar o StreamReader até que você tenha iterado ( ou causado uma operação plena carga como ToList / ToArray ou tal -. dado o seu tamanho do arquivo no entanto, eu suponho que você não vai fazer isso)

Aqui está um bom uso amostra de que:

using (StreamReader sr = new StreamReader("c:\\test.file"))
{
    var qry = from l in CreateEnumerable(sr).Skip(1)
              where l[3].Contains("something")
              select new { Field1 = l[0], Field2 = l[1] };
    foreach (var item in qry)
    {
        Console.WriteLine(item.Field1 + " , " + item.Field2);
    }
}
Console.ReadLine();

Isso vai pular a linha de cabeçalho, em seguida, imprimir o primeiro campo dois do arquivo onde o 4º campo contém a string "algo". Ele vai fazer isso sem carregar o arquivo inteiro na memória.

Outras dicas

significa O Windows e alto desempenho I /, uso portas IO conclusão . Você pode ter TODO algum encanamento extra para fazê-lo funcionar no seu caso.

Este é com a compreensão que você quer usar C # /. NET, e de acordo com Joe Duffy

18) Não utilize chamadas do Windows de procedimento assíncrona (APCs) em gerenciado código.

Eu tive que aprender que a maneira dura;), mas descartando o uso APC, IOCP é a única opção sensata. Ele também suporta muitos outros tipos de I / O, frequentemente utilizados em servidores de socket.

Quanto ao analisar o texto real, veja blogue de Eric White para algum uso corrente simplificada.

Eu estaria inclinado a usar uma combinação de arquivos de memória mapeada (ponto MSDN para um invólucro .NET aqui ) e uma análise incrementais simples, produzindo volta a uma lista IEnumerable de sua linha de registro / texto (ou qualquer outro)

Você menciona que alguns campos são muito, muito grande, se você tentar lê-los na íntegra para memória que você pode estar recebendo-se em apuros. Gostaria de ler o arquivo em 8K (ou pequenos pedaços), analisar o buffer atual, acompanhar o estado.

O que você está tentando fazer com esses dados que você está analisando? Você está procurando por algo? Você está transformando-o?

Eu não vejo um problema com você escrevendo um analisador personalizado. Os requisitos parecem suficientemente diferente de qualquer coisa já fornecidas pela BCL, então vá em frente.

"elegância" é obviamente uma coisa subjetiva. Na minha opinião, se os olhares da API do seu analisador e funciona como um "leitor" padrão BCL do tipo API, então isso é muito "elegante".

Quanto aos tamanhos grandes de dados, faça o seu trabalho parser lendo um byte de cada vez e usar uma máquina de estado simples de trabalhar para fora o que fazer. Deixe o fluxo e colocar em buffer à classe FileStream subjacente. Você deve estar OK com o desempenho e consumo de memória.

Exemplo de como você pode usar essa classe um analisador:

using(var reader = new EddReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)) {
    // Read a small field
    string smallField = reader.ReadFieldAsText();
    // Read a large field
    Stream largeField = reader.ReadFieldAsStream();
}

Enquanto isso não ajudar a resolver a grande questão de entrada, uma possível solução para a questão de análise pode incluir um analisador personalizado que o padrão usuários a estratégia para fornecer um delimitador.

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