IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Caml Discussion :

Module pour écrire en binaire


Sujet :

Caml

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut Module pour écrire en binaire
    Bonjour,

    Je suis actuellement en train d'apprendre Ocaml. Je souhaiterais compresser des images et pour cela j'ai besoin d'écrire bit à bit. Je me suis lancé dans l'écriture et voilà ce que j'obtiens :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    (* binary writing and reading *)
     
    type binary_buffer = {
      file : out_channel;
      buffer : char array;
      size : int;
      mutable byte_index : int;
      mutable bit_index : int;
    } ;;
     
    let open_binary_file name =
      {
        file = open_out name;
        buffer = Array.make 256 (Char.chr 0);
        size = 256;
        byte_index = 0;
        bit_index = 0;
      } ;;
     
    let write_buffer buffer =
      for i = 0 to buffer.size do
        output_byte buffer.file (int_of_char buffer.buffer.(i))
      done ;;
     
    let write_bit buffer bit =
      let bits_written = buffer.byte_index * 8 + buffer.bit_index in
      let byte_index = buffer.byte_index in
      let bit_index = buffer.bit_index in
      let current_byte = buffer.buffer.(byte_index) in
        begin
          if bits_written >= buffer.size then
    	write_buffer buffer;
          if bit <> 0 then
    	let new_byte = (int_of_char current_byte) lor (1 lsl (7 - bit_index)) in
    	  buffer.buffer.(byte_index) <- (char_of_int new_byte);
          if bit_index = 7 then
    	buffer.byte_index = buffer.byte_index + 1;
          buffer.bit_index = (buffer.bit_index + 1) mod 8;
        end ;;
    Malheureusement j'obtiens l'erreur suivante dans l'interpréteur :

    File "binary.ml", line 37, characters 29-50:
    Error: This expression has type int but an expression was expected of type
    unit

    C'est au niveau de la fonction write_bit. Je comprends le pourquoi de l'erreur mais je ne sais pas comment lui dire d'ignorer le fait que cette affectation puisse être une expression de type int.

    Si quelqu'un pouvait m'aider ;-)

    De plus, j'ai pensé un petit peu comme si j'écrivais un programme C car ce sont des entrées sorties mais si quelqu'un a une meilleure approche, je suis preneur.

    Merci à vous.

    --
    sperca

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 928
    Points
    928
    Par défaut
    Salut

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    buffer.buffer.(byte_index) <- (char_of_int new_byte);
    ...
    buffer.byte_index = buffer.byte_index + 1;
    Un truc devrait te choquer au niveau de la différence de syntaxe


    x = y, c'est une comparaison entre x et y, pas une affectation !

    Et sinon, fais un peu confiance aux auteurs d'ocaml. Les accès aux fichiers sont bufferisé. Tu peux particulièrement t'en convaincre en voyant ça.

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    832
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 832
    Points : 1 104
    Points
    1 104
    Par défaut
    Si tu veux ignorer une valeur de retour, utilise la fonction "ignore", qui a comme type ('a -> unit). Si elle n'existait pas déjà, tu pourrais facilement la recoder toi-même :

    Pour manipuler des données binaires structurées, il y a une très bonne lib qui est bitstring. Cependant, c'est du caml un peu avancé (ça utilise une extension syntaxique etc. etc.), donc ça pourrait te rendre un peu confus si tu débutes. Je te conseillerais de te familiariser avec le langage avant de commencer à utiliser des bibliothèques externes.

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    D'accord merci à toi.

    Je vais donc utiliser un seul octet au lieu d'un tableau de d'octets et la fonction output_byte.

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    Re,

    J'ai remplacé le tout par ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    (* binary writing and reading *)
     
    type binary_buffer = {
      file : out_channel;
      mutable byte : char;
      mutable bit_index : int;
    } ;;
     
    let open_binary_file name =
      {
        file = open_out name;
        byte = Char.chr 0;
        bit_index = 0;
      } ;;
     
    let write_buffer buffer =
        output_byte buffer.file (int_of_char buffer.byte) ;;
     
    let write_bit buffer bit =
      let bits_written = buffer.bit_index in
        begin
          if bits_written >= 8 then
    	begin
    	  write_buffer buffer;
    	  buffer.bit_index <- 0;
    	end
          if bit <> 0 then
    	let new_byte = (int_of_char buffer.byte) lor (1 lsl (7 - buffer.bit_index)) in
    	  buffer.byte <- char_of_int new_byte;
          buffer.bit_index <- buffer.bit_index + 1;
        end ;;
    Donc déjà j'ai une première question, dans les différents cours que j'ai lu, le if peu posséder une branche else facultative qui se sera remplacé automatiquement par else () à condition que le type soit unit. Or lorsque je fais ça dans Emacs, l'indentation ne suit pas avec le tuareg-mode, donc je suppose que je m'y prends mal à un moment.

    Le compilateur me signale également une erreur au niveau du if bit <> 0, je ne comprends pas pourquoi...

    Ce petit module peut paraître inutile mais c'est surtout pour mettre au point un fichier mli et faire un semblant de modularité...

    Merci à vous.

    --
    sperca

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    Citation Envoyé par sperca Voir le message
    Le compilateur me signale également une erreur au niveau du if bit <> 0, je ne comprends pas pourquoi...
    c'est parcequ'il manque un point-virgule juste avant le if (ou plutôt juste après le end) (enfin bref entre les deux quoi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    	end;
          if bit <> 0 then
    style impératif égal point-virgule après chaque instruction

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    je viens de lire rapidement ta fonction write_bit et j'ai l'impression qu'il y a une erreur. Après avoir écrit le byte il ne semble pas être remis à 0b00000000 or tu sembles écrire un bit uniquement si un 1 est demandé, donc je me demande s'il ne risque pas de rester un 1 du byte précédent lorsqu'un 0 en fournit à la fonction.

    sinon tu testes d'abord si le byte est complet (auquel cas tu l'écris dans le fichier) puis tu push un bit, perso je ferais plutôt l'inverse ca me semblerait plus logique.

    il te faut aussi une fonction flush pour terminer l'écriture du fichier si le dernier byte n'est pas complet il faut tout de meme l'écrire

  8. #8
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    une autre remarque, ce n'est pas une erreur, juste plutot une question de gout je dirais.

    perso j'utiliserais un type int pour le byte plutot qu'un type char, ce qui éviterait de faire le va et vient int_of_char / char_of_int.

    sinon si tu choisis de garder un char, tu pourrais utiliser output_char au lieu de output_byte pour rester cohérent avec ton type.

  9. #9
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    une autre remarque, bit_index semble inversé dans ton code c'est à dire que normalement le bit de poid faible est le bit d'indice 0 et le bit de poid fort d'un octet et le bit d'indice 7. Or j'ai l'impression que ton code fait l'inverse, ce qui rend plus difficile la lecture de ton code pour quelqu'un qui se demanderait si tu remplis les bit par la gauche ou bien par la droite.

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    Merci c'était bien le ';' après le end.

    Pour l'écriture bit à bit, je les écris de "gauche à droite". Si le bit à écrire est 1 je réalise un ou bit à bit sinon j'avance vu que de base c'est 0.

    En tout cas merci encore, je vais pouvoir avancer ;-)

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    sinon en ajoutant juste le point virgule le code ne fonctionne pas, ci-dessous il devrait y avoir 64 si je ne m'abuse, or on a 128 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # #use "bo.ml";;
    type binary_buffer = {
      file : out_channel;
      mutable byte : char;
      mutable bit_index : int;
    }
    val open_binary_file : string -> binary_buffer = <fun>
    val write_buffer : binary_buffer -> unit = <fun>
    val write_bit : binary_buffer -> int -> unit = <fun>
    # let b = open_binary_file "/tmp/tb";;
    val b : binary_buffer = {file = <abstr>; byte = '\000'; bit_index = 0}
    # write_bit b 0 ;;
    - : unit = ()
    # write_bit b 1 ;;
    - : unit = ()
    # b ;;
    - : binary_buffer = {file = <abstr>; byte = '\128'; bit_index = 1}
    j'ai réécrit ton code "à ma sauce" et il ne semble plus y avoir ce bug

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    (* binary writing and reading *)
     
    type binary_buffer =
      { file : out_channel;
        mutable byte : int;
        mutable bit_index : int;
      }
     
    let open_binary_file filename =
      { file = open_out filename;
        byte = 0b0000_0000;
        bit_index = 7;
      }
     
    let write_bit buf bit =
      if bit = 1 then
        buf.byte <- buf.byte lor (
          match buf.bit_index with
          | 0 -> 0b0000_0001
          | 1 -> 0b0000_0010
          | 2 -> 0b0000_0100
          | 3 -> 0b0000_1000
          | 4 -> 0b0001_0000
          | 5 -> 0b0010_0000
          | 6 -> 0b0100_0000
          | 7 -> 0b1000_0000
          | _ -> assert false);
      buf.bit_index <- pred buf.bit_index;
      if buf.bit_index < 0 then begin
        output_byte buf.file buf.byte;
        buf.bit_index <- 7;
        buf.byte <- 0b0000_0000;
      end
    le match ca semble assez verbeux comme ça, mais j'aime bien pouvoir visualiser facilement les choses et j'aime bien quand j'arrive à faire un code que l'on peut lire en réfléchissant le moins possible

  12. #12
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    Citation Envoyé par sperca Voir le message
    Pour l'écriture bit à bit, je les écris de "gauche à droite". Si le bit à écrire est 1 je réalise un ou bit à bit sinon j'avance vu que de base c'est 0.
    oui j'avais vu, mon propos était de dire que ta variable bit_index est inversée et par conséquent peut induire en erreur le lecteur de ton code.

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    Sinon, je pensais à une manière peut-être un peu plus Caml. Au lieu d'utiliser un buffer et d'écrire dès que je dépasse cette capacité. Je pourrais par exemple utiliser une liste de type bit (Zero | Un) et l'écriture une fois pour toute au moment de la fermeture du fichier.

    L'inconvénient de cette méthode c'est la taille de la liste en mémoire. Par contre ça me permettrait par exemple d'écrire des informations avant la liste de bits, telles que le nombre de bits écrits etc.

    Vous avez des avis là-dessus ?

    Merci à vous.

    --
    sperca

  14. #14
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 128
    Points : 146
    Points
    146
    Par défaut
    je ne pense pas que cela vaille vraiment la peine, caml est un langage fonctionnel *impure* donc le but est de faire les bons compromis, si la partie impérative est très localisée comme ici ça ne pose pas vraiment de problème.

    sinon tu peux toujours le faire à titre d'exercice pour te rendre compte par toi-même,
    et pour répondre à ta question sur l'espace mémoire, comme tu disais que c'était pour des images compressées (si je me souviens bien) donc si je pars d'une image de 400Ko pour faire mon estimation, la taille d'un constructeur sans arguement est d'un mot, celle d'un noeud dans une liste est de 2 mots (un pour l'élément courant et un pointeur vers le noeud suivant) les constructeurs sont unboxed (comment on dit d'ailleurs en francais ? déboîté ? désemboîté ? lol) si mon estimation est correcte ca devrait faire environ 25 Mo en mémoire sur une archi 32bits. La machine moyenne de nos jours ayant 2 Go de RAM ce n'est donc pas un problème.

    Pour ta question sur l'insertion d'une entête après coup perso ce que je ferais serait d'écrire tes bytes dans une chaine puis tu ajoutes ton entête et tu écris le tout dans le fichier. Quand je dis une chaine c'est l'idée, en fait il serait plus approprié d'utiliser le type Buffer.t de la bibliothèque standard, juste dans ta fonction write_bit au lieu d'écrire le byte tu fais Buffer.add_char, voila mon avis.

  15. #15
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    J'en prends note, merci à toi.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Article] Écrire un module pour Node.js
    Par Bovino dans le forum NodeJS
    Réponses: 0
    Dernier message: 16/04/2012, 14h06
  2. tutoriel pour écrire des modules CPAN
    Par dyngry dans le forum Modules
    Réponses: 1
    Dernier message: 03/08/2010, 18h47
  3. Réponses: 12
    Dernier message: 26/07/2006, 13h08
  4. Algo pour écrire un chiffre
    Par Skyw4lKR dans le forum Algorithmes et structures de données
    Réponses: 5
    Dernier message: 11/08/2004, 14h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo