3 Classes NET d'usage courant
3.6 Les fichiers binaires
Les classes System.IO.BinaryReader et System.IO.BinaryWriter servent à lire et écrire des fichiers binaires. Considérons l'application suivante :
// syntaxe pg texte bin logs
// le fichier texte a des lignes de la forme nom : age qu'on rangera dans une structure string, int // (logs) est un fichier texte de logs
Le fichier texte a le contenu suivant : 1. paul : 10 2. helene : 15 3. 4. jacques : 11 5. sylvain : 12 6. xx : -1 7. 8. xx: yy : zz 9. xx : yy
Le programme est le suivant : 1. using System; 2. using System.IO; 3.
4. // syntaxe pg texte bin logs
5. // on lit un fichier texte (texte) et on range son contenu dans un fichier binaire (bin)
6. // le fichier texte a des lignes de la forme nom : age qu'on rangera dans une structure string, int
7. // (logs) est un fichier texte de logs 8.
9. namespace Chap3 { 10. class Program {
11. static void Main(string[] arguments) { 12. // il faut 3 arguments
13. if (arguments.Length != 3) {
14. Console.WriteLine("syntaxe : pg texte binaire log");
15. Environment.Exit(1);
16. }//if
17.
18. // variables 19. string ligne=null; 20. string nom=null; 21. int age=0;
22. int numLigne = 0;
23. char[] séparateurs = new char[] { ':' }; 24. string[] champs=null;
25. StreamReader input = null;
26. BinaryWriter output = null;
27. StreamWriter logs = null;
28. bool erreur = false;
29. // lecture fichier texte - écriture fichier binaire 30. try {
31. // ouverture du fichier texte en lecture 32. input = new StreamReader(arguments[0]); 33. // ouverture du fichier binaire en écriture
34. output = new BinaryWriter(new FileStream(arguments[1], FileMode.Create, FileAccess.Write));
35. // ouverture du fichier des logs en écriture 36. logs = new StreamWriter(arguments[2]); 37. // exploitation du fichier texte
38. while ((ligne = input.ReadLine()) != null) { 39. // une ligne de plus
40. numLigne++; 41. // ligne vide ? 42. if (ligne.Trim() == "") { 43. // on ignore 44. continue; 45. }
46. // une ligne nom : age
47. champs = ligne.Split(séparateurs); 48. // il nous faut 2 champs
49. if (champs.Length != 2) {
50. // on logue l'erreur
51. logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nombre de champs incorrect", numLigne, arguments[0]);
52. // ligne suivante
53. continue;
54. }//if
55. // le 1er champ doit être non vide 56. erreur = false;
57. nom = champs[0].Trim(); 58. if (nom == "") {
59. // on logue l'erreur
60. logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nom vide", numLigne, arguments[0]);
61. erreur = true;
62. }
63. // le second champ doit être un entier >=0 64. if (!int.TryParse(champs[1],out age) || age<0) {
65. // on logue l'erreur
66. logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un âge [{2}] incorrect", numLigne, arguments[0], champs[1].Trim());
67. erreur = true;
68. }//if
69. // si pas d'erreur, on écrit les données dans le fichier binaire
70. if (!erreur) { 71. output.Write(nom); 72. output.Write(age); 73. } 74. // ligne suivante 75. }//while
76. }catch(Exception e){
77. Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message); 78. } finally {
79. // fermeture des fichiers 80. if(input!=null) input.Close(); 81. if(output!=null) output.Close(); 82. if(logs!=null) logs.Close();
83. }
84. }
85. } 86. }
Attardons-nous sur les opérations concernant la classe BinaryWriter : ligne 34 : l'objet BinaryWriter est ouvert par l'opération
output=new BinaryWriter(new FileStream(arguments[1],FileMode.Create,FileAccess.Write)); L'argument du constructeur doit être un flux (Stream). Ici c'est un flux construit à partir d'un fichier (FileStream) dont on donne :
o le nom
o l'opération à faire, ici FileMode.Create pour créer le fichier
o le type d'accès, ici FileAccess.Write pour un accès en écriture au fichier lignes 70-73 : les opérations d'écriture
// on écrit les données dans le fichier binaire output.Write(nom);
output.Write(age);
La classe BinaryWriter dispose de différentes méthodes Write surchargées pour écrire les différents types de données simples ligne 81 : l'opération de fermeture du flux
output.Close();
Les trois arguments de la méthode Main sont donnés au projet (via ses propriétés) [1] et le fichier texte à exploiter est placé dans le dossier bin/Release [2] :
Avec le fichier [personnes1.txt] suivant : 1. paul : 10 2. helene : 15 3. 4. jacques : 11 5. sylvain : 12 6. xx : -1 7. 8. xx: yy : zz 9. xx : yy
les résultats de l'exécution sont les suivants :
• en [1], le fichier binaire [personnes1.bin] créé ainsi que le fichier de logs [logs.txt]. Celui-ci a le contenu suivant : 1. La ligne n° [6] du fichier [personnes1.txt] a un âge [-1] incorrect
2. La ligne n° [8] du fichier [personnes1.txt] a un nombre de champs incorrect 3. La ligne n° [9] du fichier [personnes1.txt] a un âge [yy] incorrect
Le contenu du fichier binaire [personnes1.bin] va nous être donné par le programme qui suit. Celui-ci accepte également trois arguments :
// syntaxe pg bin texte logs
// on lit un fichier binaire bin et on range son contenu dans un fichier texte (texte) // le fichier binaire a une structure string, int
// le fichier texte a des lignes de la forme nom : age // logs est un fichier texte de logs
On fait donc l'opération inverse. On lit un fichier binaire pour créer un fichier texte. Si le fichier texte produit est identique au fichier originel on saura que la conversion texte --> binaire --> texte s'est bien passée. Le code est le suivant :
1. using System; 2. using System.IO; 3.
4. // syntaxe pg bin texte logs
5. // on lit un fichier binaire bin et on range son contenu dans un fichier texte (texte) 6. // le fichier binaire a une structure string, int
1
2
7. // le fichier texte a des lignes de la forme nom : age 8. // logs est un fichier texte de logs
9.
10.namespace Chap3 { 11. class Program2 {
12. static void Main(string[] arguments) { 13. // il faut 3 arguments
14. if (arguments.Length != 3) {
15. Console.WriteLine("syntaxe : pg binaire texte log");
16. Environment.Exit(1);
17. }//if
18.
19. // variables 20. string nom = null; 21. int age = 0;
22. int numPersonne = 1;
23. BinaryReader input = null;
24. StreamWriter output = null;
25. StreamWriter logs = null;
26. bool fini;
27. // lecture fichier binaire - écriture fichier texte 28. try {
29. // ouverture du fichier binaire en lecture
30. input = new BinaryReader(new FileStream(arguments[0], FileMode.Open, FileAccess.Read)); 31. // ouverture du fichier texte en écriture
32. output = new StreamWriter(arguments[1]); 33. // ouverture du fichier des logs en écriture 34. logs = new StreamWriter(arguments[2]); 35. // exploitation du fichier binaire 36. fini = false; 37. while (!fini) { 38. try { 39. // lecture nom 40. nom = input.ReadString().Trim(); 41. // lecture age 42. age = input.ReadInt32();
43. // écriture dans fichier texte 44. output.WriteLine(nom + ":" + age); 45. // personne suivante 46. numPersonne++; 47. } catch (EndOfStreamException) { 48. fini = true; 49. } catch (Exception e) {
50. logs.WriteLine("L'erreur suivante s'est produite à la lecture de la personne n° {0} : {1}", numPersonne, e.Message);
51. }
52. }//while
53. } catch (Exception e) {
54. Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message); 55. } finally {
56. // fermeture des fichiers 57. if (input != null) 58. input.Close(); 59. if (output != null) 60. output.Close(); 61. if (logs != null) 62. logs.Close(); 63. } 64. } 65. } 66. }
Attardons-nous sur les opérations concernant la classe BinaryReader : ligne 30 : l'objet BinaryReader est ouvert par l'opération
input=new BinaryReader(new FileStream(arguments[0],FileMode.Open,FileAccess.Read));
L'argument du constructeur doit être un flux (Stream). Ici c'est un flux construit à partir d'un fichier (FileStream) dont on donne :
• le nom
• l'opération à faire, ici FileMode.Open pour ouvrir un fichier existant • le type d'accès, ici FileAccess.Read pour un accès en lecture au fichier
lignes 40, 42 : les opérations de lecture nom=input.ReadString().Trim();
age=input.ReadInt32();
La classe BinaryReader dispose de différentes méthodes ReadXX pour lire les différents types de données simples ligne 60 : l'opération de fermeture du flux
input.Close();
Si on exécute les deux programmes à la chaîne transformant personnes1.txt en personnes1.bin puis personnes1.bin en personnes2.txt2 on obtient les résultats suivants :
• en [1], le projet est configuré pour exécuter la 2ième application • en [2], les arguments passés à Main
• en [3], les fichiers produits par l'exécution de l'application. Le contenu de [personnes2.txt] est le suivant :
1. paul:10 2. helene:15 3. jacques:11 4. sylvain:12