3

私はCを初めて使用するため、エラーがどこにあるのか正確にはわかりません。ただし、問題の大部分は、doubleをd_buffer(double)配列に格納する方法または印刷する方法のいずれかにあることを私は知っています。

具体的には、私の出力は非常に大きな数を出力し続けます(小数点の前に約10〜12桁、その後にゼロの軌跡があります。さらに、これは二重入力を可能にする古いプログラムの適応であるため、実際に追加したのは2つのifステートメント(「read」forループと「printf」forループ内)とd_buffer宣言。

私はこのエラーに数時間を費やしたので、どんな入力でもいただければ幸いです。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


struct DataDescription
{
   char fieldname[30];
   char fieldtype;
   int  fieldsize;
};




/* -----------------------------------------------
   eof(fd): returns 1 if file `fd' is out of data
   ----------------------------------------------- */
int eof(int fd)
{
   char c;

   if ( read(fd, &c, 1) != 1 )
      return(1);
   else
    { lseek(fd, -1, SEEK_CUR);
      return(0);
    }
}


void main()
{
   FILE *fp;    /* Used to access meta data */
   int fd;  /* Used to access user data */

   /* ----------------------------------------------------------------
      Variables to hold the description of the data  - max 10 fields
      ---------------------------------------------------------------- */
   struct DataDescription DataDes[10];  /* Holds data descriptions
                                           for upto 10 fields */
   int  n_fields;                       /* Actual # fields */

   /* ------------------------------------------------------
      Variables to hold the data  - max 10 fields....
      ------------------------------------------------------ */
   char c_buffer[10][100];  /* For character data */
   int  i_buffer[10];       /* For integer data */
   double d_buffer[10];

   int i, j;
   int found;

   printf("Program for searching a mini database:\n");

   /* =============================
      Read in meta information
      ============================= */
   fp = fopen("db-description", "r");
   n_fields = 0;
   while ( fscanf(fp, "%s %c %d", DataDes[n_fields].fieldname, 
          &DataDes[n_fields].fieldtype,
          &DataDes[n_fields].fieldsize) > 0 )
      n_fields++;

   /* ---
      Prints meta information
      --- */
   printf("\nThe database consists of these fields:\n");
   for (i = 0; i < n_fields; i++)
      printf("Index %d: Fieldname `%s',\ttype = %c,\tsize = %d\n", 
        i, DataDes[i].fieldname, DataDes[i].fieldtype,
        DataDes[i].fieldsize);
   printf("\n\n");

   /* ---
      Open database file
      --- */
   fd = open("db-data", O_RDONLY);

   /* ---
      Print content of the database file
      --- */
   printf("\nThe database content is:\n");
   while ( ! eof(fd) )
   {  /* ------------------
         Read next record
         ------------------ */
      for (j = 0; j < n_fields; j++)
      {  
     if ( DataDes[j].fieldtype == 'I' )
        read(fd, &i_buffer[j], DataDes[j].fieldsize);
     if ( DataDes[j].fieldtype == 'F' )
        read(fd, &d_buffer[j], DataDes[j].fieldsize);
     if ( DataDes[j].fieldtype == 'C' )
        read(fd, &c_buffer[j], DataDes[j].fieldsize);       
      }

      double d;
      /* ------------------
         Print it...
         ------------------ */
      for (j = 0; j < n_fields; j++)
      {   
     if ( DataDes[j].fieldtype == 'I' )
        printf("%d ", i_buffer[j]);
     if ( DataDes[j].fieldtype == 'F' )
        d = d_buffer[j];
        printf("%lf ", d);
     if ( DataDes[j].fieldtype == 'C' )
        printf("%s ", c_buffer[j]);
      }
      printf("\n");
   }
   printf("\n");
   printf("\n");


}

期待される出力:番号「e=2.18281828」で終わる3行のデータ

問題を再現するには、次の2つのファイルがlookup-data.cファイルと同じディレクトリにある必要があります。-
[db-data] [1]
-[db-description] [2]

4

3 に答える 3

1

編集:私の以前の推測はすべて間違っていました。問題は、データベースファイルにビッグエンディアンの数値があり、データがリトルエンディアンのコンピューターで読み取られていたことです。初期の憶測で時間を無駄にしないように、「ここから読み始めてください」とマークされたセクションにスキップしてください(非常に限られた歴史的価値のためにここに残されています)。

あなたの問題は、あなたがタイプであると宣言したという事実とともに、値を印刷するために使用されるprintf()フォーマット仕様に関連しているのではないかと思います。おそらく本当なので、余分なバイトのデータをfloat値の一部として解釈していることになります。%lfd_bufferintsizeof(double) > sizeof(int)

プログラムが実行に使用しているデータが表示されないため、これはわかりませんが、fieldsizefor floatデータがそうsizeof(float)sizeof(double)ない場合は、float値を適切に格納しているのにd_buffer、印刷時に混乱させている可能性があります。または、fieldsizeがに等しくsizeof(double)sizeof(double)がより大きい場合はsizeof(int)、の終わりを書き留めてd_bufferおり、何かがデータを破損しています。

宣言をに変更してdouble d_buffer[10];、プログラムがより適切に機能するかどうかを確認することをお勧めします。また、またはfieldsizeに設定されているかどうかを確認してください。次に、型として宣言し、次のコードを使用する場合:sizeof(float)sizeof(double)sizeof(float)d_bufferfloat

  if ( DataDes[j].fieldtype == 'F' )
  {
    double d = d_buffer[j];
    printf("%lf ", d);
  }

編集:また、すべてのI/Oの使用fopen()と使用に切り替えることをお勧めします。fread()基本的なopen()andread()は戻ることができEINTRます。これは、操作を再試行する必要があることを意味します。したがって、これらは、呼び出しが戻った場合に呼び出しを再試行するループ内でのみ適切に使用されますEINTRfopen()and呼び出しはfread()、そのような詳細からあなたを隔離し、プログラムをよりスムーズに実行できるようにするいくつかのバッファリングを備えています。(現在のプロジェクトは少量のデータの読み取りと書き込みのみを行っていると確信しているため、現時点ではパフォーマンスの違いはそれほど重要ではありません。)

eof()また、関数を削除することをお勧めします。非常に珍しく、文字を読み取ってfseek()から元に戻すのに少し時間がかかる可能性があります。Cライブラリには、入力ストリームから1文字を取得する関数があり、その文字をテストして、ファイルの終わりを検出する値fgetc()であるかどうかを確認できます。EOFまた、Cライブラリにはungetc()1文字をプッシュバックできる機能もあります。これには、実際にディスクを探す必要はありませんが、キャラクターをどこかのバッファーに戻すだけです。ただし、コードを使用fgetc()する必要はありません。ungetc()からの戻り値を確認するだけですfread()また、長さがゼロの読み取りを取得した場合は、ファイルの終わりに到達したことがわかります。本番コードでは、とにかく各関数呼び出しの戻り値を確認する必要があります。すべての読み取りが成功することを期待するだけでは、逃げることはできません。

編集:もう1つ試すことができます。フォーマットコードをからに変更して、"%lf""%f"が起こるかを確認してください。私はそれが何をするのか正確にはわかりませlん、そしてあなたはそれを必要とすべきではありません。プレーンオールド"%f"はフォーマットする必要がありますdouble。しかし、それは何も変わらないかもしれません:私が見つけたこのウェブページによると、との間にprintf()違いはありません。"%lf""%f"

http://www.dgp.toronto.edu/~ajr/209/notes/printf.html

*ここから読み始めてください*

編集:さて、私が確かに理解したことの1つ。データベース形式は、インデックス値(整数値)、浮動小数点値、文字列値の順になります。ファイル内の現在の位置を進めるには、それぞれを読み取る必要があります。では、フォーマットコードをチェックし、読み取るものを決定している現在のコードは?正しくありません; 各レコードの整数、浮動小数点数、および文字列を読み取る必要があります。

編集:さて、これがデータベースを読み取ることができる正しいPythonプログラムです。メタデータファイルをわざわざ読み取る必要はありません。定数をハードコーディングしただけです(使い捨てのプログラムなので問題ありません)。

import struct

_format_rec = ">id20s"  # big-endian: int, double, 20-char string
_cb_rec = struct.calcsize(_format_rec) # count of bytes in this format

def read_records(fname):
    with open(fname) as in_f:
        try:
            while True:
                idx, f, s = struct.unpack(_format_rec, in_f.read(_cb_rec))
                # Python doesn't chop at NUL byte by default so do it now.
                s, _, _ = s.partition('\0')
                yield (idx, f, s)
        except struct.error:
            pass

if __name__ == "__main__":
    for i, (idx, f, s) in enumerate(read_records("db-data")):
        print "%d) index: %d\tfloat: %f \ttext: \"%s\"" % (i, idx, f, s)

したがって、インデックス値は32ビット整数、ビッグエンディアンです。フロートは64ビットフロート、ビッグエンディアンです。テキストフィールドは20文字固定されています(したがって、0〜19文字の文字列と終了NULバイト)。

上記のプログラムの出力は次のとおりです。

0) index: 1     float: 3.141593         text: "Pi"
1) index: 2     float: 12.345000        text: "Secret Key"
2) index: 3     float: 2.718282         text: "The number E"

