ID登録せずに、無料で質問できる♪ 教えて!gooアプリ>>

C言語で可変長から固定長に変換方法は??
ある可変長のファイルがあって、レコード長を全て40にそろいたいと思って以下のようにプログラム作りました:
・・・・・(省略)
len = strlen(buf); // fscanf使ってファイルから読みみ込んだものbufに格納
n = 40-len;
char * str = (char *)malloc(n+1);
memset(str, ' ', n);
strcat(buf,str); //レコード40バイトになるまで空白を詰めていく
fprintf(fpt,buf);//fprintf使って新しいファイルに書き込み
・・・・・・(省略)
実行してみたところ、全部40という長さになっていない、40超えるものも多数出ました。
これは何がだめですか?fprintfをつかったからだめですか?それともmalloc freeにする必要ありますか?まったく検討つかないです。どうかよろしくお願いします

このQ&Aに関連する最新のQ&A

A 回答 (9件)

>while(fgets(buf,REC,fp)!= NULL){


>len = strlen(buf);
>printf("レコード長 :%d\n",len);
>}
>結果は
>レコード長 :39
>レコード長 :2

よくあるミスです。

fgetsの2番目の引数は「文字列の終端文字であるヌル文字を格納する分を含んだ、バッファの実サイズ」を指定する事になっています。

そして、困った事に「改行文字は、文字列の文字数に含み、改行文字も読み込む」のです。

では、質問者さんのプログラムを検証してみましょう。

