Code ne prends pas en compte toutes les lignes

Chaussette
Invité n'ayant pas de compte PHPfrance

01 juin 2016, 11:04

Bonjour à tous!

Je dois préparer un exercice pour mon cours de programmation qui consiste à ordonner deux fichiers par ordre alphabétique. J'ai donc écrit un truc mais le problème c'est qu'il ne lit pas tous les mots de mes fichiers.
Il s'arrête à la lettre P va savoir pourquoi. Est-ce que quelqu'un pourrait m'aider svp ?


PS : je précise que j'ai utilisé des fonctions vues au cours, je sais qu'il y a des raccourcis d'écriture mais je me suis basé exclusivement sur ce que je connaissais :)

Code : Tout sélectionner

<?php $readFile1 = fopen("fichierex1.txt", "r"); $readFile2 = fopen("fichierex2.txt", "r"); $newFile = fopen("fichiersortant.txt", "w"); $Mot1=fgets($readFile1); $Mot1=trim($Mot1); $Mot2=fgets($readFile2); $Mot2=trim($Mot2); $Mot1 = strtr($Mot1, 'ÁÀÂÄÃÅÇÉÈÊËÍÏÎÌÑÓÒÔÖÕÚÙÛÜÝ', 'AAAAAACEEEEEIIIINOOOOOUUUUY'); $Mot1 = strtr($Mot1, 'áàâäãåçéèêëíìîïñóòôöõúùûüýÿ', 'aaaaaaceeeeiiiinooooouuuuyy'); $Mot2 = strtr($Mot2, 'ÁÀÂÄÃÅÇÉÈÊËÍÏÎÌÑÓÒÔÖÕÚÙÛÜÝ', 'AAAAAACEEEEEIIIINOOOOOUUUUY'); $Mot2 = strtr($Mot2, 'áàâäãåçéèêëíìîïñóòôöõúùûüýÿ', 'aaaaaaceeeeiiiinooooouuuuyy'); while(!feof($readFile1) && !feof($readFile2)) { if($Mot1<$Mot2) { fwrite($newFile,$Mot1."\r\n"); $Mot1=fgets($readFile1); $Mot1=trim($Mot1); } else { fwrite($newFile,$Mot2."\r\n"); $Mot2=fgets($readFile2); $Mot2=trim($Mot2); } } fclose($newFile); fclose($readFile1); fclose($readFile2); echo("Opération terminée");
?>

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9783 Messages

01 juin 2016, 11:28

Bonjour,

A première vue, je dirai que tu fais une lecture ligne à ligne mais si tes 2 fichiers ne font pas le même nombre de lignes, ta boucle while va s'arrêter dès que tu atteints le fichier la fin du fichier le moins long.

J'ai bien compris que tu utilisais que les fonctions que tu avais vu en cours mais perso si j'avais à faire cela, je ferais:
1) file_get_contents() de chacun des fichiers,
2) je met le contenu de chaque ligne dans un tableau avec la fonction explode() (sur le caractère de retour à la ligne \r\n)
3) je fusionne les tableaux des 2 fichiers en 1 seul avec array_merge()
4) je fait le tri avec sort()
5) je met le résultat obtenu dans le nouveau fichier avec file_put_contents() et implode() (idem avec le caractère \r\n)
Quand tout le reste a échoué, lisez le mode d'emploi...

Chaussette
Invité n'ayant pas de compte PHPfrance

01 juin 2016, 11:29

Merci pour ta réponse !

C'est donc ça mon problème... Je vais essayer de voir ce que je peux faire.

Je note quand même ta solution, ça pourra toujours me servir en dehors. Merci

Mammouth du PHP | 1967 Messages

01 juin 2016, 11:52

Pour garder ta manière de faire, je transformerai ta boucle while en une boucle for allant de 0 à la somme des lignes des 2 fichiers (non inclu) . ainsi tu sera sur d'avoir parcouru les 2 fichiers en entier
Spols
pour les fan de rubik's cube ou pour les curieux ==> le portail francophone du rubik's cube

Eléphant du PHP | 65 Messages

01 juin 2016, 13:59

J'ai trouvé intéressant d'essayer de comprendre pourquoi ton code ne fonctionne pas.
Mes deux fichiers (bien ajouter une ligne vide à la fin de chaque fichier) :

fichier1.txt :
antoine
onche
jean
pierre

fichier2.txt
paul
jacques

Le résultat :
antoine
onche
jean
paul
jacques

Comme on peut le constater ce n'est pas classé par ordre alphabétique.
Le script ($mot1 < $mot2) compare :
antoine avec paul = antoine
onche avec paul = onche
jean avec paul = jean
pierre avec paul = paul
pierre avec jacques = jacques

Ton script trie 2 mots par ordre alphabétique et bug à la fin du fichier.

edit: j'ai remarqué un comportement que je n'arrive pas à expliquer, lorsqu'on arrive à la fin de fichier1 ou fichier2, la variable correspondante vaut '' (une string vide) et la boucle s'arrête, même si il reste des lignes dans l'autre fichier. La condition n'est pas respectée par PHP. Quelqu'un saurait m'expliquer pourquoi ?

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

01 juin 2016, 15:49

Bonjour,

Le problème de tri vient effectivement du fait que tu compares uniquement deux des mots et que tu considères le premier des deux comme étant le premier de tous en l'inscrivant immédiatement dans ton fichier de sortie. L'écriture dans le fichier ne devrait avoir lieu qu'une fois que tu as comparé ton mot à tous les autres pour être certain qu'il s'agit bien du premier.

