# ouverture de fichier en C



## HommeCocoa (20 Juillet 2004)

Bonjour,

J'essaie d'écrire un petit fichier en C qui inscrit des valeurs dans un fichier texte précédemment créé. J'ai un livre sur le C ou il expliquent comment et j'ai aussi trouvé des tutoriaux:
http://membres.lycos.fr/macbuilder/tutoriaux/tut_c/

J'ai alors copié le code du tutoriel

#include <stdio.h>


int main (int argc, const char * argv[])
{
	FILE *f;
	f=fopen("essai.data", "r");
	if (f == NULL)
	{
		printf("erreur d'ouverture du fichier\n");
	}
}

Mais l'écriture du fichier échoue à chaque fois. J'ai essayé un autre code trouvé dans un bouquin mais c'est le meme problème.

Quelqu'un peut-il m'expliquer le problème?
Merci d'avance

David


----------



## tatouille (20 Juillet 2004)

```
#include <stdio.h>

int main() {
  FILE *sourceFile;
  FILE *destinationFile;
  char *buffer;
  int n;

  sourceFile = fopen("file.c", "r");
  destinationFile = fopen("file2.c", "w");

  if(sourceFile==NULL) {
    printf("Error: can't access file.c.\n");
    return 1;
  }
  else if(destinationFile==NULL) {
    printf("Error: can't create file for writing.\n");
    return 1;
  }
  else {
    n = fread(buffer, 1, 1000, sourceFile);
    fwrite(buffer, 1, n, destinationFile);
    fclose(sourceFile);
    fclose(destinationFile);

    destinationFile = fopen("file2.c", "r");
    n = fread(buffer, 1, 1000, destinationFile);
    printf("%s", buffer);
    fclose(destinationFile);
    return 0;
  }
}
```

..........w==write
..........r==read


et tu peux composer destinationFile = fopen("file2.c", "rw");

j'ai passé quelques étapes après c'est la function fscanf dont tu vas avoir besoin


----------



## canari (20 Juillet 2004)

réponse peu complête, mais de mémoire, "r" c'est pour lire,  essaye avec "w" pour écrire


----------



## HommeCocoa (20 Juillet 2004)

merci bien! je vais essayer ça!


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

Il y  a une petite erreur dans ton exemple Tatouille :
Tu écrit :

  sourceFile = fopen("file.c", "r");
  destinationFile = fopen("file2.c", "w");

  if(sourceFile==NULL) 
  {
    printf("Error: can't access file.c.\n");
    return 1;
  }
  else if(destinationFile==NULL) {
    printf("Error: can't create file for writing.\n");
    return 1;
  }

Or si tu arrive a ouvrir la source et pas la destination, ta source reste ouverte et tu perd un descripteur de fichier. Comme le nombre de descripteur de fichier est limité (par session de la meme application) tu va saturer et au bout d'un moment plus aucun fichier ne pourra etre ouvert.
Ensuite le "else" ne sert a rien puisque le "return" rends la main a l'appelant.

Je prefererait donc :

  sourceFile = fopen("file.c", "r");

  if(sourceFile==NULL) 
  {
    printf("Error: can't access file.c.\n");
    return 1;
  }
  destinationFile = fopen("file2.c", "w");
   if(destinationFile==NULL) 
   {
    fclose(sourceFile); /* Je ferme le fichier source */
    printf("Error: can't create file for writing.\n");
    return 1;
  }

Ensuite, plutot que de retourner (1) dans le cas d'une erreur, et (0) quand tout est bon, il pourrait etre intéressant d'utiliser "errno" qui donne un numéro d'erreur.

Cordialement


----------



## HommeCocoa (21 Juillet 2004)

Maintenant que ça marche, je me suis amusé avec ceci:

#include <stdio.h>

int i;
int res;

int main()
{

	FILE *destinationFile;

	destinationFile = fopen("file.txt", "w");

	if(destinationFile==NULL)
	{
		printf("Error: can't create file for writing.\n");
		return 1;
	}
	else 
	{
		fprintf(destinationFile, "Liste des carres des nombres de 1 a 1000\n");
		for (i=0; i<10; i++)
		{
			res = i*i;
			fprintf(destinationFile, "%d * %d = %d\n", i, i, res);
		}
		fclose(destinationFile);
		return 0;
	}
} 