while(fgets(buf,REC,fp)!= NULL){
RECは40です。そのため、fgetsは「bufが一杯になる39文字までを読んで、読み込みを打ち切り、末尾にヌル文字を付加」します。

len = strlen(buf);
bufには「39文字+終端のヌル文字」が入っているので、strlenは終端のヌル文字の手前にある文字数の「39」を返し、lenは39になります。

printf("レコード長 :%d\n",len);
printfは「レコード長 :39+改行」を表示します。

while(fgets(buf,REC,fp)!= NULL){
RECは40です。そのため、fgetsは「さっき読み残した1文字と、改行文字を読んで打ち切り、末尾、つまり改行文字の後にヌル文字を付加」します。

len = strlen(buf);
bufには「読み残した1文字+改行文字1文字+終端のヌル文字」が入っているので、strlenは終端のヌル文字の手前にある文字数の「2」を返し、lenは2になります。

printf("レコード長 :%d\n",len);
printfは「レコード長 :2+改行」を表示します。

結果、質問者さんのプログラムは

レコード長 :39
レコード長 :2

を表示する事になります。

これは「意図した結果ではないが、fgets()関数の仕様通りの動作」であり「正常に動作している」のです。

単に「意図通りの引数を指定しなかった為、意図した動きをしなかっただけ」なのですね。

そういう訳で「40文字+改行1文字+終端ヌル1文字」で、バッファは42バイト必要で、fgetsには「42文字あるバッファ」と「42」を指定しなければならないのです。

レコード長の定義として「#define REC 40」と定義しているのなら、以下のように書かねばなりません。

#define REC 40
char buf[REC + 2] /* 改行文字と終端ヌル文字も格納できるように、レコード長より2文字分大きいバッファを用意しなければならない */
(途中省略)
while(fgets(buf,siziof(buf),fp)!= NULL){ /* fgetsの第2引数に定数は指定しない事。必ず「sizeof(指定したバッファ)を指定すること */
len = strlen(buf);
if (buf[len - 1] == '\n') buf[--len] = '\0'; /* 末尾に改行があるなら、末尾の改行を終端文字ヌルで上書きして取り去り、その分、lenを1減らす */
printf("レコード長 :%d\n",len);
}
    • good
    • 0

追記な訂正。



>fprintf(fpt,40,"%-40s\n",buf);



fprintf(fpt,"%-40s\n",buf);

の誤り。度々すいません。
    • good
    • 0
この回答へのお礼

ありがとうございました。私がやりたいこと的中に当たりました。早速アドバイス通りにプログラム実行してみたのです。作られたファイルをレコード長の長さを確認するため以下のプログラムを作りました。 while(fgets(buf,REC,fp)!= NULL){
len = strlen(buf);
printf("レコード長 :%d\n",len);
}
結果は
レコード長 :39
レコード長 :2
というふうになったですが、これはどいう意味か、教えて頂ければ嬉しいのですが。。。もしかしたら長さ2のところは改行と終端文字の値でしょうか?
ちなみに、C言語を詳しくなるため、お勧めの本があれば教えて頂きたいです。よろしくお願いします

お礼日時:2009/06/01 15:43

蛇足な追記。



今回のように「40文字の幅に左詰めでファイルに書く」のではなく「40文字以下の、n文字の空白文字の文字列」が欲しいなら

char * str = (char *)malloc(n+1);
memset(str, ' ', n);
str[n] = '\0';
(中略)
free(str);

でも構わないけど、もっと簡単に

char *str;
str = " (ここに空白を40文字並べる:注参照) " + 40 - n;

でOK(但し、strが指す文字列は読み込み専用なので書き換えできない)

注:このサイトで投稿すると「連続した複数の半角空白」は「1つの半角空白」に勝手に直されて投稿されるので40個の空白を並べて書けません。実際にプログラムに書く時は「40個の空白を並べて」書いてください。
    • good
    • 0

>これは何がだめですか?



strにn文字分の' 'を埋め込むまでは良いが、そのstrに終端文字の'\0'を付けてないのがダメ。

厳しい事を言えば、fscanfを使うのもダメだし、memsetを使うのもダメだし、mallocを使うのもダメだし、mallocがエラーを返してくるのをチェックしてないのもダメだし、ちゃんとfreeしているか明記してないのもダメ。

で、どうして失敗しているのかと言うと、例えば
1行目が30文字
2行目が20文字
3行目が35文字
だったとしよう。

1行目は、nが40-30で10、+1して11文字のバッファがmallocで返される。

そこに10文字の' 'を埋める。

運良く、mallocが返したメモリは「最初は全部ゼロで埋まってる」ので、strには「10文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。

それを「30文字のbufにstrcatする」ので「30文字+10文字」になる。

そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには10文字の空白が残ってる)

一見、1行目は正しく動く。

2行目は、nが40-20で20、+1して21文字のバッファがmallocで返される。

そこに20文字の' 'を埋める。

運良く、mallocが返したメモリは、さっき使ったメモリと同じ場所なので「10文字の空白と、11文字目以降全部ゼロで埋まってる」ので、strには「20文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。

それを「20文字のbufにstrcatする」ので「20文字+20文字」になる。

そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには20文字の空白が残ってる)

一見、2行目も正しく動く。

3行目は、nが40-35で5、+1して6文字のバッファがmallocで返される。

そこに5文字の' 'を埋める。が、そこには「前回mallocして、使用後にfreeした、使い終わったメモリの残骸」があるので、その残骸には「既に20文字の空白が埋まっている」ので、5文字の' 'を埋めても意味は無い。

残念ながら、mallocが返したメモリは、さっき使ったメモリと同じ場所なので「20文字の空白と、21文字目以降全部ゼロで埋まってる」ので、strには「20文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。空白を5文字しか埋めてないのに。

それを「35文字のbufにstrcatする」ので「35文字+20文字」になる。5文字の空白を足すつもりで、終端文字を付け忘れた所為で、20文字の空白が足されちゃう訳だ。

そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには20文字の空白が残ってる)

そして、質問者さんは

>全部40という長さになっていない、40超えるものも多数出ました。

って悩む事になる。

とりあえずの修正なら

memset(str, ' ', n);

の直後に

str[n] = '\0';

ってのを追加すれば大丈夫。

で、mallocとかstrlenとかmemsetとか使わず、最も簡単に済ます方法は、以下の通り。

char buf[260] /* scanfを行うのに充分な大きさを確保しておく。もちろん、buf[41]ではダメ */
・・・・・(省略)
buf[40] = '\0'; /* 元々のbufが40文字以上だった時に41文字目以降を捨てる。これを忘れると41文字以上の行が出来てしまう */
fprintf(fpt,40,"%-40s\n",buf); /* 左揃えで40文字の幅に書き込めば終わり */

で、本当の所を言うと「buf[40] = '\0';の処理で、行末で40バイトに切り捨てる際に、漢字などの2バイト文字の後半バイトだけ切り捨ててしまう」と言う事が起きないように「レコードの末尾に2バイト文字の先頭バイトだけ残る場合は、それを空白に置き換える」と言う処理も必要だったりする。

以下蛇足。

冒頭の「fscanfを使うのもダメ」な理由。

もし「入力ファイル名の指定を間違えて、読みこんだファイルがテキストファイルじゃなく、空白文字や改行コードが出てこないバイナリファイルだった」としたら、scanfは何文字のデータをbufに入れようとするだろう?

「うっかり、5メガバイトある動画ファイル」を入力に指定したら?そして、その5メガバイトの中に「空白文字や改行コードが1つも無い」としたら?

scanfは、言われた通り、bufに「空白文字や改行コードが現れるまで読みこもう」とするだろう。

そして「bufのアドレスに5メガバイトのデータを読み込もう」として、プログラムが落ちてしまう。メモリをグチャグチャに壊したままで。

なので「絶対の保障」が無い限り、scanfやfscanfは使っちゃいけない。
    • good
    • 0

単に、


fprintf(fpt,"%-40s\n",buf);
でいいんじゃないかと。
    • good
    • 0

#include <stdio.h>


#include <string.h>

#define BUFSIZE 10000
#define WIDE 40

int main(int argc, char *argv[])
{
FILE *fp;
char buf[BUFSIZE];
int n;
if (argc == 1) {
fprintf(stderr, "USAGE: %s <input>\n", argv[0]);
return 1;
}
if (NULL == (fp = fopen(argv[1], "r"))) {
perror("fopen error");
return 1;
}

while (NULL != fgets(buf, BUFSIZE, fp)) {
while (buf[strlen(buf)-1] == '\n' || buf[strlen(buf)-1] == '\r') {
buf[strlen(buf)-1] = '\0';
}
for (n = strlen(buf); n < WIDE; n++) {
buf[n] = ' ';
}
buf[WIDE] = '\n';
buf[WIDE+1] = '\0';
fprintf(stdout, "%s", buf);
}
return 0;
}

ポイントは文字列の長さと改行コード'\n'と終端文字'\0'
    • good
    • 0

質問の内容も、何をやりたいのかも、何故やりたいのかも、何がわからないのかも、


伝わってきません。

ひとつひとつ整理していってください。

●目的。

●理由。

●わかっている事。

●わからない事。

●期待する動作。

●現状どうなっているのか。


> 可変長から固定長
可変長、固定長と書かれていますが、何を指していますか?
可変長、固定長がどい言うものか理解していますか?

> ある可変長のファイルがあって、
これは、何かのデータを格納してあるテキストファイルか何かですか?

> レコード長を全て40にそろいたい
どう言うファイルレイアウトを想像していますか?
ファイル仕様書にはどう定義されていますか?

> 実行してみたところ、全部40という長さになっていない、40超えるものも多数出ました。
どのようにして確認しましたか?

> fprintfをつかったからだめですか?
ファイルの書式によります。
ファイル仕様書にしたがってください。

> それともmalloc freeにする必要ありますか?
判断にあたいするだけの情報が提供されていないようです。
これでは、回答できません。

> まったく検討つかないです。
状況を整理する事からはじめてください。
わかっている事とわかっていない事の切り分けなど進めると、
もう少し有効的な質問が書ける様になるかと思います。

●以下憶測で作成したサンプルプログラムです。
----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
  char input_filename[] = "input.txt";
  char output_filename[] = "output.bin";
  FILE *pInputFile = NULL;
  FILE *pOutputFile = NULL;
  char inputBuffer[1024];
  char editBuffer[40];
  int editBufferLen = 0;
  
  /*
   * ●ファイルを開く
   */
  pInputFile = fopen(input_filename, "r");
  if (pInputFile == NULL)
  {
    perror("入力ファイルを開けませんでした.");
    exit(1);
  }
  pOutputFile = fopen(output_filename, "wb");
  if (pOutputFile == NULL)
  {
    perror("出力ファイルを開けませんでした.");
    fclose(pInputFile);
    exit(1);
  }
  
  /*
   * ●ファイルの行毎に処理するためのループ処理
   * ファイル終端に達するまで、繰り返し処理する。
   */
  while (fgets(inputBuffer, sizeof(inputBuffer), pInputFile) != NULL)
  {
    /*
     * ●1行毎の処理
     */
    
    sscanf(inputBuffer, "%s", editBuffer);  /* 行末の改行コードを除去 */
    editBufferLen = strlen(editBuffer);
    memset(editBuffer + editBufferLen, ' ', sizeof(editBuffer) - editBufferLen);
    fwrite(editBuffer, 40, 1, pOutputFile);
  }
  
  /*
   * ●ファイルを閉じる
   */
  fclose(pInputFile);
  fclose(pOutputFile);
  
  return 0;
}
----------------------------------------------------------------------
    • good
    • 0

あぁ。

それ以前にlenの中身は変なのが入っているんじゃ。。。

それと、ついでなので、
コーディングスタイルとして、n のサイズはチェックした方がよいですね。
    • good
    • 0

単純に、改行コードを出力していないからじゃないですか?



fputs() を利用するか、fprintf(fpt,"%s\n", buf)としてみてください。

あと。strcat()は文字列の連結なので、memcpy()を利用した方がよいと思いますよ。
    • good
    • 0
この回答へのお礼

ありがとうございます。memcpyで試してみたいと思います。

お礼日時:2009/05/28 23:17

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qfgetsで拾われる改行文字を削除したい

お世話になります

 C言語初心者のものです。今課題でC言語を用いたプログラミングを
Fedora上でやっています。問題は、fgetsでテキストファイルから、取得
した文字列の中から改行文字を削除できないことです。文字変数のアド
レスはわかっているのですが、終端文字に置換しようとすると、セグメ
ントエラーになってしまいます。これは如何にして解決すべきでしょう
か。よろしくお願いします。

Aベストアンサー

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが最大文字数に達したり、ファイルの最後になったりで、strに改行文字が含まれない場合には、このループは止まりません(Segmentension Falutになって止まる)

・そのような状態になってないか、予めチェックする
・ループを終了させる仕組みを用意しておく
: forの終了条件を記述する、for中で if(*(str+i)=='\0') { break;} 等としておく、等
といった対策が必要です。


あと細かいところを言えば
・strを配列で用意したなら *(s+i)じゃなくてs[i]でいいんじゃないかな
・あるいは char *pみたいにしておいて、 iのループでなく pでループを組む( for(p=str;*p!='\0';p++) )とか。

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが...続きを読む

Q固定長ファイルの書き込み方法(スペースの場合)

固定長ファイルの書込み処理で、スペース(長さ=15)を書込む場合、
どうやってやればいいのでしょうか?
以下のようにやるのは、ダサイですよね?
fprintf(fp, " ");/* スペースを15コ */

sprintf等の %s 等の方法を変えて、出来ないですよね?

Aベストアンサー

> 以下のようにやるのは、ダサイですよね?
> fprintf(fp, " "); /* スペースを15コ */

いいえちっとも。やりたいことが明確で好ましいと思います。
空白の数が変わるのであれば

const char* spaces(int n) /* n個の空白を返す */
を作り、fprintf(fp, spaces(15)); すればいい。

Q構造体の初期化のmemsetの第三引数

memset(&lvitem, 0, sizeof(LVITEM));
memset(&lvitem, 0, sizeof(lvitem));
LVITEMに特化した質問ではなく構造体の初期化について、この2つの書き方によるソース管理やコンパイルでのメリットとデメリットを教えてください。

Aベストアンサー

