On peut dire qu'en Objective-C, les processus de lecture et d'écriture dans un fichier sont beaucoup moins divers qu'en C. En effet, en C, on peut ouvrir un fichier en mode texte, récupérer son contenu ou encore écrire dans ce fichier, mais aussi le manipuler en mode binaire, en récupérer les données ou en écrire, tout cela avec très peu de fonctions « couteau suisse ». En effet, la fonction qui écrit un
int dans un fichier est la même que celle qui écrit un
double.
En Objective-C, il existe une fonction pour chaque variable « normale » (par exemple un
int), mais aussi pour chaque objet. En fait, beaucoup d'objets du
framework Foundation savent s'écrire eux-mêmes dans des fichiers.
C'est le cas de la classe
NSString, même si son cas est particulier, car elle permet de lire et d'écrire dans un fichier en mode texte, en utilisant des caractères.
Initialiser une chaîne à partir d'un fichier
Cette fois-ci, on va étudier le prototype d'une fonction permettant de créer une chaîne à partir du contenu d'un fichier :
Code : Objective-C | + (id) stringWithContentsOfFile:(NSString *) path
encoding:(NSStringEncoding) enc
error:(NSError **) error
|
Cette méthode retourne un objet
id, pas de surprise de ce côté-là. Le premier paramètre
path est une chaîne de caractères représentant le chemin du fichier. Il suffit de donner une chaîne du type
@"images/fichier.png".
Le deuxième paramètre est déjà plus intriguant. Il n'y a pas d'étoile entre les parenthèses, ce n'est donc pas un objet. En fait,
NSStringEncoding représente un entier positif. Il faut donc fournir un nombre en paramètre pour indiquer le
codage de caractères, c'est-à-dire la façon dont les caractères sont écrits dans le fichier.
Afin de simplifier la vie du développeur, des constantes ont été définies pour chacun de ces nombres. Ainsi,
NSASCIIStringEncoding remplace l'entier 1,
NSUTF8StringEncoding l'entier 4, etc.
Il est impératif de fournir un type de codage à l'appel de cette méthode.
Mais lequel choisir ?
La liste complète peut être trouvée
ici. Pour la langue française, il est recommandé d'utiliser le codage Latin1 (
NSISOLatin1StringEncoding) ou l'UTF-8 (
NSUTF8StringEncoding). J'utiliserai ce dernier tout au long du tutoriel.
Le troisième paramètre n'est pas vraiment un objet non plus. Les deux étoiles indiquent que la méthode attend un pointeur sur un pointeur.

Comme son nom l'indique, cet objet
error sert à récupérer les éventuelles erreurs lors de la lecture du fichier. Comme la méthode retourne un
id (elle retourne
nil si l'objet n'a pas pu être créé), on ne peut pas retourner un objet contenant l'erreur. Il faut donc donner l'adresse d'un pointeur pour que la méthode puisse modifier l'objet.
Il est pourtant toujours possible de modifier un objet passé en paramètre. Alors pourquoi passer l'adresse d'un pointeur ?
C'est vrai, mais pas avec cette fonction-là. Un schéma s'impose.