ce petit programme ouvre un fichier txt et y inscrit les dix premier carré (1*1=1  2*2=4 ...).
Si je clique sur Build and Run, tout se passe bien, le fichier se crée et les nombres s'inscrivent, mais si je supprime le fichier créé et que je lance mon application dans le terminal, elle ne parvient pas à recréer le fichier mais ne me signial pas d'erreur.

Là encore quelqu'un peut-il m'éclairer?
Merci!
David


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

La seule irrégularité que je vois pour l'instant est l'utilisation du type "int". A proscrire car la précision de "int" varie selon les systemes (en general 32 bits mais cela peut varier et rends donc ton source non portable)
Utilise donc "short" si tu veut un nombre signé sur 15 bits, "long" signé sur 31 bits (1 bit est reserve au signe.

La sequence %d doit être adaptée : %d si tu utilise un short, %ld si tu utilise un long.


Pour ton probleme de fichier non créé, fait "ls -l" sous le terminal pour visualiser les fichiers de ton dossier. Parfois le Finder souffre de probleme de raffraichissement.

Cordialement


----------



## molgow (21 Juillet 2004)

Didier Guillion a dit:
			
		

> Ensuite, plutot que de retourner (1) dans le cas d'une erreur, et (0) quand tout est bon, il pourrait etre intéressant d'utiliser "errno" qui donne un numéro d'erreur.



Il existe aussi les constantes _EXIT_FAILURE_ et _EXIT_SUCCESS_ de la librairie _stdlib.h_. Elles sont plutôt à utiliser en association avec la fonction _exit(int statuts)_ de cette même librairie.


----------



## HommeCocoa (21 Juillet 2004)

mis à part ce problème, j'ai augmenté le nombre de passage dans la boucle for à quelques millions...

Par conséquent j'ai un joli petit fichier txt rempli de carré... le tout ne pesant pas moins de 500Mo loool...

Si quelqu'un a une bonne connection et est intéressé par des carrés en vrac, qu'il me fasse signe..


----------



## HommeCocoa (21 Juillet 2004)

Didier Guillion a dit:
			
		

> Bonjour,
> 
> La seule irrégularité que je vois pour l'instant est l'utilisation du type "int". A proscrire car la précision de "int" varie selon les systemes (en general 32 bits mais cela peut varier et rends donc ton source non portable)
> Utilise donc "short" si tu veut un nombre signé sur 15 bits, "long" signé sur 31 bits (1 bit est reserve au signe.
> ...



J'ai adopté le type float, est-ce aussi adapté?

#include <stdio.h>

float i;
float res;

int main()
{

	FILE *destinationFile;

	destinationFile = fopen("file.txt", "w");

	if(destinationFile==NULL)
	{
		printf("Error: can't create file for writing.\n");
		return 1;
	}
	else 
	{
		fprintf(destinationFile, "Liste des carres des nombres de 1 a 1000\n");
		for (i=0; i<10000000; i++)
		{
			res = i*i;
			fprintf(destinationFile, "%f * %f = %f\n", i, i, res);
		}
		fclose(destinationFile);
		return 0;
	}
}


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

Oui, le type "float" est parfaitement compatible.
A noter que tu peut parametrer l'affichage du %f en utilisant par exemple %.2f qui va te donner deux chiffres apres la virgule.


Une remarque infime, si tu déclare tes variables en dehors du corps de la fonction "main" elles sont globales, ce n'est pas génant, mais pas vraiment une bonne habitude a prendre.

Ecrit donc plutot :


 int main()
 {
float i;
float res;
 FILE *destinationFile;

...

Et pour ton probleme de génération de fichier ?

Cordialement


----------



## HommeCocoa (21 Juillet 2004)

Oui effectivement je vais déclarer mes variables en local.

Pour ce qui est de la génération de fichier j'ai été voir avec ls-l mais il y a toujours rien... Je comprend pas très bien pourquoi.


----------



## HommeCocoa (21 Juillet 2004)

!!!!! Etrange, après une nouvelle tentative cela semble fonctionner, le fichier texte est effectivement créé... Bizarre

Mis à part ça, j'ai plusieur autres questions, quelle est la différence entre le mode "development" et le mode "deployement" qu'il est possible de spécifier dans Xcode?

Et sinon, je ne comprend toujours pas pourquoi mon premier code ne marchait pas:

int main (int argc, const char * argv[])
{
FILE *f;
f=fopen("essai.data", "r");
if (f == NULL)
{
printf("erreur d'ouverture du fichier\n");
}
}
 Chez moi, il me donne toujours le message d'erreur d'ouverture.

Merci encore!


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

Je pense que la difference entre les deux modes est due au niveau d'optimisation.
En general on developpe un code non optimisé car c'est plus facile a debogger.
Puis, on genere une version diffusable optimisée pleinement.

A noter : toujours refaire les tests sur la version  optimisée, certaines options d'optimsation peuvent se "planter".

Pour ta deuxieme question, le fichier "essai.data" est til present au meme niveau que l'executable ? Si non, c'est normal qu'il ne puisse s'ouvrir...

Cordialement


----------



## HommeCocoa (21 Juillet 2004)

Oui il était censé se créer au niveau de l'exécutable. Mais ne se créait pas.
J'ai changé son extention pour du .txt et le type, je l'ai mis en "w" et là du coup le fichier s'ouvre correctement, cela provient de quoi?


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

Quand tu utilise "fopen" (file open=ouverture de fichier) avec comme parametre "r" (read=lecture) le fichier doit etre présent sur le disque dur.
Dans ce type d'ouverture tu peut seulement lire sur le descripteur de fichier par "getc","fscanf" par exemple.

Quand tu utilise "w" (write=ecriture) le fichier est créé, si il existe deja son contenu est remis à zero. 
Dans ce type d'ouverture tu peut seulement écrire sur le descripteur de fichier par "putc","fprintf" par exemple.

Des modes te permettent d'ouvrir en lecture ET écriture mais c'est une autre histoire.

Cordialement


----------



## HommeCocoa (21 Juillet 2004)

haaa d'accord, je ne savais pas ça. C'est donc un peu plus clair pour moi maintenant merci!


----------



## HommeCocoa (21 Juillet 2004)

Je continue dans mes petits essais et je voulais créer un programme détérminant des nombres premier puis les écrires dans un fichiers. La première partie fut de trouver un algorithme pour trouver ces nombres premiers. J'ai pensé à ceci:


#include <stdio.h>
#define cste 200

int main (int argc, const char * argv[])
{
	int nombre;
	int i;
	int mod;


	for(nombre=1;nombre<cste;nombre++)
	{
		i = 2;
		mod = nombre % i;


		while ((mod!=0) || (i<nombre))
		{
		i++;
		mod = nombre % i;		
		}

		if(i==nombre)
		{
			printf("Ceci est un nombre premier %d\n\n", nombre);
		}


	}
    return 0;
}


Mais malheureusement cela ne fonctionne pas et j'ai un peu du mal à le débugger...
le while est-il bien une boucle TANTQUE?

le principe étant que pour chaque nombre présenté, la boucle le divise par des nombres croissant (variable i) et regarde si il y a un reste et ceci TANTQUE il trouve un reste OU que le nombre croissant est plus petit que le nombre à tester. Donc il sort de la boucle soit parce qu'il a ttrouvé un diviseur qui ne produit soit de reste soit parce qu'il est arrivé à concurrence du nombre lui même. Puis par un teste on repere de quel facon il est sorti de la boucle... Bon je sais mon algo est un peu mal exprimé...


----------



## HommeCocoa (21 Juillet 2004)

Oups je viens de m'apercevoir de ma boulette, en faite il s'agissait simplement de la condition de sortie de la boucle. Voici une version qui semble fonctionner:

#include <stdio.h>
#define cste 10000

int main (int argc, const char * argv[])
{
	int nombre;
	int i;
	int mod;


	for(nombre=2;nombre<cste;nombre++)
	{
		i = 2;
		mod = nombre % i;


		while (mod!=0)
		{
		i++;
		mod = nombre % i;		
		}


		if(i==nombre)
		{
			printf("Ceci est un nombre premier %d\n\n", nombre);
		}


	}
    return 0;
}

Cela me sort effectivement des nombres premiers... Par contre un pot m'avais filé un code qui semble beaucoup plus performant. Pour avoir testé les deux, le sien est beaucoup plus rapide, mais je comprend moins bien le principe:

#include <stdio.h>
#define n 10000

int main (int argc, const char * argv[])
{
	int Nombres[n];
	int i;
	int j;

	for(i=0;i<n;i++)
		Nombres_ = i;

	Nombres[0] = Nombres[1] = Nombres[2] = 0;

	for(i=2;i<n;i++)
		for(j=i*2;j<n;j+=i)
			Nombres[j] = 0;

	for(i=0;i<n;i++)
		if(Nombres != 0)
			printf("%d\n", Nombres);

	return 0;
}


David_


----------



## Didier Guillion (21 Juillet 2004)

Bonjour,

Quelques remarques :

- Usage de "int" dont nous avons deja parlé.
- Création d'un tableau de 10000 elements sur la pile (10000*4=40000 octets) pas glop.
J'aurait plutot ecrit :

/* Declaration du pointeur sur les nombres */
long * Nombres;

/* Allocation de 10000 elements */
Nombres=(long*) malloc(sizeof(long)*10000);
/* L'allocation s'est bien passée ? */
if(Nombres !=NULL)
 {
 /* Travail sur le tableau (cela ne change pas) */
 ...

 /* Liberation du tableau */
 free(Nombres);
 }
A noter que "10000" gagnerait à ete placé en constante comme dans ton premier exemple.

Cordialement


----------



## HommeCocoa (21 Juillet 2004)

En faite, le deuxieme code n'est pas de moi, mais c'est vrai que je pourrais lui porter ces améliorations. Par contre, j'ai un peu de peine à comprendre le faite qu'il soit tellement plus rapide que le mien à l'execution.


----------



## la tortue (21 Juillet 2004)

HommeCocoa a dit:
			
		

> En faite, le deuxieme code n'est pas de moi, mais c'est vrai que je pourrais lui porter ces améliorations. Par contre, j'ai un peu de peine à comprendre le faite qu'il soit tellement plus rapide que le mien à l'execution.



Toi tu recherches les nombres dont les seuls diviseurs sont 1 et lui-même, avec des divisions euclidiennes. 
Lui recherche les nombres qui ne sont pas multiple d'un autre nombre, avec des additions uniquement:en gros il supprime de sa liste les multiples d'autres nombres. 
Dans ton cas, tu fais de nombreuses divisions euclidiennes (calcul pas si simple) et dans son cas, il n'y a aucun calcul (ou presque).


----------



## tatouille (21 Juillet 2004)

Didier Guillion a dit:
			
		

> Bonjour,
> 
> Il y  a une petite erreur dans ton exemple Tatouille :
> Tu écrit :
> ...



oui j'étais parti dans un truc j'ai viré des lignes rapidemment
pour simplifier


----------



## arnolix (23 Juillet 2004)

Pour ce qui est de ton code moins optimisé :
celui de ton pote est la traduction de la méthode du crible d'Erathostène. C'est un code efficace mais qui nécesite la création d'une table qui à la taille du plus grand nombre premier à tester. Donc pas question de l'utiliser pour des tests de primalité sur des entiers > à 10^10 par exemple. Le principe est de cocher tous les multiples dans la table, puis on recommence. Les entiers qui restent sont ceux qui ne sont multiples d'aucun entier autre que 1, c'est à dire les premiers.

Si le tien est peu efficace en voici quelques raisons : si tu cherches les diviseurs d'un  entier n tu peux te limiter à ceux inférieurs ou = à sqrt(n). En effet si n a des facteurs ? 1, et donc au moins deux facteurs, les deux sont inférieurs ou = à sqrt(n), sinon t'aurai un produit > à n.
De plus, quand par exemple 2 ne divise pas n, alors forcément 4 non plus : il faut donc éliminer les multiples de tes diviseurs qui échouent, ce qui devient très compliqué.

A propos de ton offre généreuse de table de 10 000 carrés sache qu'il vaut mieux éléver 10 000 nombres au carré qu'un seul enfant au biberon  (c'est pas de moi, c'est d'Alphonse Allais).


----------



## tatouille (23 Juillet 2004)

arnolix a dit:
			
		

> A propos de ton offre généreuse de table de 10 000 carrés sache qu'il vaut mieux éléver 10 000 nombres au carré qu'un seul enfant au biberon  (c'est pas de moi, c'est d'Alphonse Allais).



 je pensais pas que fopen ferait 2 pages   

merde j'ai fait un x%2==1


----------