@carte-sd : à priori je dirais que tu es bien arrivé au bout de ton fichier :) Ce n'est pas parce que tu n'utilises jamais la valeur "pierre" dans le fichier de sortie que tu n'es pas arrivé à la fin du fichier quand tu as récupéré ce prénom. Fgets() va retourner FALSE quand il arrivera au bout du fichier et afficher ou comparer cette valeur comme s'il s'agissait d'une chaîne revient à tester une chaîne vide (ou la valeur 0 pour du numérique)

A noter également le risque d'une boucle sans fin : si le fichier 1 contient "A" et que le fichier 2 contient "B", "C" et "D", la comparaison permettra de constater que "A" < "B" et écrira donc "A" dans le fichier, puis arrivera au bout du fichier 1. "B" sera ensuite comparé à "" (false retourné par fgets) et si "B" n'est pas inférieur à "", tu vas boucler sans jamais lire le reste du fichier 2
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 65 Messages

01 juin 2016, 17:08

@Ryle
Comment expliquer cela dans ce cas ?

fichier1.txt
antoine
onche
jean
pierre
jeanette
bernard

fichier2.txt
paul
jacques

Code : Tout sélectionner

<?php header('content-type:text/html; charset=utf-8'); $fp1 = fopen('fichier1.txt', 'r'); $fp2 = fopen('fichier2.txt', 'r'); $newFile = fopen('resultat.txt', 'w'); $mot1 = fgets($fp1); $mot2 = fgets($fp2); $mot1 = trim($mot1); $mot2 = trim($mot2); while (!feof($fp1) and !feof($fp2)) { if ($mot1 < $mot2): echo $mot1 . ' < ' . $mot2 . '<br />'; fwrite($newFile, $mot1 . "\r\n"); $mot1 = fgets($fp1); $mot1 = trim($mot1); else: echo $mot1 . ' > ' . $mot2 . '<br />'; fwrite($newFile, $mot2 . "\r\n"); $mot2 = fgets($fp2); $mot2 = trim($mot2); endif; } var_dump(feof($fp1)); // false var_dump(feof($fp2)); // true $mot1 = fgets($fp1); var_dump($mot1); // jeanette fclose($fp1); fclose($fp2); fclose($newFile); echo "Opération terminée"; ?>
D'après mes calculs je devrais avoir une boucle infinie mais PHP sort de la boucle alors que la condition n'est pas vérifiée.

Mammouth du PHP | 2703 Messages

01 juin 2016, 18:58

1) file_get_contents() de chacun des fichiers,
2) je met le contenu de chaque ligne dans un tableau avec la fonction explode() (sur le caractère de retour à la ligne \r\n)
plus simple avec http://php.net/manual/fr/function.file.php

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9783 Messages

01 juin 2016, 20:26

Exact bien vu ! ;)
Quand tout le reste a échoué, lisez le mode d'emploi...

Chaussette
Invité n'ayant pas de compte PHPfrance

02 juin 2016, 10:09

Bonjour à tous et merci pour vos réponses.

En effet, je n'avais pas fait attention que je partais du principe que $Mot1 était forcément plus petit que $Mot2 et que je comparais que deux mots à la fois.

Pour le problème de la dernière ligne, compter leurs nombres pourrait en effet être une solution !

Je vais essayer tout ça et je reviens vers vous.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

02 juin 2016, 11:02

@Chaussette : comme suggéré précédemment, je pense qu'il faudrait plutôt que tu parcours les deux fichiers indépendamment l'un de l'autre pour obtenir tous les noms dont tu as besoin et ensuite seulement commencer à les trier :)



@carte-sd : c'est effectivement un problème dans l'expression de la condition :) (enfin pas vraiment un problème puisque sa résolution engendrerait la boucle infinie, mais sa non résolution fait qu'on ne va pas au bout de l'un des fichiers :))

En théorie, la condition de sortie de boucle devrait être lorsque l'on a atteint la fin des deux fichiers :
feof($fp1) && feof($fp2)
On doit donc boucler tant que cette condition est fausse. Cela peut se traduire de deux façon, l'inverse de la condition dans sa globalité, ou l'inverse de chacun des éléments de la condition :
! ( feof($fp1) && feof($fp2) )
!feof($fp1) || !feof($fp2)
Le contraire du ET logique est un OU :). On ne devrait donc pas sortir tant que fichier1 OU fichier2 ne sont pas entièrement lus.


Attention également, en PHP il y a une différence entre les opérateurs AND et &&, ainsi qu'entre OR et || dans la précédence de leur exécution. Les opérateurs && et || sont prioritaires sur les affectations (=, +=, ...) ou l'opérateur ternaire, tandis que AND et OR ne le sont pas. Il est à mon sens préférable d'utiliser les opérateurs && et || (moins explicites qu'en SQL, mais communs à la majorité des langages informatiques) à moins de savoir précisément pourquoi on utilise AND et OR :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 65 Messages

02 juin 2016, 13:44

@Ryle
C'est vraiment subtile, je viens de réaliser quelques tests et effectivement, j'ai compris, ça risque de me prendre un certain temps avant d'intégrer tout ça dans ma tête, c'est déroutant.
Je vais de ce pas me renseigner sur or / and / && et ||, je ne connaissais pas cette différence...