Qual é a maneira apropriada de lidar com arquivos de texto grandes no Objective-C? Digamos que eu precise ler cada linha separadamente e queira tratar cada linha como um NSString. Qual é a maneira mais eficiente de fazer isso?
Uma solução está usando o método NSString:
+ (id)stringWithContentsOfFile:(NSString *)path
encoding:(NSStringEncoding)enc
error:(NSError **)error
e, em seguida, divida as linhas com um separador de nova linha e itere sobre os elementos da matriz. No entanto, isso parece bastante ineficiente. Não existe uma maneira fácil de tratar o arquivo como um fluxo, enumerando cada linha, em vez de apenas ler tudo de uma vez? Como o java.io.BufferedReader do Java.
Respostas:
Essa é uma ótima pergunta. Acho que o @Diederik tem uma boa resposta, embora seja lamentável que o cacau não tenha um mecanismo para exatamente o que você deseja fazer.
NSInputStream
permite ler trechos de N bytes (muito parecidos comjava.io.BufferedReader
), mas você deve convertê-lo em umNSString
por conta própria, procurar novas linhas (ou qualquer outro delimitador) e salvar os caracteres restantes para a próxima leitura ou ler mais caracteres se uma nova linha ainda não foi lida. (NSFileHandle
permite ler umNSData
que você pode converter em umNSString
, mas é essencialmente o mesmo processo.)A Apple possui um Guia de programação de fluxo que pode ajudar a preencher os detalhes, e essa pergunta SO também pode ajudar se você estiver lidando com
uint8_t*
buffers.Se você estiver lendo seqüências de caracteres como essa com frequência (especialmente em diferentes partes do seu programa), seria uma boa idéia encapsular esse comportamento em uma classe que possa lidar com os detalhes para você, ou mesmo subclassificar
NSInputStream
(ela foi projetada para ser subclasse ) e adicionando métodos que permitem ler exatamente o que você deseja.Para o registro, acho que esse seria um bom recurso a ser adicionado e apresentarei uma solicitação de aprimoramento para algo que torne isso possível. :-)
Edit: Acontece que esta solicitação já existe. Existe um radar datado de 2006 para isso (rdar: // 4742914 para pessoas internas da Apple).
fonte
Isso funcionará para a leitura geral a
String
partir deText
. Se você quiser ler um texto mais longo (tamanho grande) , use o método mencionado por outras pessoas como buffer (reserve o tamanho do texto no espaço de memória) .Digamos que você leia um arquivo de texto.
Você quer se livrar da nova linha.
Aí está.
fonte
Isso deve fazer o truque:
Use da seguinte maneira:
Esse código lê caracteres que não são de nova linha do arquivo, até 4095 por vez. Se você tiver uma linha com mais de 4095 caracteres, ela continuará lendo até atingir uma nova linha ou final de arquivo.
Nota : Eu não testei este código. Por favor, teste-o antes de usá-lo.
fonte
"%4095[^\n]%n%*c"
consumirei silenciosamente e jogarei fora um caractere com cada leitura de buffer. Parece que este formato assume que as linhas serão mais curtas que o tamanho do buffer.O Mac OS X é Unix, o Objective-C é um superconjunto de C, então você pode usar a velha escola
fopen
e afgets
partir de<stdio.h>
. É garantido que funcione.[NSString stringWithUTF8String:buf]
irá converter a string C paraNSString
. Também existem métodos para criar seqüências de caracteres em outras codificações e criar sem copiar.fonte
fgets
incluirá o'\n'
caractere; portanto, você pode retirar isso antes de converter a string.Você pode usar
NSInputStream
uma implementação básica para fluxos de arquivos. Você pode ler bytes em um buffer (read:maxLength:
método). Você precisa verificar se há novas linhas no buffer.fonte
A maneira apropriada de ler arquivos de texto no Cocoa / Objective-C está documentada no guia de programação String da Apple. A seção para ler e gravar arquivos deve ser exatamente o que você . PS: O que é uma "linha"? Duas seções de uma sequência separadas por "\ n"? Ou "\ r"? Ou "\ r \ n"? Ou talvez você esteja realmente procurando parágrafos? O guia mencionado anteriormente também inclui uma seção sobre como dividir uma string em linhas ou parágrafos. (Esta seção é chamada de "parágrafos e quebras de linha" e está vinculada ao menu à esquerda da página que apontei acima. Infelizmente, este site não permite que eu publique mais de um URL, pois ainda não é um usuário confiável.)
Parafraseando Knuth: a otimização prematura é a raiz de todo mal. Não assuma simplesmente que "a leitura do arquivo inteiro na memória" é lenta. Você comparou? Você sabia que ele realmente lê o arquivo inteiro na memória? Talvez ele simplesmente retorne um objeto proxy e continue lendo nos bastidores enquanto você consome a string? ( Isenção de responsabilidade: eu não tenho idéia se o NSString realmente faz isso. É possível que sim. ) O ponto é: primeiro, vá com a maneira documentada de fazer as coisas. Então, se os benchmarks mostrarem que isso não tem o desempenho que você deseja, otimize.
fonte
-stringWithContentsOf*
métodos seguidos por-componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]
, ele verá o\r
e\n
separadamente e adicionará uma linha em branco após cada linha.Muitas dessas respostas são grandes pedaços de código ou são lidas no arquivo inteiro. Eu gosto de usar os métodos c para essa mesma tarefa.
Observe que fgetln não manterá seu caractere de nova linha. Além disso, +1 do comprimento do str porque queremos criar espaço para a terminação NULL.
fonte
Para ler um arquivo linha por linha (também para arquivos grandes extremos), você pode fazer as seguintes funções:
Ou:
A classe DDFileReader que permite isso é o seguinte:
Arquivo de interface (.h):
Implementação (.m)
A aula foi feita por Dave DeLong
fonte
Assim como o @porneL disse, a API C é muito útil.
fonte
Como outras pessoas responderam, o NSInputStream e o NSFileHandle são boas opções, mas isso também pode ser feito de maneira bastante compacta com o NSData e o mapeamento de memória:
BRLineReader.h
BRLineReader.m
fonte
Esta resposta NÃO é ObjC, mas C.
Como o ObjC é baseado em 'C', por que não usar fgets?
E sim, tenho certeza de que a ObjC tem seu próprio método - ainda não sou proficiente o suficiente para saber o que é :)
fonte
meta
pergunta; perguntas muito antigas de usuários regulares podem ser sinalizadas para revisão?da resposta de @Adam Rosenfield, a sequência de formatação de
fscanf
seria alterada como abaixo:funcionará em osx, linux, finais de linha do windows.
fonte
Usando categoria ou extensão para facilitar nossa vida.
fonte
Achei a resposta de @lukaswelte e o código de Dave DeLong muito úteis. Eu estava procurando uma solução para esse problema, mas precisava analisar arquivos grandes,
\r\n
não apenas\n
.O código escrito contém um erro se estiver analisando por mais de um caractere. Eu mudei o código como abaixo.
arquivo .h:
arquivo .m:
fonte
Estou adicionando isso porque todas as outras respostas que tentei ficaram aquém de uma maneira ou de outra. O método a seguir pode manipular arquivos grandes, linhas longas arbitrárias e linhas vazias. Foi testado com conteúdo real e retirará o caractere de nova linha da saída.
O crédito vai para @Adam Rosenfield e @sooop
fonte
Vejo muitas dessas respostas baseadas na leitura de todo o arquivo de texto na memória, em vez de levá-lo um pedaço de cada vez. Aqui está minha solução no Swift moderno e agradável, usando o FileHandle para manter baixo o impacto na memória:
Observe que isso preserva o retorno de carro no final da linha; portanto, dependendo das suas necessidades, você pode ajustar o código para removê-lo.
Uso: basta abrir um identificador de arquivo para o arquivo de texto de destino e chamar
readLine
com um comprimento máximo adequado - 1024 é padrão para texto sem formatação, mas deixei em aberto caso você saiba que será mais curto. Observe que o comando não excederá o final do arquivo; portanto, talvez seja necessário verificar manualmente se você não o alcançou se pretender analisar a coisa toda. Aqui está um código de exemplo que mostra como abrir um arquivomyFileURL
e lê-lo linha por linha até o final.fonte
Aqui está uma solução simples e agradável que eu uso para arquivos menores:
fonte
Use este script, ele funciona muito bem:
fonte