Voici le fonctionnement de la fonction en cas d'erreur, si on donnait un objet en paramètre comme on le fait d'habitude.
Souvenez-vous, la valeur d'une variable est copiée lorsqu'elle est passée à une fonction.
Vous voyez le problème ? Le pointeur
erreur passé en paramètre ne sera pas modifié par la fonction. Voici à présent ce qui se passe si on donne l'adresse du pointeur.
La fonction manipule le pointeur
p_erreur_copie, ce qui correspond à manipuler
erreur. Quand nous récupérons
p_erreur, nous avons bien le même objet
erreur que celui qui a été manipulé par la fonction.
Pour résumer, voici la méthode pour récupérer le contenu (et tout le contenu !) d'un fichier dans une
NSString :
Code : Objective-C | NSError * erreur = nil;
NSString * contenu = [NSString stringWithContentsOfFile:@"documents/fichier.txt"
encoding:NSUTF8StringEncoding
error:&erreur]; // On donne l'adresse de "erreur"
|
Oui, on peut écrire un message sur plusieurs lignes, ça fait plus joli et c'est plus lisible pour les longs messages.
Il s'agit de la meilleure façon de passer les paramètres. Souvent, on ne se soucie pas des erreurs et on passe NULL (attention, pas nil) en paramètre pour error:.
Ainsi donc, notre chaîne de caractères
contenu contient
tout le fichier
@"documents/fichier.txt". Cette méthode existe aussi en version
init..., comme d'habitude.
Il existe une seconde méthode similaire accomplissant le même travail. Voici son prototype :
Code : Objective-C | + (id) stringWithContentsOfURL:(NSURL *) url
encoding:(NSStringEncoding) enc
error:(NSError **) error
|
Comme vous le voyez, on n'est pas très loin de l'autre méthode, à l'exception du premier paramètre. Cette fois, on doit donner une instance de
NSURL à la méthode. On peut donc initialiser cette instance à l'aide des méthodes vues tout à l'heure :
Code : Objective-C | NSURL * cheminURL = [NSURL fileURLWithPath:@"documents/fichier.txt"];
NSString * contenu = [NSString stringWithContentsOfURL:cheminURL
encoding:NSUTF8StringEncoding
error:NULL];
// On passe NULL en paramètre pour ne pas se préoccuper des erreurs
|
Voilà, vous savez lire un fichier texte !
Tout cela est bien joli, mais tout un fichier dans une seule chaîne, ce n'est pas vraiment pratique ! Comment faire si on ne veut lire qu'une seule ligne ? Ou caractère par caractère ? Ou pour lire une chaîne formatée, comme avec fscanf() ?
L'avantage de cette technique, c'est qu'on n'a justement plus besoin de se soucier de ça ! Une fois la chaîne de caractères créée, il suffit de la parcourir pour se balader dans le fichier. Par exemple :
Code : Objective-C | char c = 0;
int i;
for (i = 0 ; i < [contenu length] && c != '\n' ; i++) {
c = [contenu characterAtIndex:i];
// Du code...
}
|
Dans le cas présent, on lit la première ligne caractère par caractère.

Étant donné que la classe
NSString propose beaucoup de méthodes très pratiques, on peut facilement manipuler le contenu d'un fichier.
Bon, je l'admets, on ne retrouve pas de fonction similaire à
fscanf() dans la documentation de
NSString. En revanche, si vous voulez vraiment analyser une chaîne formatée, je vous laisse regarder du côté de la classe
NSScanner.
Écrire dans un fichier
Bien entendu, si vous créez une instance de
NSMutableString à partir d'un fichier et que vous la modifiez, le fichier restera inchangé. Pour mettre à jour le fichier, il faut écrire cette chaîne dans ce fichier. C'est possible grâce à la méthode que voici :
Code : Objective-C | - (BOOL) writeToFile:(NSString *) path
atomically:(BOOL) useAuxiliaryFile
encoding:(NSStringEncoding) enc
error:(NSError **) error
|
La méthode retourne un booléen, qui vaut
YES si l'écriture a pu se faire, et
NO s'il y a eu un problème.
Vous devez certainement reconnaître les paramètres 1, 3 et 4 : ce sont les mêmes que lors de la lecture. Le second paramètre est intéressant. Si vous fournissez
YES, l'écriture se fera dans un fichier distinct (c'est-à-dire différent de
path), qui sera ensuite renommé pour remplacer
path. Le fichier temporaire est donc effacé. Si vous fournissez
NO, l'écriture se fait directement dans le fichier
path. L'avantage de fournir
YES est qu'en cas de
crash inopiné, le contenu de
path restera inchangé.
Quand vous écrivez une chaîne dans un fichier, le contenu de celui-ci est écrasé et remplacé par la chaîne !
Comme pour la lecture, il existe la même méthode attendant une instance de
NSURL :
Code : Objective-C | - (BOOL) writeToURL:(NSURL *) url
atomically:(BOOL) useAuxiliaryFile
encoding:(NSStringEncoding) enc
error:(NSError **) error
|
Essayez de mettre cela en pratique !
