1レコード19バイトのファイルを
読み込む処理を行っています。
地区名10バイト
県名8バイト
改行1バイト
このデータをdouken(構造体)に格納したいのですが
>while (fgets(dou,19,fp) != NULL){
で、エラーになってしまいます。
どのようにしたら
ファイルから読み込んだデータを
構造体に格納できますか?
#include<stdio.h>
#include <stdlib.h>
struct douken {
char tiku[10];
char ken[8];
}
main(void){
FILE *fp;
struct douken dou[100];
int i;
fp = fopen("ex3.fil","rb");
if ( fp == 0 ){
printf("can't open\n");
exit(1);
}
while (fgets(dou,19,fp) != NULL){
・
・
・
No.5ベストアンサー
- 回答日時:
>>while (fgets(buffer,20,fp) != NULL){
>と、するということですか?
>その場合、
>ここのサイズは必ず4の倍数になるということですよね?
構造体を直接扱うと、アーキテクスチャやコンパイラ依存してしまいます。
32bit機なら4byteですし、16bit機なら2byte。64bit機なら8byteです。
また、コンパイラの設定によってもどのように確保されるかまったく分からないのです。
一度バッファに蓄えてからmemcpyでコピーする方が安全ですし、可搬性があります。
C言語では\0を文字列の終端文字として使用しているので、10文字格納したいなら11byte確保する必要もあります。
簡単に修正してみました。
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct douken_ {
char tiku[11];
char ken[9];
} douken;
int main(void){
FILE *fp;
douken dou [100];
char buff [18 /* douken */ + 1 /* LF(\n) */ + 1 /* \0 */];
int i;
i = 0;
fp = fopen("ex3.fil","rb");
if ( fp == 0 ){
printf("can't open\n");
exit(1);
}
// douを\0で埋める
memset (dou , '\0' , sizeof dou);
// 一度バッファに格納
while (fgets(buff,sizeof buff,fp) != NULL){
// memcpy関数でコピー
memcpy(&dou[i],buff,10);
memcpy(&dou[i],buff+10,8);
// 構造体配列より大きなファイルを開いたときの配慮
if (i == 99) break;
i++;
}
return 0;
}
この回答への補足
ご返事有り難うございました。
参考のプログラムまで書いて頂いたので
とても勉強になりました。
参考のプログラムで
幾つか質問があるのですが…
1.
>typedef struct douken_ {
>char tiku[11];
>char ken[9];
>} douken;
どうして「typedef」というのを
入れたのかが分かりません。
本を読むと
構造体の場合は省略することが出来ると
書かれてあったのですが…。
どのような利点があるか
教えて下さい。
2.
// douを\0で埋める
memset (dou , '\0' , sizeof dou);
どのような理由で
douを\0で埋めるんですか?
3.
確認をするために
一番最後のところでprintfを書いてみました。
>if (i == 99) break;
>i++;
printf("%s",dou[i]);
と、したところ
「nullnullnull・・・」
となりました。
printf("%s",dou[i].tiku);
としたところ
何も表示されませんでした。
ファイルの中身は漢字なんですが
そこが問題なのでしょうか?
それとも別の原因があるのでしょうか?
面倒をお掛けしますが
よろしくお願いします。
No.9
- 回答日時:
#7です
--------------------------------------
douP = dou;
while ((fgets((char*)douP++,sizeof(struct douken),fp)) != NULL)
{}
---------------------------
i=0;
while ((fgets((char*)dou[i++],sizeof(struct douken),fp)) != NULL)
{}
---------------------------
でも、同じです。ポインタか配列かの違いだけです。
コンパイラの性能等にもよりますが、上記のポインタを使ったほうが
若干早いか、コードが小さくなる場合が多いです。
No.8
- 回答日時:
構造体を fgets で読もうとしてるのがたぶん変。
(構造体のサイズは、sizeof で分かるんだけど。)
そもそもファイルはテキストでしょうかバイナリでしょうか。テキストなら、構造体の要素 tiku や ken をそれぞれ読んでそれを構造体に入れるといいのでは。
この回答への補足
ご返事が遅れましてすいません。
お陰様で随分と理解を深めることが出来ました。
>そもそもファイルはテキストでしょうかバイナリでしょうか。
テキストです。
>テキストなら、構造体の要素 tiku や ken をそれぞれ読んで
>それを構造体に入れるといいのでは。
ということは、
shirousa01さんが回答して頂いたように
>while (fgets(buff,sizeof buff,fp) != NULL){
と、いったんバッファに蓄えて
>memcpy(&dou[i].tiku ,buff,10);
>memcpy(&dou[i].ken ,buff+10,8);
と、入れればよいと言うことでしょうか?
ご面倒かとは思いますが
ご教授して頂けたら幸いです。
No.7
- 回答日時:
[admin@opteron99] ~/gcctext
$ less ex3.fil
12345abcdefgh^M一二三四五ABCD^M1234567890IJKLMNOP
[admin@opteron99] ~/gcctext
$ cat test5.c
#include<stdio.h>
#include <stdlib.h>
#define NofLine 3
#define SizeofLineEnd 1
#if MSVCC
#pragma pack(push,1)
struct douken {
char tiku[10];
char ken[8];
char crlfNULL[SizeofLineEnd+1];
} ;
#pragma pack(pop)
#endif
#if GNUGCC
struct douken {
char tiku[10];
char ken[8];
char crlfNull[SizeofLineEnd+1];
} __attribute__((packed));
#endif
int main(void){
FILE *fp;
int i;
struct douken dou[NofLine];
struct douken *douP;
printf("struct douken SIZE = %d\n",sizeof(struct douken));
printf("dou SIZE = %d\n",sizeof(dou));
fp = fopen("ex3.fil","rb");
if ( fp == 0 ){
printf("can't open\n");
exit(1);
}
douP = dou;
while ((fgets((char*)douP++,sizeof(struct douken),fp)) != NULL)
{}
douP = dou;
for(i=0;i<NofLine;i++)
{
printf("Line%d:=",i+1);
printf("tiku:%10.10s:",douP->tiku);
printf("ken:%8.8s\n",douP->ken);
douP++;
}
printf("end\n");
exit(0);
}
[admin@opteron99] ~/gcctext
$ cc -DGNUGCC -g -Wall test5.c
[admin@opteron99] ~/gcctext
$ ./a.exe
struct douken SIZE = 20
dou SIZE = 60
Line1:=tiku:12345:ken:abcdefgh
Line2:=tiku:一二三四五:ken:ABCD
Line3:=tiku:1234567890:ken:IJKLMNOP
end
[admin@opteron99] ~/gcctext
$
-----------------------------------------------------------------------------
Cygwin ShitJIS 改行文字は、CRのみとしています。
直接構造体にとる方法は、独自プロトコルのデータのやり取り等で
比較的よく使われると思っています。(使っていました。)
CPUやコンパイラの仕様によりアライメントに注意は必要ですが、
きっちりコンパイラに指定すれば、できないなんてことはないでしょう。
指摘事項について、ご確認されていないようですので回答しますと
fgetsは、第2引数sizeよりも1バイト少ないデータをストリーム
から読んで、第1引数のアドレスに書く、ただし、EOFまたは改行で
終わる。最後にNULLを書く。
ということなので、fgetsのwhileで取得するには、
tikuが10バイト、kenの8バイト、改行1文字(貴殿の指定により1文字としました。
Windowsなら2バイトですね)、NULL1バイトの計20バイトの領域が必要になります。
ですので、18を指定していたことと、領域の定義が不足していたことが貴殿のプログラム
の問題のポイントだと思います。また他にもdouを100個の配列にしていますが
まったくケアされていないこともバグのひとつですね。
上記プログラムもその2点とキャストをいれたぐらいです。
それからアライメントの指定を追加しているだけです。
それから、
改行およびNULLは飛ばして構造体に入れたいのであれば、fgetsでは20バイトのバッファ
に取得し、18バイトを構造体にコピーするほうが、無駄なメモリを取らないので
いい方法ですね。
その場合は、doukenから、crlfNullのメンバーを削除し、buffを定義(20byte)、
データ取得の部分を以下に置き換えるといいでしょう。
while ((fgets(buff,sizeof(buff),fp)) != NULL)
{
memcpy(douP++,buff,sizeof(struct douken));
}
この回答への補足
ご返事が遅れましてすいません。
折角回答して頂いたので
なんとか理解しようと頑張ってみましたが
初心者の私には難しかったです。
ごめんなさい。
早くこのプログラムが理解できるように
勉強していきたいと思います。
一つだけ質問させて頂きたいのですが…。
>struct douken dou[NofLine];
>struct douken *douP;
普通に宣言した後に
ポインタで宣言していますよね。
これはどのような意味があるんですか?
ご面倒かとは思いますが
ご教授して頂けたら幸いです。
No.6
- 回答日時:
1.どのような利点があるか
構造体を定義すると
struct douken型
という型が定義されますが、好みの問題ですがstructを何度も書くのは面倒な為、構造体の名前を
struct douken_型
として、それをtypedefで
douken型
と再定義しています。
この定義の仕方はよく使われている方法です。
2.douを\0で埋めるんですか?
宣言しただけでは、構造体の中にゴミが詰まっています。
普段は意識しなくても問題ありませんが、ファイルを扱う場合や、構造体の場合、そのデータがバグにつながる可能性があるため、\0で初期化した方がバグが発生しにくいので、\0で初期化しています。
3.確認したところ、いくつか間違いがありました。
char buff [18 /* douken */ + 2 /* CrLf(\n\r) */ + 1 /* \0 */];
memset (dou , '\0' , sizeof dou);
memset (buff, '\0' , sizeof buff);
while (fgets(buff,sizeof buff,fp) != NULL){
memcpy(&dou[i].tiku ,buff,10);
memcpy(&dou[i].ken ,buff+10,8);
if (i == 99) break;
i++;
memset (buff, '\0' , sizeof buff);
}
データは漢字でも問題ありませんが、1文字2バイトになります。
また、encodingの問題がある場合もあります。
私がテストしたところ問題なく動作しました。
この回答への補足
ご返事が遅れましてすいません。
お陰様で随分と理解を深めることが出来ました。
幾つかご確認と質問があるのですが…。
>char buff [18 /* douken */ + 2 /* CrLf(\n\r) */ + 1 /* \0 */];
18というのはtikuとkenを合わせたバイト数
+2というのはtikuとkenを合わせた改行文字数
+1というのは終端データ
を確保していると考えて
宜しいのでしょうか?
>memset (dou , '\0' , sizeof dou);
というのは
douのバイト数分
0を埋めていると言うことでしょうか?
>memcpy(&dou[i].tiku ,buff,10);
ポインタにされていますよね?
実数(この言い方が正しいのか分かりませんが…)
でも、出力できました。
どうしてポインタにされたのでしょうか
ご面倒かとは思いますが
ご教授して頂けたら幸いです。
No.4
- 回答日時:
> 32bit機というのは、どういうことですか?
現在主流のPCのことです。
>> tiku:4バイト + 4バイト + 2バイト + 2バイト(ゴミ)
> どうして地区10バイトが
> このようになるのでしょうか?
データを収納する「箱」が4バイトの大きさだから、です。
tikuに"1234567890"というデータが入っているとすると
実際には
[1234][5678][90??]
という4*3=12バイトの領域を使うことになります。
??の部分が「ゴミ」です。
この回答への補足
ご返事有り難うございました。
お陰様で良く理解することが出来ました。
>while (fgets(buffer,20,fp) != NULL){
と、するということですか?
その場合、
ここのサイズは必ず4の倍数になるということですよね?
No.3
- 回答日時:
構造体ポインタをキャラクタポインタに型キャストすれば可能かとおもいますが、構造体を直接ファイルから扱うのはバグにつながりますよ?
まず、おそらく上記の構造体のサイズは18バイトにならない可能性が高いです。
32bit機の場合、4バイト単位で数値を扱う為
tiku:4バイト + 4バイト + 2バイト + 2バイト(ゴミ)
ken:4バイト + 4バイト
の20バイトになっていると思います。
この回答への補足
ご返事有り難うございました。
少し質問があるのですが…。
>32bit機の場合、4バイト単位で数値を扱う為
32bit機というのは、どういうことですか?
>tiku:4バイト + 4バイト + 2バイト + 2バイト(ゴミ)
どうして地区10バイトが
このようになるのでしょうか?
初歩的な質問かも知れませんが
ご教授して頂けたら幸いです。
No.2
- 回答日時:
fgetsの仕様をよく確認ください。
18+(改行文字数)1+(\0の終端データ)1要求する必要があると思います。デバッガにたよるのもあまりよくありませんが、デバッグされてはいかがでしょうか?
以下、そのままgdbを動作させた場合のログの一部です。
(gdb) run
Breakpoint 1, main () at test4.c:24
(gdb) p dou
$1 = {{tiku = "1234567890", ken = "abcdefgh"}, {tiku = "\000G, ken = "<\000\000\000\004\000\000"}, {tiku = "\004\000\000\000P, ken = "\021\000}, {tiku = ", {tiku = "\000\000, ken = "Uy
(gdb) next
(gdb) next
Breakpoint 1, main () at test4.c:24
(gdb) p dou
$2 = {{tiku = "\n\00034567890", ken = "abcdefgh"}, {tiku = "\000G, ken = "<\000\000\000\004\000\000"}, {tiku = "\004\000\000\000P, ken = "\000\000
(gdb)
ex3.filは
234567890abcdefgh
1234567890ABCDEFGH
1234567890IJKLMNOP
にしています。
環境は、linux で、gcc4.1.1です。
No.1
- 回答日時:
fgets の使い方を見れば「どうしてエラーになるのか」はほとんど明らかだと思うんだけど,
1.fgets は第1引数に char * を要求するけど struct douken * は char * に変換できない
というのが原因だよね. ただ, これは「コンパイラが文句を言ってくれる*たちのよい*エラー」であって, 実際には
2.struct douken は 18バイトかもしれないけど fgets で 19バイト読み込んでいる
という「コンパイラがきっと文句を言わない*質の悪い*エラー」もまぎれこんでいるので注意.
この回答への補足
ご返事有り難うございました。
少し質問があるのですが…。
>1.fgets は第1引数に char * を要求するけど
> struct douken * は char * に変換できない
ということは、
このようにしていったんchar型に格納しないと
いけないということですか?
char buffer[19];
while (fgets(buffer,19,fp) != NULL){
>2.struct douken は 18バイトかもしれないけど fgets で
>19バイト読み込んでいる
地区名10バイト
県名8バイト
改行1バイト
というレコードフォーマットでしたので
19バイトと指定したのですが
18バイトということは
改行は含まないで指定すると言うことですか?
ご教授して頂けたら幸いです。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# c言語の問題です 2 2023/07/21 10:51
- C言語・C++・C# C言語初心者 構造体 課題について 1 2023/03/10 19:30
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
このQ&Aを見た人はこんなQ&Aも見ています
-
新NISA制度は今までと何が変わる?非課税枠の拡大や投資対象の変更などを解説!
少額から投資を行う人のための非課税制度であるNISAが、2024年に改正される。おすすめの銘柄や投資額の目安について教えてもらった。
-
構造体とfscanf
C言語・C++・C#
-
構造体のメンバをfor文で回したい
C言語・C++・C#
-
数字以外が入力されたらエラー文を出したい。
C言語・C++・C#
-
-
4
csvファイルを構造体に格納したいです
C言語・C++・C#
-
5
関数から配列を返すには?
C言語・C++・C#
-
6
Enterキーを押されたら次の処理に移るという事をしたい。
C言語・C++・C#
-
7
ファイルのデータを構造体に代入する方法
C言語・C++・C#
-
8
バッファとは何ですか
C言語・C++・C#
-
9
C言語で、メモリを解放しないで終わるプログラム
C言語・C++・C#
-
10
if文の条件にscanf関数を使うと…?
C言語・C++・C#
-
11
fopne で失敗する原因
C言語・C++・C#
-
12
2つのファイルを比較するC言語プログラムについて
C言語・C++・C#
-
13
C言語で構造体のメンバを簡単に出力する方法ありますか?
C言語・C++・C#
-
14
#include <Windows.h>というヘッダファイルについて
C言語・C++・C#
-
15
c言語 構造体
C言語・C++・C#
-
16
適切な変換関数が存在しない???
C言語・C++・C#
-
17
構造体の勉強中です 合計点の高い順に並べ替えがわかりません
C言語・C++・C#
-
18
C言語 exitの使い方
C言語・C++・C#
-
19
char*を初期化したいのですが
C言語・C++・C#
-
20
リスト構造のソートで悩んでます。。。
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語をコンパイルするとコンピ...
-
mallocについて
-
大量のデータを読み込んで表示...
-
C言語の関数と配列に関する質問
-
質問失礼します。 プログラム言...
-
VisualStudio2022でC言語プログ...
-
c言語
-
double型が正常に認識されてい...
-
c言語
-
システムエンジニアの適正について
-
MACで動く実行ファイルをWindow...
-
gcc13.2のバグ?
-
Notepad++の関数リスト表示でC...
-
gccを行ってもexeファイルが生...
-
トリプトファンってケト原性あ...
-
C言語 列挙型(enum型)変数について
-
C言語 配列と関数の練習問題
-
Bitcoin、BTCはブロックチェー...
-
これなにがちがうんですか??
-
だいがくの電し書籍で
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
gccを行ってもexeファイルが生...
-
あなたは、Excelはどうやって学...
-
WindowsのCapsLock(キャップス...
-
質問失礼します。 プログラム言...
-
double型が正常に認識されてい...
-
Notepad++の関数リスト表示でC...
-
どちのほうがすきですか?
-
Stuck
-
Notepad++の関数リスト表示の変...
-
ArduinoでMouse関数を使用して...
-
C言語の関数と配列に関する質問
-
C言語って古いですか?
-
Linuxでの開発環境構築や設定の...
-
Bitcoin、BTCはブロックチェー...
-
C++6.0でのresource.hについて
-
MACで動く実行ファイルをWindow...
-
C言語 列挙型(enum型)変数について
-
c言語
-
大量のデータを読み込んで表示...
-
こんなことてしますか??
おすすめ情報