141 votes

Objective-C : Lecture d'un fichier ligne par ligne

Quelle est la manière appropriée de traiter les gros fichiers texte en Objective-C ? Disons que je dois lire chaque ligne séparément et que je veux traiter chaque ligne comme une NSString. Quelle est la manière la plus efficace de le faire ?

Une solution consiste à utiliser la méthode NSString :

+ (id)stringWithContentsOfFile:(NSString *)path 
      encoding:(NSStringEncoding)enc 
      error:(NSError **)error

puis diviser les lignes avec un séparateur de nouvelle ligne, et enfin itérer sur les éléments du tableau. Cependant, cela semble assez inefficace. N'y a-t-il pas un moyen simple de traiter le fichier comme un flux, en énumérant chaque ligne, au lieu de tout lire en une seule fois ? Un peu comme le java.io.BufferedReader de Java.

93voto

Yoon Lee Points 1180

Cela fonctionnera pour la lecture générale d'une chaîne de caractères à partir d'un texte. Si vous souhaitez lire un texte plus long (grande taille de texte), utilisez la méthode que d'autres personnes ont mentionnée ici, comme la mise en mémoire tampon (réserve de la taille du texte dans l'espace mémoire).

Disons que vous lisez un fichier texte.

NSString* filePath = @""//file path...
NSString* fileRoot = [[NSBundle mainBundle] 
               pathForResource:filePath ofType:@"txt"];

Vous voulez vous débarrasser de la nouvelle ligne.

// read everything from text
NSString* fileContents = 
      [NSString stringWithContentsOfFile:fileRoot 
       encoding:NSUTF8StringEncoding error:nil];

// first, separate by new line
NSArray* allLinedStrings = 
      [fileContents componentsSeparatedByCharactersInSet:
      [NSCharacterSet newlineCharacterSet]];

// then break down even further 
NSString* strsInOneLine = 
      [allLinedStrings objectAtIndex:0];

// choose whatever input identity you have decided. in this case ;
NSArray* singleStrs = 
      [currentPointString componentsSeparatedByCharactersInSet:
      [NSCharacterSet characterSetWithCharactersInString:@";"]];

Voilà, c'est fait.

64voto

Quinn Taylor Points 29688

C'est une excellente question. Je pense que @Diederik a une bonne réponse, bien qu'il soit regrettable que Cocoa ne dispose pas d'un mécanisme pour faire exactement ce que vous voulez faire.

NSInputStream vous permet de lire des morceaux de N octets (très similaire à java.io.BufferedReader ), mais vous devez le convertir en un fichier de type NSString par vous-même, puis recherchez les nouvelles lignes (ou tout autre délimiteur) et enregistrez les caractères restants pour la prochaine lecture, ou lisez plus de caractères si une nouvelle ligne n'a pas encore été lue. ( NSFileHandle vous permet de lire un NSData que vous pouvez ensuite convertir en un NSString mais il s'agit essentiellement du même processus).

Apple a un Guide de programmation en continu qui peuvent aider à remplir les détails, et cette question SO peut aussi aider si vous avez à faire avec uint8_t* tampons.

Si vous êtes amené à lire fréquemment des chaînes de caractères de ce type (en particulier dans différentes parties de votre programme), il serait bon d'encapsuler ce comportement dans une classe qui peut gérer les détails pour vous, ou même de sous-classer la classe NSInputStream (c'est conçus pour être sous-classés ) et en ajoutant des méthodes qui vous permettent de lire exactement ce que vous voulez.

Pour information, je pense que ce serait une fonctionnalité intéressante à ajouter, et je vais déposer une demande d'amélioration pour quelque chose qui rende cela possible :-)


Edit : Il s'avère que cette demande existe depuis quelques années. Il existe un Radar datant de 2006 à ce sujet (rdar://4742914 pour les personnes internes à Apple).

34voto

Adam Rosenfield Points 176408

Cela devrait faire l'affaire :

#include <stdio.h>

NSString *readLineAsNSString(FILE *file)
{
    char buffer[4096];

    // tune this capacity to your liking -- larger buffer sizes will be faster, but
    // use more memory
    NSMutableString *result = [NSMutableString stringWithCapacity:256];

    // Read up to 4095 non-newline characters, then read and discard the newline
    int charsRead;
    do
    {
        if(fscanf(file, "%4095[^\n]%n%*c", buffer, &charsRead) == 1)
            [result appendFormat:@"%s", buffer];
        else
            break;
    } while(charsRead == 4095);

    return result;
}

Utilisez comme suit :

FILE *file = fopen("myfile", "r");
// check for NULL
while(!feof(file))
{
    NSString *line = readLineAsNSString(file);
    // do stuff with line; line is autoreleased, so you should NOT release it (unless you also retain it beforehand)
}
fclose(file);

Ce code lit les caractères non rectilignes du fichier, jusqu'à 4095 à la fois. Si vous avez une ligne qui est plus longue que 4095 caractères, il continue à lire jusqu'à ce qu'il trouve une nouvelle ligne ou la fin du fichier.

Note : Je n'ai pas testé ce code. Veuillez le tester avant de l'utiliser.

12voto

porneL Points 42805

Mac OS X est Unix, Objective-C est un sur-ensemble de C, vous pouvez donc utiliser la bonne vieille méthode. fopen et fgets de <stdio.h> . Son fonctionnement est garanti.

[NSString stringWithUTF8String:buf] convertira la chaîne C en NSString . Il existe également des méthodes pour créer des chaînes de caractères dans d'autres codages et pour créer sans copier.

9voto

diederikh Points 17459

Vous pouvez utiliser NSInputStream qui possède une implémentation de base pour les flux de fichiers. Vous pouvez lire des octets dans un tampon ( read:maxLength: méthode). Vous devez rechercher vous-même les nouvelles lignes dans le tampon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X