
ファイルから文字を読み込んで
それを配列に入れて辞書順にソートさせようとしています。
それで、ソート以前の問題なのですが、ファイルから文字列を読み込んで配列にいれようとするのですが、
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
No.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;
}
回答ありがとうございました!!
おかげで、うまく配列に入れることができ、
配列に入れてしまってからは上手く辞書順にソートすることができました!!
ポインタのポインタというのは思いつきませんでした。
こういう使い方をはじめてしたので勉強になりました。
わざわざ一行ずつ詳しい解説も書いていただき、非常にさんこうになりました!
本当にありがとうございました!(^-^)
No.5
- 回答日時:
#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;
}
No.4
- 回答日時:
う~ん, リストは示しませんが,
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

No.3
- 回答日時:
#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の外側で宣言して下さい。

No.2
- 回答日時:
提示されている方法では、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次元配列を使った方が分かりやすいですかね?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# 至急お願いします。C言語で.imgのファイルを読み込んで1バイトづつ出力するプログラムを作りたいので 3 2023/01/16 22:49
- Visual Basic(VBA) 3つのプロシージャをまとめたら実行時エラー発生で対応不能 6 2022/05/17 01:47
- Excel(エクセル) 配列操作について 5 2023/04/18 07:27
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
C# Listを使わずに2次元配列の...
-
先頭アドレスとは何ですか?
-
構造体とポインタ配列
-
C++ vectorに配列をプッシュしたい
-
メモリの初期値
-
C言語でのカンマ区切りについて
-
配列で格納したものをmsgboxで...
-
配列をEraseしてもメモリが開放...
-
mallocの確保要素数の限界は?
-
VBAで配列からbmp画像を出力す...
-
配列を使わずに、変数名を動的...
-
テキストファイルから文字列を...
-
ExcelVBAで質問です。離れた二...
-
Functionの戻り値を2次元配列...
-
VBからDLLへ多次元配列を渡す/...
-
自販機での金銭収受を想定した...
-
unsigned char の配列で途中で0...
-
多次元配列のポインタ渡し
-
VBでC言語のポインタみたい...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
C# Listを使わずに2次元配列の...
-
【速いブラインドタッチ】手を...
-
配列をEraseしてもメモリが開放...
-
テキストファイルから文字列を...
-
先頭アドレスとは何ですか?
-
配列で格納したものをmsgboxで...
-
複数の選択範囲の行番号を個別...
-
C# 配列の変数宣言について。
-
C++ vectorに配列をプッシュしたい
-
配列を含む構造体の初期値について
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
キーボードのキー配列について
-
ExcelVBAで質問です。離れた二...
-
2次元配列を戻り値とする関数?
-
unsigned char配列への入力の仕方
-
【C言語】配列の中に配列を入れ...
-
Redimした動的配列はEraseする...
おすすめ情報