5

(議論のためにエンディアンを無視します - これは単なるテストケース/概念実証です - そして私はstrcpy実際のコードでも決して使用しません!)

次の単純な C コードを考えてみましょう。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* variables of type message_t will be stored contiguously in memory */
typedef struct {
  int message_id;
  char message_text[80];
} message_t;

int main(int argc, char**argv) {
  message_t* m = (message_t*)malloc(sizeof(message_t));
  m->message_id = 1;
  strcpy(m->message_text,"the rain in spain falls mainly on the plain");

  /* write the memory to disk */
  FILE* fp = fopen("data.dat", "wb");
  fwrite((void*)m, sizeof(int) + strlen(m->message_text) + 1, 1, fp);
  fclose(fp);

  exit(EXIT_SUCCESS);
}

書き込むファイルは、ディスクから簡単に読み取ることができます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  int message_id;
  char message_text[80];
} message_t;

int main(int argc, char**argv) {
  message_t* m = (message_t*)malloc(sizeof(message_t));

  FILE* fp = fopen("data.dat", "rb");
  fread((void*)m, sizeof(message_t), 1, fp);
  fclose(fp);

  /* block of memory has structure "overlaid" onto it */
  printf("message_id=%d, message_text='%s'\n", m->message_id, m->message_text);

  exit(EXIT_SUCCESS);
}

例えば

$ ./write 
$ ./read 
message_id=1, message_text='the rain in spain falls mainly on the plain'

私の質問は、OCaml で、私が持っているのは次のとおりです。

type message_t = {message_id:int; message_text:string}

どうすればそのデータを取得できますか? Marshalできない、できないinput_binary_int。たとえば、 "what is " のような C のヘルパー関数を呼び出してsizeof(int)、n バイトを取得し、C 関数を呼び出して "これらのバイトを int に変換" することはできますが、この場合、新しい C コードを追加することはできません。解凍」は、私が「すべき」と知っていることに基づいて、OCaml で行う必要があります。s のブロックで文字列を反復処理するか、sizeof'\0' を探すだけの問題ですか、それとも賢い方法はありますか? ありがとう!

4

4 に答える 4

5

この種の低レベルの構造体処理を行うには、OCaml Bitstring が非常に便利だと思います。80 文字すべてをディスクに書き込んだ場合、message_t の同等のリーダーは次のようになります。

bitmatch (Bitstring.bitstring_from_file "data.dat") with
  | { message_id : 32;
      message_text : 8 * 80 : string;
    } -> 
      Printf.printf "message_id=%ld, message_text='%s'\n" 
                    message_id message_text
  | { _ } -> failwith "Not a valid message_t"

そのままで、トリムする必要がありますが、message_textビットストリングは、一般的にこの種のタスクを実行したいものです。

于 2011-05-17T19:05:24.977 に答える
4

これを Ocaml でコーディングする方法を理解する前に、データ表現が何であるかを理解する必要があります。C コードは、リーダーとライターの間で一貫性がありません。ライターstrlen(m->message_text)+1は文字列のバイトのみを書き込みますが、リーダーは最大 80 バイトを想定しています。

私のアドバイスは、すべてのマーシャリングを同じ言語 (C または Ocaml) で行うことです。Ocaml のマーシャリング ライブラリをお勧めします。これは既に機能しており、クロスプラットフォームで使いやすいものです。

C と Ocaml マーシャリング コードの間の相互運用性が必要な場合は、マーシャリング フォーマットを確立し、両方の言語で同じ仕様を実装する必要があります。それを行う前に、テキスト表現を使用できるかどうかを検討してください。テキスト表現は、エラーが発生しにくく、サードパーティ ツールでの検査と操作が簡単ですが、サイズが大きくなります。JSONは軽量のデータ表現形式ですが、重量のあるXMLを使用することもできます。すべてのデータが整数と文字列のように本当に単純で、文字列に改行が含まれていない場合は、整数を 10 進数で記述し、その後にスペース (または a:または a ,)、文字列、改行を続けることができます。

C マーシャリング形式が事前定義されており、それを変更できない場合、それはプラットフォームに依存する (アーキテクチャと C コンパイラに依存する) ことに注意してください。Ocaml では、そのようなプラットフォームの詳細にアクセスすることはできません。したがって、最善の策は、Ocaml プログラムを C ヘルパーとリンクして、ヘルパーsizeof(int)が元のアプリケーションと同じ C 型表現 ( 、エンディアン、構造パディング) を使用するようにすることです。

于 2011-05-16T23:04:28.357 に答える
2

同じプラットフォームで同じ C コンパイラを使用することに依存して、書き込まれたデータと読み戻されたデータの形式について考える必要がなくなります。残念ながら、C と OCaml の間で相互運用しようとしている場合、その余裕はありません。構造体のバイト数を数え、整数がリトルエンディアンかビッグエンディアンかを判断し、それに応じて OCaml 側でコーディングする必要があります。

バイナリ ファイルを解析するために、各タイプを個別に手動でアンマーシャリングする必要があります。たとえば、リトルエンディアンの 32 ビット整数を読み取るには、次のようにする必要があります。

let input_le_int32 inch =
  let res = ref 0l in
  for i = 0 to 3 do
    let byte = input_byte inch in
    res := Int32.logor !res (Int32.shift_left (Int32.of_int byte) (8*i))
  done;
  !res

NUL で終了する文字列を読み取るには、次のようにします。

let input_c_string inch =
  let res = Buffer.create 256 in
  try while true do
    let byte = input_byte inch in
    if byte = 0 then raise Exit else
    Buffer.add_char res (char_of_int byte)
  done; assert false with Exit ->
  Buffer.contents res

すべてが正しければ、次のようにして構造を読み戻すことができます。

let input_message inch =
  let message_id   = input_le_int32 inch in
  let message_text = input_c_string inch in
  { message_id; message_text; }

注:順不同でフィールドを読み取らないように、読み取りを順序付けすることが不可欠です (!)。並列割り当てを使用しないでください。let

于 2011-05-17T13:14:44.807 に答える
1

アドバイスありがとうございます。と決めた方法をブログに書いています。

于 2011-05-17T22:06:14.207 に答える