¿Cuál es la forma adecuada de tratar con archivos de texto grandes en Objective-C? Digamos que necesito leer cada línea por separado y quiero tratar cada línea como un NSString. ¿Cuál es la forma más eficiente de hacer esto?
Una solución es usar el método NSString:
+ (id)stringWithContentsOfFile:(NSString *)path
encoding:(NSStringEncoding)enc
error:(NSError **)error
y luego dividir las líneas con un separador de nueva línea, y luego iterar sobre los elementos en la matriz. Sin embargo, esto parece bastante ineficiente. ¿No hay una manera fácil de tratar el archivo como una secuencia, enumerando cada línea, en lugar de solo leerlo todo de una vez? Algo así como java.io.BufferedReader de Java.
Respuestas:
Esa es una gran pregunta. Creo que @Diederik tiene una buena respuesta, aunque es lamentable que Cocoa no tenga un mecanismo para exactamente lo que quieres hacer.
NSInputStream
le permite leer fragmentos de N bytes (muy similares ajava.io.BufferedReader
), pero debe convertirlos en unoNSString
por su cuenta, luego buscar nuevas líneas (o cualquier otro delimitador) y guardar los caracteres restantes para la próxima lectura, o leer más caracteres si aún no se ha leído una nueva línea. (leNSFileHandle
permite leer unoNSData
que luego puede convertir en unNSString
, pero es esencialmente el mismo proceso).Apple tiene una Guía de programación de Stream que puede ayudar a completar los detalles, y esta pregunta SO también puede ayudar si se trata de
uint8_t*
buffers.Si va a leer cadenas como esta con frecuencia (especialmente en diferentes partes de su programa), sería una buena idea encapsular este comportamiento en una clase que pueda manejar los detalles por usted, o incluso subclasificar
NSInputStream
(está diseñado para ser subclases ) y agregar métodos que le permiten leer exactamente lo que desea.Para el registro, creo que esta sería una buena característica para agregar, y presentaré una solicitud de mejora para algo que lo haga posible. :-)
Editar: Resulta que esta solicitud ya existe. Hay un Radar que data de 2006 para esto (rdar: // 4742914 para personas internas de Apple).
fuente
Esto funcionará para leer un general
String
deText
. Si desea leer un texto más largo (gran tamaño del texto) , utilice el método que otras personas mencionaron aquí, como el búfer (reserve el tamaño del texto en el espacio de la memoria) .Digamos que lees un archivo de texto.
Desea deshacerse de la nueva línea.
Ahí tienes.
fuente
Esto debería funcionar:
Use de la siguiente manera:
Este código lee caracteres que no son de nueva línea del archivo, hasta 4095 a la vez. Si tiene una línea que tiene más de 4095 caracteres, sigue leyendo hasta que llega a una nueva línea o al final del archivo.
Nota : no he probado este código. Por favor, pruébelo antes de usarlo.
fuente
"%4095[^\n]%n%*c"
consumiré y desecharé en silencio un carácter con cada lectura de búfer. Parece que este formato supone que las líneas serán más cortas que la longitud del búfer.Mac OS X es Unix, Objective-C es un superconjunto C, por lo que puede usar la vieja escuela
fopen
yfgets
de<stdio.h>
. Está garantizado para trabajar.[NSString stringWithUTF8String:buf]
convertirá la cadena C aNSString
. También hay métodos para crear cadenas en otras codificaciones y crear sin copiar.fuente
fgets
incluirá el'\n'
carácter, por lo que es posible que desee quitarlo antes de convertir la cadena.Puede usar el
NSInputStream
que tiene una implementación básica para secuencias de archivos. Puede leer bytes en un búfer (read:maxLength:
método). Tienes que escanear el búfer en busca de nuevas líneas.fuente
La forma adecuada de leer archivos de texto en Cocoa / Objective-C está documentada en la guía de programación String de Apple. La sección para leer y escribir archivos. debe ser justo lo que buscas. PD: ¿Qué es una "línea"? ¿Dos secciones de una cadena separadas por "\ n"? O "\ r"? O "\ r \ n"? ¿O tal vez estás realmente después de los párrafos? La guía mencionada anteriormente también incluye una sección sobre la división de una cadena en líneas o párrafos. (Esta sección se llama "Párrafos y saltos de línea", y está vinculada en el menú del lado izquierdo de la página que señalé anteriormente. Desafortunadamente este sitio no me permite publicar más de una URL ya que estoy no es un usuario confiable todavía).
Parafraseando a Knuth: la optimización prematura es la raíz de todo mal. No asuma simplemente que "leer todo el archivo en la memoria" es lento. ¿Lo has comparado? ¿Sabes que en realidad lee todo el archivo en la memoria? ¿Tal vez simplemente devuelve un objeto proxy y sigue leyendo detrás de escena mientras consume la cadena? ( Descargo de responsabilidad: no tengo idea si NSString realmente hace esto. Posiblemente podría ) . El punto es: primero ve con la forma documentada de hacer las cosas. Luego, si los puntos de referencia muestran que esto no tiene el rendimiento que desea, optimice.
fuente
-stringWithContentsOf*
métodos seguidos-componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]
, ve el\r
y por\n
separado y agrega una línea en blanco después de cada línea.Muchas de estas respuestas son largos fragmentos de código o se leen en todo el archivo. Me gusta usar los métodos c para esta misma tarea.
Tenga en cuenta que fgetln no mantendrá su carácter de nueva línea. Además, hacemos +1 la longitud de la cadena porque queremos hacer espacio para la terminación NULL.
fuente
Para leer un archivo línea por línea (también para archivos grandes extremos) se puede hacer mediante las siguientes funciones:
O:
La clase DDFileReader que permite esto es la siguiente:
Archivo de interfaz (.h):
Implementación (.m)
La clase fue hecha por Dave DeLong
fuente
Al igual que @porneL dijo, la API de C es muy útil.
fuente
Como otros han respondido, tanto NSInputStream como NSFileHandle son buenas opciones, pero también se puede hacer de manera bastante compacta con NSData y la asignación de memoria:
BRLineReader.h
BRLineReader.m
fuente
Esta respuesta NO es ObjC sino C.
Dado que ObjC está basado en 'C', ¿por qué no usar fgets?
Y sí, estoy seguro de que ObjC tiene su propio método, pero todavía no soy lo suficientemente competente como para saber qué es :)
fuente
meta
pregunta; ¿Deberían marcarse para revisión las preguntas muy antiguas de usuarios habituales?de la respuesta de @Adam Rosenfield, la cadena de formato de
fscanf
se cambiaría de la siguiente manera:funcionará en osx, linux, terminaciones de línea de windows.
fuente
Usando categoría o extensión para hacer nuestra vida un poco más fácil.
fuente
Encontré la respuesta de @lukaswelte y el código de Dave DeLong muy útil. Estaba buscando una solución a este problema, pero necesitaba analizar archivos grandes
\r\n
no solo\n
.El código tal como está escrito contiene un error si se analiza por más de un carácter. He cambiado el código de la siguiente manera.
archivo .h:
archivo .m:
fuente
Estoy agregando esto porque todas las otras respuestas que probé se quedaron cortas de una manera u otra. El siguiente método puede manejar archivos grandes, líneas largas arbitrarias, así como líneas vacías. Se ha probado con contenido real y eliminará el carácter de nueva línea de la salida.
El crédito va para @Adam Rosenfield y @sooop
fuente
Veo que muchas de estas respuestas se basan en leer todo el archivo de texto en la memoria en lugar de tomarlo un fragmento a la vez. Aquí está mi solución en Swift agradable y moderno, usando FileHandle para mantener bajo el impacto de la memoria:
Tenga en cuenta que esto conserva el retorno de carro al final de la línea, por lo que, según sus necesidades, es posible que desee ajustar el código para eliminarlo.
Uso: simplemente abra un identificador de archivo en su archivo de texto de destino y llame
readLine
con una longitud máxima adecuada: 1024 es estándar para texto sin formato, pero lo dejé abierto en caso de que sepa que será más corto. Tenga en cuenta que el comando no desbordará el final del archivo, por lo que es posible que tenga que verificar manualmente que no lo haya alcanzado si tiene la intención de analizar todo. Aquí hay un código de muestra que muestra cómo abrir un archivomyFileURL
y leerlo línea por línea hasta el final.fuente
Aquí hay una buena solución simple que uso para archivos más pequeños:
fuente
Use este script, funciona muy bien:
fuente