1

yop,


Je manipule des ficiers pour la première fois en C, avec stdio. J'ai pas de souci particulier, mais je me pose une question.
Pour copier un fichier existant vers un autre, vierge, je fais ça :void* buffer [BLOCK_SIZE]; do { size_t read_count = fread (buffer, 1, BLOCK_SIZE, src); size_t write_count = fwrite (buffer, 1, BLOCK_SIZE, dest); if (read_count != write_count) { fprintf (stderr, "Eh merde...\n"); } } while (feof (src) == 0);
Je précise que je suis sur PC, et que je ne connais pas les architectures modernes, les manières de fonctionner des procs et des divers éléments du HW, qui pourraient (peut-être) changer ici beaucoup de choses.
en googlant, je suis tombé sur des trucs de ce genre : http://www.programmingsimplified.com/c-program-copy-file (copie par caractère oO)

Question :
- y at-t-il beaucoup mieux (sans lib externe) que ma méthode naïve et certainement nioubique ?
- est-ce que la méthode que j'ai vue ici où là sur le oueb est en fait pas si mauvaise, parce que la libc est optimisée pour ça toussa toussa
- déjà à la troisième question ?

Merci d'avance. smile

2

En pratique (et peut-être dans les specs ?) FILE est déjà buffurisée, donc je ne pense pas que ça change grand chose de lire par paquet explicitement comme tu le fais.