さて、Cコードをコンパイルしようとすると、コンピューターがリトルエンディアンであるため、ガベージ値が出力されます。リトルエンディアンのコンピューターでCコードを実行しようとしていますか?

編集:質問である最新のコメントに答えるには:入力レコードごとに、read()3回呼び出す必要があります。4バイト整数(ビッグエンディアン)であるインデックスを初めて読み取るとき。2番目の読み取りは8バイトの浮動小数点値であり、これもビッグエンディアンです。3番目の読み取りは文字列として20バイトです。読み取るたびに、ファイル内の現在の位置が移動します。3つの読み取りが一緒になって、ファイルから1つのレコードを読み取ります。3つのレコードを読んで印刷すると、完了です。

私のコンピューターはリトルエンディアンなので、Cで値を正しく取得するのは難しいですが、私はそれを行いました。8バイトの値を整数または浮動小数点数として読み取ることができるユニオンを作成し、それをread();への呼び出しのバッファーとして使用しました。次に、__builtin_bswap64()(GCCの機能)を呼び出して、ビッグエンディアン値をリトルエンディアンに交換し、結果を64ビット整数として格納し、浮動小数点数として読み取りました。__builtin_bswap32()また、整数インデックスを交換するために使用しました。私のCプログラムは次のように出力します。

The database content is:
1 3.141593 Pi
2 12.345000 Secret Key
3 2.718282 The number E

したがって、各レコードを読み、データのエンディアンが正しいことを確認してください。そうすれば、プログラムが機能するようになります。

編集:これは、エンディアンの問題をどのように修正したかを示すコードフラグメントです。

typedef union
{
    unsigned char buf[8];
    double d;
    int64_t i64;
    int32_t i32;
} U;

// then, inside of main():

   printf("\nThe database content is:\n");
   {  /* ------------------
         Read next record
         ------------------ */
      for (j = 0; j < n_fields; j++)
      {
        U u;
        read(fd, u.buf, 4);
        u.i32 = __builtin_bswap32(u.i32);
        i_buffer[j] = u.i32;
        read(fd, u.buf, 8);
        u.i64 = __builtin_bswap64(u.i64);
        d_buffer[j] = u.d;
        read(fd, c_buffer[j], 20);
      }

データベースがビッグエンディアン形式であったことに少し驚いています。x86ファミリプロセッサを使用するコンピュータはすべてリトルエンディアンです。

私のようにこれらの番号(4、8、20)をハードコーディングしないでください。受け取ったメタデータを使用する必要があります。それはあなたにお任せします。

__builtin_bswap32()編集:また、またはを呼び出すべきではありません__builtin_bswap64()。電話ntohl()して...64ビットのものが何であるかわかりません。しかしntohl()、ポータブルです。ビッグエンディアンのコンピューターでコンパイルしている場合はスワップをスキップし、リトルエンディアンのコンピューターでコンパイルしている場合はスワップを実行します。

ntohl()編集:ここStackOverflowで64ビットに相当するソリューションを見つけました。

https://stackoverflow.com/a/4410728/166949

Linuxだけを気にするなら、それは簡単です。使用する代わりに、andを使用してから使用#include <arpa/inet.h>することができます。#include <endian.h>be32toh()be64toh()

これを取得したら、次のようにデータベースファイルを読み取ることができます。

u.i64 = be64toh(u.i64);

上記のコードをビッグエンディアンのマシンでコンパイルすると、何もしないようにコンパイルされ、ビッグエンディアンの値が読み取られます。上記のコードをリトルエンディアンのマシンでコンパイルすると__builtin_bswap64()、64ビット値が正しく読み取られるように、バイトと同等にコンパイルされてスワップされます。

編集:私はいくつかの場所で、3つの別々の読み取りを行う必要があると言いました:1つはインデックスを取得するため、1つはフロートを取得するため、もう1つは文字列を取得するためです。実際には、を宣言しstructて1回の読み取りを発行すると、1回の読み取りですべてのデータが取り込まれます。ただし、注意が必要なのは、Cコンパイラが内部に「パディング」バイトを挿入すると、のバイト構造がファイルから読み取られたレコードのバイト構造と正確に一致しなくstructなる可能性があることです。structCコンパイラは、アラインメントバイト(#pragmaステートメント)を制御する方法を提供する必要がありますが、詳細には触れたくありませんでした。この単純なプログラムの場合、3回読み取るだけで問題が解決します。

于 2012-07-04T01:33:06.877 に答える
0

db-descriptionから読み取っているフィールドタイプがdb-dataのデータと一致すると想定しているのはなぜですか?私は確かにそうしません。少なくとも、これらのフィールドタイプを印刷して、期待どおりのものであることを確認する必要があります。

于 2012-07-04T02:26:36.593 に答える
0

希望する結果を得るには、置き換えることをお勧めします

int  d_buffer[10];

と:

double d_buffer[10];
于 2012-07-04T01:39:26.250 に答える