(1)memset(&lvitem, 0, sizeof(LVITEM));
この書き方は、構造体LVITEMを初期化しているんだな、と分かりやすい。
(2)memset(&lvitem, 0, sizeof(lvitem));
この書き方はとにかく変数lvitemを初期化するんだな、という感じだが、型が分からないので宣言しているところを参照しなければならない。
それともう一つ、ただ単に何回も初期化するんでなければ、こういう書き方もできる。
(3)LVITEM lvitem = {0};

結局どれがいいのかといえば、個人的にはケースバイケースですねえ。初期化を1回すればいいような感じなら(3)、構造体名を明示した方が調べる手間がなくなるようなら(1)、そうでなければ(2)を使います。
コンパイルでのメリット、デメリットは特にないんじゃないかなあ。アセンブラがまだ全盛だった頃ならともかく、いまじゃどのコンパイラだって最適化オプションで同じようなコードはくでしょう。
気にするほどでもないと思うけど…

Qファイルの中身検索と表示

Cでの作成についてなんですが、
fopenでテキストファイルを開き、テキストの中身の
特定文字が含まれている行を新に作ったファイルの中に
書き込みたいんですが、テキストの中身の検索と
抽出方法がいろいろ試してみましたがわかりません。
よろしくお願いします。

Aベストアンサー

★文字列の検索関数を使えば良い。
・テキストの中身の検索は、行単位で文字列を取得(fgets)してから、この文字列内に
 検索文字列の有無をチェックします。この有無は文字列から文字列を検索する関数
 『strstr』関数を使うのが便利です。また、文字列から1文字の検索を行う場合は
 『strchr』関数を使います。質問では、特定の文字が含まれている行となっていますが
・検索するのは1文字、文字群、文字列のどれですか?
 これによっていろいろと代わりますよ。処理が…。
・とりあえず文字列から特定の文字列を含まれた行を抽出するサンプルを下に載せます。

サンプル:
char buff[ 256 ];
char *find = "検索文字列";
FILE *fp; ←読み込み用
FILE *fo; ←抽出用

省略(fopen×2つ)

while ( fgets(buff,sizeof(buff),fp) != NULL ){
 if ( strstr(buff,find) != NULL ){
  fputs( buff, fo );
 }
}

省略(fclose×2つ)

解説:
・上記のサンプルでは、行単位でテキストの中身を取得して、この文字列から検索文字列(find)が
 含まれるか『strstr()』関数でチェックしています。見つかると『NULL』以外を返します。
・そして、見つかったらば行単位で『fputs』関数で出力しています。
 これで見つかった行だけが、新たに作ったファイルに書き込まれていきます。→抽出です。
・また『strstr()』関数の部分を
 『if ( strchr(buff,'a') != NULL ){ … }』とすると『a』の文字が含まれる行のみを抽出します。
 『if ( strspn(buff,"+-*/") != 0 ){ … }』とすると『+-*/』の文字群が含まれる行のみを抽出します。
 検索する文字とは、1文字、文字群、文字列のどれですか?
・以上。おわり。→上記のを参考にして下さい。

参考URL:http://www9.plala.or.jp/sgwr-t/lib/strspn.html,http://www9.plala.or.jp/sgwr-t/c/sec17.html

★文字列の検索関数を使えば良い。
・テキストの中身の検索は、行単位で文字列を取得(fgets)してから、この文字列内に
 検索文字列の有無をチェックします。この有無は文字列から文字列を検索する関数
 『strstr』関数を使うのが便利です。また、文字列から1文字の検索を行う場合は
 『strchr』関数を使います。質問では、特定の文字が含まれている行となっていますが
・検索するのは1文字、文字群、文字列のどれですか?
 これによっていろいろと代わりますよ。処理が…。
・とりあえず文字列から特定...続きを読む

Qバッファとは何ですか

C言語を使用してるとバッファという言葉がよく出てきますがバッファとは何ですか
メモリとは違うものですか
訳をみても緩衝材とか一時的に蓄える場所という意味でよく分かりません
一時的でない使い方も多い気がしますが実際はどういうものですか

Aベストアンサー

#1です

寝ぼけて適当に書いたので修正。