(Tu es sûr que ça fonctionne ton truc quand tu arrives à la fin du fichier d'une taille qui n'est pas un multiple de la taille de ton buffer (BLOCK_SIZE) ? Je n'ai pas fait de C depuis longtemps, mais bon... cheeky)

3

ah non, pas testé du tout, je viens à peine d'écrire ça. Mais je pense qu'en arrivant en fin de fichier, je vais avoir read_count == write_count, mais feof (...) == 1.

4

En tout cas, pour ce genre de choses perso je passe par les API du système. C'est pas portable dans l'absolu, mais en pratique avec un ifdef ça se passe bien.

5

j'y ai pensé, bien sûr, mais c'est pas rigolo, ça fait qu'une ligne, c'est forcément bugfree, je vois pas trop l'intérêt quoi embarrassed

6

grin

En tout cas plus ça va et plus ton fwrite me semble suspect. A priori j'écrirais plutôt fwrite(buff, 1, read_count, dst) pour que ça fonctionne à la dernière passe. Sinon, sauf erreur, ton fichier copié aura forcément une taille de N fois BLOCK_SIZE, supérieure ou égale à la taille du fichier d'origine.

7

ah, ok, j'avais pas percuté. En effet, je déconne sûrement, merci bien. smile

8

En fait, les implémentations trouvées sur le net me font peur : pour un fichier de 1Mo, on va vérifier un million de fois (inutilement !) si on a atteint la fin du fichier ? On va faire des lectures octet par octet, façon sixties ??

9

Oué, nan, j'avoue que c'est moisi, c'est quand même mieux de lire par paquet comme tu as commencé, bien sûr happy (mais pas autant que de déléguer au système, sauf contexte particulier où tu penses que tu feras mieux)

Ça a l'air correct : http://stackoverflow.com/questions/10195343/copy-a-file-in-an-sane-safe-and-efficient-way

10

Faire ça octet par octet, ça veut dire 3 appels de fonction par octet copié. Certes il y a un cache, mais ça va quand même ramer et bouffer 100% du CPU pour rien du tout ; c'est une méthode indigne d'un codeur qui se respecte embarrassed
Utiliser un buffer dont la taille est une puissance de 2 (pour que ça corresponde à un nombre entier de secteurs) est bien meilleur.

Sinon, effectivement, ton code va échouer si la taille du fichier n'est pas un multiple entier de la taille du buffer, ou si elle est nulle. Le patch de Pen² corrige ce bug, mais en introduit un autre plus subtil : le fait que fread() renvoie une valeur plus petite que la taille du buffer ne veut pas forcément dire que tu as atteint la fin du fichier "normalement", ça peut être aussi que la lecture a échoué. Dans ce cas, ton code va soit partir en boucle infinie, soit se terminer normalement sans prévenir qu'il y a eu une erreur, suivant la valeur renvoyée par feof(). Il faut donc rajouter un appel à ferror() après fread() pour faire la différence entre la fin du fichier et une erreur de lecture.

Note qu'il y a des arguments pour laisser faire l'OS plutôt que réinventer la roue pour copier les fichiers :
- ça fait du code en moins à taper
- ça évite d'introduire des bugs cheeky
- ça supporte les fichiers de 232 octets et plus, ce qui n'est pas le cas de toutes les versions de la libc
- ça copie efficacement et correctement les fichiers spéciaux (symlinks, fichiers avec 2 streams ou plus, fichiers "creux"...)
- ça copie aussi les attributs (date de création originale, droits d'accès...)
- suivant l'OS, ça peut permettre d'éviter la recopie de buffers -> plus rapide
- suivant l'OS, ça peut permettre la copie de manière asynchrone, ce qui permet à l'utilisateur d'interrompre l'opération (par exemple si la source est un lecteur réseau inaccessible avec un timeout long) sans devoir se farcir du multithreading

EDIT : cross
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

11

Zerosquare (./10) :
- suivant l'OS, ça peut permettre la copie de manière asynchrone, ce qui permet à l'utilisateur d'interrompre l'opération (par exemple si la source est un lecteur réseau inaccessible avec un timeout long) sans devoir se farcir du multithreading

Vu comment ce comportent certains OS la dessus (genre CD défectueux, disque dur avec des soucis, réseau en train de lâcher) j'ai des doutes grin

(le nombre de fois ou j'ai du rebooter complètement une machine a cause d'une copie depuis un media défectueux... grin)
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

12

Exact mais je me couvre, j'ai dit "suivant l'OS" cheeky
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

13

./1> quitte à faire de la copie par bloc, autant passer outre la libc et utiliser directement les appels bas niveau, (2)open, (2)read, (2)write.

Sinon, si le fichier cible et destination ne sont pas sur le même support physique, ou si ce support physique est distant, il sera probablement plus rapide de lire et écrire simultanément avec un buffer tournant. (à l'inverse, ça ralentira probablement les copies sur le même support physique local donc il faut détecter quelle variante utiliser). O_NONBLOCK est ton ami pour ça, mais c'est autrement plus compliqué à implémenter.

Et puis en fonctionnalités après, comme dit plus haut une copie complète va copier aussi les métadonnées. Et là c'est compliqué parce que dépendant de l'OS et même du système de fichier.
  • fichiers à trous : sous Linux 3.1 et+ tu peux utiliser lseek pour ça ou un ioctl FIEMAP.
  • fichiers spéciaux : fstat → mknod, mkfifo (à la limite, sans les gérer, au moins les détecter pour les sauter)
  • symlink : readlink, symlink (idéalement une option pour copier soit le lien soit le fichier cible - proposer aussi d'ajuster les symlinks relatifs pour qu'ils restent valides)
  • dates : fstat → utime
  • permissions : fstat → fchmod
  • owner : fstat → fchown, lchown
  • attributs fs : ioctl FS_IOC_GETFLAGS → ioctl FS_IOC_SETFLAGS
  • attributes étendus : fgetxattr → fsetxattr
  • ACL : acl_get_fd → acl_set_fd (ou fgetxattr → fsetxattr pour les guerriers)

Pour faire les choses bien, tu devrais :
  • Faire attention aux race conditions (le même nom de fichier peut pointer sur un autre fichier s'il se fait écraser entre deux appels, pour ça on utilise les variantes f* des appels, qui prennent un descripteur de fichier et pas un nom).
  • Obtenir un lock sur le fichier (flock). Histoire de collaborer avec les applis qui les utilisent.


Si ton but c'était la libc, c'est pas tellement pertinent. Mais si c'était pour jouer avec les fichiers en général, y'a de la matière happy

14

(je crois que ça va changer Folco de la prog sur TI grin)
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

15

Folco (./5) :
j'y ai pensé, bien sûr, mais c'est pas rigolo, ça fait qu'une ligne, c'est forcément bugfree, je vois pas trop l'intérêt quoi embarrassed



Oh purée le fou rire du matin !!!

GT lol
avatar
Accrochez vous ca va être Cerebral !!

16

spectras (./13) :
Sinon, si le fichier cible et destination ne sont pas sur le même support physique, ou si ce support physique est distant, il sera probablement plus rapide de lire et écrire simultanément avec un buffer tournant. (à l'inverse, ça ralentira probablement les copies sur le même support physique local donc il faut détecter quelle variante utiliser). O_NONBLOCK est ton ami pour ça, mais c'est autrement plus compliqué à implémenter.

J'ai un fichier .89t que je veux convertir en .92t, donc créer une copie et changer le magic. On va pas se tirer sur la nouille des kilomètres pour au final, provoquer un trollde 50 pages, où mêmes les experts ne seront pas d'accord antre eux.
Un tel fichier faisant au plus 64ko + son header, j'ai fait un malloc de 70ko (à moins que je puisse mettre ça dans la pile ? aucune idée ^^), puis un fread/fwrite de tout l'ensemble. C'est ien plus simple et ça marche très bien. smile

Et l'absence de support réseau vous dissuadera d'utiliser mes logiciels ludiques au travail, na embarrassed

17

A part ça, merci beaucoup, j'aurais jamais immaginé que ça pouvait être si compliqué de copier un bête fichier de données en 2014 grin

18

En 2014 non, mais en 1980, oué cheekyPath p1= ... ;
Path p2= ... ;
Files.copy(p1, p2) ;
#sifflote# (https://docs.oracle.com/javase/tutorial/essential/io/copy.html )


(pourquoi pas un malloc de la taille exacte ?)

19

Folco (./16) :
Un tel fichier faisant au plus 64ko + son header, j'ai fait un malloc de 70ko (à moins que je puisse mettre ça dans la pile ? aucune idée ^^), puis un fread/fwrite de tout l'ensemble. C'est ien plus simple et ça marche très bien. smile.gif?20
Bien sûr, de toutes façons à moins que tu n'aies été en plein trip d'expérimentation sur la manipulation de fichiers, c'était surtout pour montrer toute la complexité d'une copie complète.
Note aussi que pour une telle taille de fichier, la version octet par octet marche très bien. Le temps nécessaire à tes quelques milliers d'appels de fonction restera négligeable. Surtout avec les processeurs modernes qui n'ont aucun problème à anticiper ce genre de saut très prévisible. Pour ce que t'en sais en plus, l'appel est peut-être même inliné.

Sinon, 70k sur la pile ne posent pas de problème particulier. Dans le doute, tu peux toujours faire un "ulimit -s" pour vérifier, ça te donne la taille maximale de la pile en ko. Typiquement, ça doit être quelques Mo (8Mo sur mon laptop).

20

Pen^2 (./18) :
(pourquoi pas un malloc de la taille exacte ?)

Parce que la taille exacte est comprise entre <taille d'un fichier texte vide au format AMS + header du fichier .89t> et <65520 + header fichier .89t>
Donc j'me fépachié cheeky

21

http://manpagesfr.free.fr/man/man3/getline.3.html

getline(), de chez GNU, est très bien foutue. Ya la même chose sous Windows ? Si on compile sous Win avec MinGW, la fonction sera incluse statiquement ? C'est parfait pour mes besoins en tout cas. smile

22

À froid, je ne connais pas de fonction équivalente sous Windows. Quelques infos sur des alternatives ici :
http://stackoverflow.com/questions/735126/are-there-alternate-implementations-of-gnu-getline-interface

Pour la compilation statique avec MinGW :
http://mingw-users.1079350.n2.nabble.com/Re-MinGW-dependencies-td5765728.html
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

23

Merci bien. En fait, je vais me passer de getline, parce que c'est un peu plus compliqué que ça, ce que je fais. Rien de très compliqué en fait, juste con. grin

Y a-t-il une fonction standard qui permette d'écrire de manière certaine en little endian ou en big endian ? J'ai un uint32_t à écrire en little endian, et un uint16_t en big endian, dans le même fichier hehe

Merci d'avance happy

Ya bien ça : http://man7.org/linux/man-pages/man3/endian.3.html mais c'est parfaitement non standard grin

24

Tu peux regarder dans les environs de swab, mais ça c'est pour inverser, pas sûr qu'il y a ait exactement ce que tu cherches.

25

Dans les trucs pour le réseau, peut-être ?
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

26

Pour le big-endian, comme le dit Flanker, tu peux utiliser les fonctions réseau : http://stackoverflow.com/questions/21996794/when-to-use-hton-ntoh-and-when-to-convert-data-myself

Mais perso, je me suis fait mes propres fonctions, c'est tellement bateau... hehe
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

27

Ah mais j'ai déjà écrit ce qu'il faut, c'est pas un problème évidemment, je me demandais juste s'il y avait plus propre. Juste une occasion de me cultiver, et donc merci pour les liens. smile

28

htons htonl, ntohs ntohl etc...
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

29

Bon, voilà #bananayel# : tromb Fichier joint : hJfK

Au final, je lis le texte brut original avec fread, je recopie le buffer obtenu dans un autre, avec les traitements qui vont bien : shrink des espaces (optionnel), suppression des commentaires (optionnel), transformation des fins de ligne en 0x0D20 pour être TI-compliant (support de CR/LF/CRLF, et des fichiers aux fins de lignes inconsistantes embarrassed).
Une fois tous ces traitements réalisés, fwrite du buffer vers le nouveau fichier, et voilà.
Au début, j'avais fait une version avec fgetc/fputc/ungetc, mais c'et très vite galère, notamment à cause des vérifications d'erreur permanentes à faire, ce qui est toujours aussi génial pour pourrir un source C.

Voilà, merci à tous pour tout. smile

30

top
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo