- ベストアンサー
ファイルの読み込みとメモリ確保について。
ファイルから文字を読み込んで それを配列に入れて辞書順にソートさせようとしています。 それで、ソート以前の問題なのですが、ファイルから文字列を読み込んで配列にいれようとするのですが、 buffを動的にメモリ確保してその配列に入れたいと考えているのですが、なぜか入ってくれません。 whileでファイルの終わりがくるまで一行ずつ読み込んで それをsに入れていき、sをbuff[]の配列に順番にいれていこうとしているのですが・・・。 ファイルは aaaa aabc dda wer zie ced sdfe be など適当な文字の並びです。 malloc関数で動的に確保したメモリはその後普通の配列と同様に使えるのではなかったのでしょうか? なので普通にbuff[i]=s;といった処理で入れれると思ったのですが。 ファイルは一行の長さの最大が100で 行数が4000行あると仮定しています。 今は小さいファイルでテストしていますが。 以下ソースです。 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 #define MAX_LINE 4000 main() { FILE *fp; char *buff,s[MAX_SIZE]; int i; fp=fopen("words.txt","r"); buff=(char*)malloc(sizeof(char)*MAX_LINE); i=0; while(fgets(s,MAX_SIZE-1,fp)!=NULL){ buff[i]=s; printf("%s",buff[i]); i++; } fclose(fp); } とりあえずファイルの内容を配列に入れないとソートできないので、配列に全て入れてしまいたいと考えています。 間違いがどこにあるのか指摘よろしくおねがいします。m(-_-)m
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
訂正と補足です。 半分寝ぼけて書いたコードなので最後のメモリの解放の部分が間違ってました。 > free( buff ); for( i = 0; i < MAX_LINE; i++ ){ free( buff[i] ); } free( buff ); さて、まず問題はファイルから一行ずつ単語を読み込んでバッファへ保存しておき、 データをソートする。 その際にファイルから読み込んだ一行の単語を動的に用意した配列へうまく保存でき ないということでしたね。順を追ってみていきましょう。 #define MAX_SIZE 100 #define MAX_LINE 4000 char *buff, s[MAX_SIZE]; まず、大前提として「C言語では文字列は char 型の配列で表す」ということがあり ます。つまり、文字列データ型を標準ではサポートしてないのです。 確か、「ファイルから文字列を一行読み込んでそれを配列に保存する」ということを したいんでしたね。 と、いうことはただ単に配列を用意するのではなく「文字列を格納する配列」の配列 を用意してやる必要があります。 #define MAX_SIZE 100 #define MAX_LINE 4000 char** buff; char s[ MAX_SIZE ]; 配列の配列のためのポインタ変数は「ポインタのポインタ」を用意してやります。 この変数へ文字列保存用へ確保したバッファのアドレスを入れてやるのです。 buff=(char*)malloc(sizeof(char)*MAX_LINE); という風になっていますが、配列の配列を用意してやるので少し手順が変わってき ます。 buff = ( char** )malloc( sizeof( char* ) * MAX_LINE ); まず、それぞれ確保する文字列バッファのアドレスを格納するためのポインタ配列 を確保してやります。 for( i = 0; i < MAX_LINE; i++ ){ buff[i] = ( char* )malloc( sizeof( char ) * MAX_SIZE ); } その後に、それぞれのポインタに文字列のためのバッファへのアドレスを配列に保 存してやることで「文字配列の配列」が確保できるわけです。 確保したメモリは使い終わったらちゃんと解放してやる必要があります。さもない とメモリリークの原因となってしまいますから。 具体的にどうするかというとメモリの確保と逆の手順を踏みます。 for( i = 0; i < MAX_LINE; i++ ){ free( buff[i] ); } ポインタ配列に入っているそれぞれの文字列用のバッファを解放する。 free( buff ); 最後にポインタ配列を解放する。 さて、次に文字列のコピーですが、先に述べた通りC言語では文字列をサポートし ていません。 buff[i]=s; なので、ただ単に代入するだけではコピーできません。 for( j = 0; j < MAX_LINE; j++ ){ for( i = 0; i < MAX_SIZE; i++ ){ buff[j][i] = s[i]; } } このようにする必要があります。が、標準関数に文字列をコピーする関数が用意さ れていますのでそれを使いましょう。 その関数は string.h で定義されている strcpy 関数や stdio.h 出て異議されて いる sprintf 関数です。 sprintf 関数のほうが何かと使い勝手がいいのでこれを使います。 sprintf( buff[i], "%s", s ); この関数は printf 関数と同様にコンソール画面ではなくバッファへ文字列を出力 してくれます。 以上を踏まえたコードが先に投稿したソースです。 さて、ここでは単語の配列を文字配列の配列で扱いましたが、コードが結構うざく なったりします。 そんなときは文字配列を構造体として扱うとすっきりするかと思います。 #define MAX_SIZE 100 typedef struct { char str[ MAX_SIZE ]; } WORDS; WORDS* buf; buf = ( WORDS* )malloc( sizeof( WORDS ) * MAX_LINE ); sprintf( buf[i].str, "%s", str ); 一応サンプルコードを提示しておきます。 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 #define MAX_LINE 4000 typedef struct { char str[ MAX_SIZE ]; } WORDS; int main(void){ FILE* fp; WORDS* buf; char str[ MAX_SIZE ]; int i; if( ( fp = fopen( "words.txt", "r" ) ) == NULL ){ return -1; } buf = ( WORDS* )malloc( sizeof( WORDS ) * MAX_LINE ); for( i = 0; i < MAX_LINE; i++ ){ if( fgets( str, MAX_SIZE - 1, fp ) == NULL ){ break; } sprintf( buf[i].str, "%s", str ); printf( "%s", buf[i].str ); } free( buf ); fclose( fp ); return 0; }
その他の回答 (5)
- HogePiyo
- ベストアンサー率57% (24/42)
#include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 #define MAX_LINE 4000 int main(void){ FILE* fp; char** buff; char str[MAX_SIZE]; int i; if( ( fp = fopen( "words.txt", "r" ) ) == NULL ){ return -1; } buff = ( char** )malloc( sizeof( char* ) * MAX_LINE ); for( i = 0; i < MAX_LINE; i++ ){ buff[i] = ( char* )malloc( sizeof( char ) * MAX_SIZE ); } for( i = 0; i < MAX_LINE; i++ ){ if( fgets( str, MAX_SIZE - 1, fp ) == NULL ){ break; } sprintf( buff[i], "%s", str ); printf( "%s", buff[i] ); } free( buff ); fclose(fp); return 0; }
- Tacosan
- ベストアンサー率23% (3656/15482)
う~ん, リストは示しませんが, 1.ファイルの内容を一度 char の配列に入れてしまう. 2.各行の先頭を char * の配列に記憶する (ついでに各行の終端を '\0' に変える). 3.各行をソートする. の順でいけませんかね. 1.はあらかじめファイルの大きさがわかっていれば簡単 (fread) ですし, 2.も例外的な状況を考えなければわりと簡単にできるはずです. 2次元配列を使うなら char **lines; lines = malloc(MAX_LINE * sizeof lines[0]); for (i = 0; i < MAX_LINE; i++) { lines[i] = malloc(MAX_SIZE); } のようにメモリを確保しておく方が使い勝手はよいです. あと, buff という名前の配列に対しては &buff[i] と buff+i が同じ意味になります. ちなみに 「char buff[10000]ととっておいて, buff[0], buff[100]は, 問題ありませんが, ポインターに対しては, このような使い方はできません.」などと書かれていますが, char *buff でも十分なメモリを確保していれば全く同じように使えます... というか, 使えないと char buff[10000] のときに問題になる.
お礼
回答ありがとうございます。 なるほど、freadでできるんですね、fgetsよりそっちのほうがいいかもしれませんね。 &buff[i] と buff+i が同じなんですね、今理解できました。 ありがとうございました。m(-_-)m
- tatsu99
- ベストアンサー率52% (391/751)
#2です。 >buff[0]とかbuff[100]とかの書き方ではダメなのですか? buffはchar *buffと宣言しています。 buffはポインターですから、buff+jがbuffの先頭+jの位置を示します。 char buff[10000]ととっておいて、buff[0],buff[100]は、問題ありませんが、ポインターに対しては、このような使い方はできません。 >いっそのこと、2次元配列を使った方が分かりやすいですかね? mallocで4000×100バイトを確保すること自体が、考え方としては2次元配列の考え方になっています。 char buff[4000][100];として領域を確保すれば、本来の2次元配列になります。考え方としては、これが最もシンプルです。 strcpy(buff[i],s)で、データが格納出来ます。 但し、char buff[4000][100];はmain関数の内部で宣言するとサイズが大きすぎる為(たぶん)エラーになるでしょう。mainの外側で宣言して下さい。
お礼
再度回答ありがとうございました。
- tatsu99
- ベストアンサー率52% (391/751)
提示されている方法では、4000個×4バイトのメモリを割り当てているだけですので、あなたの望んだ結果は得られません。 1.連続したメモリに格納するためには、メモリは4000行×100バイト必要になります。 2.上記で確保したメモリを100バイト単位で1行毎に使用します。 以下は、そのように修正した結果です。 ----------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_SIZE 100 #define MAX_LINE 4000 #define ALLOC_SIZE (MAX_LINE*MAX_SIZE) main() { FILE *fp; char *buff,s[MAX_SIZE]; int i; int j; fp=fopen("words.txt","r"); buff=(char*)malloc(ALLOC_SIZE); i=0; while(fgets(s,MAX_SIZE-1,fp)!=NULL){ j = i*MAX_SIZE; strcpy(buff+j,s); printf("%s",buff+j); i++; } fclose(fp); }
お礼
回答ありがとうございます。 for(i=0;i<40;i++){ printf("%d\n",i*100); printf("%s",buff+(i*100)); } をwhileの外に入れて確認したところ、100ずつの区切りで ちゃんと格納されていることがわかりました。 ですが、buff[0]とかbuff[100]とかの書き方ではダメなのですか? buff+(i*100)など+でその場所を表現するのを初めて見たもので・・・。 イマイチ、なぜこういう書き方をするのか正直わかりません。 とりあえずそういう風に指定したら入っているということは確認できましたが。 いっそのこと、2次元配列を使った方が分かりやすいですかね?
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
buff[i] の型は char s の型は char[] 両者の型が異なります。
お礼
回答ありがとうございます。 エラーメッセージの出る原因はそれだったんですね。 同じcharでも違うんですね、知りませんでした。
お礼
回答ありがとうございました!! おかげで、うまく配列に入れることができ、 配列に入れてしまってからは上手く辞書順にソートすることができました!! ポインタのポインタというのは思いつきませんでした。 こういう使い方をはじめてしたので勉強になりました。 わざわざ一行ずつ詳しい解説も書いていただき、非常にさんこうになりました! 本当にありがとうございました!(^-^)