すぐ見つけることができたもので正確なものは英語版ですがこちらくらいかも。
Data buffer - Wikipedia (en.)
http://en.wikipedia.org/wiki/Data_buffer

一応簡単なものはこちらです。
バッファとは - e-Wrods
http://e-words.jp/w/E38390E38383E38395E382A1.html

「複数の機器やソフトウェアの間でデータをやり取りするときに、処理速度や転送速度の差を補うためにデータを一時的に保存しておく記憶装置や記憶領域のこと。」
が現在の基本定義です。処理速度・転送速度の差のための緩衝材的な意味です。

昔はソフトウェアとハードウェア間に使うデータでソフトウェア側がデータを受け取るか、整形して送信するときに使うメモリ領域が基本的にバッファでした。
マルチプロセッサ・マルチタスクの時代になってくると、ソフトウェア間の処理速度の違いを吸収するために使うメモリ領域にもバッファという言葉が使われるようになりました。ソフトウェア間で逐次(FIFO)処理されるデータのためのメモリ領域がこちらの使われ方の主戦場といったところでしょうか。

ソフトウェア間でただ一括転送されるデータならバッファという言葉は誤用ということになるのですが、よく誤用されます。

#1です

寝ぼけて適当に書いたので修正。

すぐ見つけることができたもので正確なものは英語版ですがこちらくらいかも。
Data buffer - Wikipedia (en.)
http://en.wikipedia.org/wiki/Data_buffer

一応簡単なものはこちらです。
バッファとは - e-Wrods
http://e-words.jp/w/E38390E38383E38395E382A1.html

「複数の機器やソフトウェアの間でデータをやり取りするときに、処理速度や転送速度の差を補うためにデータを一時的に保存しておく記憶装置や記憶領域のこと。」
が現在の基本定義です。処理速度・転送速...続きを読む

Qファイル出力で改行を入れたい!

data.txtにファイル出力を行うのですが、gets(c)で入力した一語一語に改行を入れてdata.txtに出力したいのですがどうすればよいでしょうか。
返答のほどよろしくお願いいたします。
include<stdio.h>
#define SIZE 20
void main(void)
{
char c[SIZE], *p;
FILE *fp;

fp = fopen("data.txt","w+");
p = c;
while(gets(c) != NULL){
fputs(p,fp);
}

fclose(fp);
printf("data.txtへ出力しました\n");

}

Aベストアンサー

一語一語ってのは、1回の入力毎って事でしょうか?
とするならば、これでどうでしょうか?

#include <stdio.h>
#define SIZE 20
void main(void)
{
  char c[SIZE], *p;
  FILE *fp;
  
  fp = fopen("data.txt","w+");
  p = c;
  while(gets(c) != NULL){
    fputs(p,fp);
    fputc('\n', fp);
  }
  
  fclose(fp);
  printf("data.txtへ出力しました\n");
  
}

Q構造体を指すポインタからその中のポインタ変数が指す要素の参照

構造体を指しているポインタから、
その構造体内にあるポインタ変数の指している要素に値を代入する方法が、どうしてもわかりませんでした。
どなたか助けてください、お願いします。


struct Kouzou
{
int* p;
};

int main()
{
Kouzou kou;
Kouzou* k_p;

k_p = &kou;

k_p -> *p = 10; //エラー
}

Aベストアンサー

*k_p->p = 10;

ですけど、pが指す先を定義しておかないと
エラーになりますよ。

Q可変長構造体をファイルから読み込み処理

可変長の構造体、
typedef struct 構造体(仮)
{
char c1,c2;
float f1,f2;
double d;
int size;//↓strのサイズ
char str[1];//文字配列
}構造体(仮);

の形式で書かれたバイナリデータファイルがあります。

そのファイルを読み込んでcsv形式で出力する処理を、
ファイルからの読み込む回数を減らしてやりたいと思っています。

その方法を教えていただけませんか?
よろしくおねがいします。

Aベストアンサー

>mallocで取れるだけ領域を取ってそこからその
処理系や環境によりますがWindowsやLinuxの場合100Mぐらいは余裕でとれてしまいます。

>構造体の途中からfreadすることって可能でしょうか?
可能です。
memcpy( (char*)s+読み込んだバイト , pBuf , structSize - (読み込んだバイト) );
みたいな感じになります。
sizeを読み込んでから切れる場合と、sizeの前で切れる場合があるので
sizeの前で切れた場合は次のバッファを読み込んでから構造体のサイズを確定する処理
をする必要があるでしょう。

ただ構造体のalignmentには注意してください。
例えば
typedef struct _S{
char a;
short b;
}S;
の場合3バイトとは限りません。コンパイラの設定によっては
paddingの部分で切れる可能性も考慮する必要があります。

Qテキストファイルから文字列を読み込んで二次元配列に格納するには

毎回お世話になっています。

ただいまC言語を勉強中の学生です。

基本的な文法は頭に入れたつもりでしたが、まだポインタと ファイルの入出力に苦手意識があり 実際うまくできないでいるので

どなたか ご存知の方にアドバイスいただければと思って書き込ませていただきます。

今回練習しているのは 先に述べたように、テキストファイルに

abc
def
ghi
jkl

などと文字列を書いておき、そのファイルを読み込んで
二次元配列に格納したいのですが、何を勘違いしているのか、
うまく実現できません。

参考書などを見て 一次元配列へ格納する場合は できたようなのですが、今後このプログラムを発展させる段階で 二次元配列に文字列を
格納できたほうが好都合なので 今模索しています。。。

ちなみに 一次元配列に格納する場合は 以下のように書きました。


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

main()
{
char a[10];

FILE *fp;

fp = fopen("word.dat", "r");

if (fp == NULL)
{
printf("File does not exist.\n");
exit(1);
}

while(fscanf(fp,"%s",a)==1){
printf("%s\n",a);
}

fclose(fp);

return 0;
}

実はコレに関しても分からない点があり、それは 目的の配列変数
a[]に格納できたのだから それを確認したいと思い
printfで a[0]~a[10]を表示してみようとしたところ 変に文字化け
したものが表示されたり、何も表示されなかったり よく分からないことが起きます。私は何を勘違いしてしまっているのでしょうか(>_<)

長々と書いてしまいましたが、今回 御教授いただきたいのは

(1)、テキストファイルから 英字の文字列を読み込んで二次元配列に格納するために用いるのに 一番最適な関数はどれか(fscanfやfgetsなど) そして、その関数を使って 一番シンプルな記述をするにはどのように記述すればよいのか

(2)、上のようなプログラムの書き方で、配列aにテキストファイルから読み込んで格納できたようなのに、a[0]~a[10]をprintfしたときに
うまく中身が表示されないのはなぜか。

という点なのですが、(1)をとりあえず急いでおりますので、(2)はおまけ程度に考えていただければと思います。

なぜかファイルの入出力がいまだに把握できなくて苦手としておりますので、どうか よろしくお願いいたします。

毎回お世話になっています。

ただいまC言語を勉強中の学生です。

基本的な文法は頭に入れたつもりでしたが、まだポインタと ファイルの入出力に苦手意識があり 実際うまくできないでいるので

どなたか ご存知の方にアドバイスいただければと思って書き込ませていただきます。

今回練習しているのは 先に述べたように、テキストファイルに

abc
def
ghi
jkl

などと文字列を書いておき、そのファイルを読み込んで
二次元配列に格納したいのですが、何を勘違いしているのか、
うまく実現...続きを読む

Aベストアンサー

#include <stdio.h>
int
main(void)
{
char a[10][10];
int i, maxi;
FILE *fin;

if ((fin = fopen("test.dat", "r")) == NULL){
fprintf(stderr, "Can't open file\n");
}
for (i = 0; i<10; i++) {
if (fgets(a[i], 10, fin) == 0) {
maxi = i;
break;
}
}
for (i = 0; i < maxi; i++) {
printf("a[%d] = %s", i, a[i]);
}

fclose(fin);
return 0;
}

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。


人気Q&Aランキング