ページ 11

fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 14:44
by あざらく
テキストファイルを2次元配列へいれたいです。
今やっているやり方は、一度fgetcで1次配列へ格納し、
それを2次元配列へいれる。感じでしています。

なぜ、fgetsを使わないかというと、crの改行も扱いたいからです。
下に入力処理の関数のコードを載せました。
これで行うと、
*** stack smashing detected ***: ./a.out terminated
となり、エラーになります。

fgetsで読み取り格納したように、fgetcを使う方法を教えてください。

コード:

int input(void)
{

	int word_cnt, loop_word, cnt;
	FILE *fp;
	char getc_word[500];
  char before_date[100][50];
	char get_ch;
	fp = fopen(hk.in_file,"r");
	if ( fp == NULL ) {
		printf("指定された入力ファイルが存在しないか、開けません\n");
		exit (-1);
	}
	printf("1次元配列へ\n");
	for(word_cnt=0; (get_ch = fgetc(fp)) != EOF; word_cnt++) {
		getc_word[word_cnt] = get_ch;
		if (getc_word[word_cnt] == 0x0a){
      		//改行文字を終端文字に置き換える
			getc_word[word_cnt] = '\0';
		}
		if (getc_word[word_cnt] == 0x0d){
    		//改行文字を終端文字に置き換える
			getc_word[word_cnt] = '\0';
		}
		getc_word[word_cnt] = toupper(getc_word[word_cnt]);
	}
	loop_word = 0;
	for(cnt = 0; loop_word < word_cnt; cnt++){
		while(getc_word[loop_word] != '\0'){
			before_date[cnt][loop_word] = getc_word[loop_word];
			loop_word++;
		}
		loop_word++;
	}
	fclose(fp);
	return before_date;
}

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 16:22
by みけCAT
30行目のbefore_date[cnt][loop_word]で範囲外アクセスになる可能性が比較的高いですね。
ここではloop_wordではなく、29行目のwhile文の直前で0に初期化される新しい変数を用いないとダメです。
文字列の最後に'\0'を格納するのも忘れずに。

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 17:51
by ISLe()
あざらく さんが書きました:なぜ、fgetsを使わないかというと、crの改行も扱いたいからです。
テキストモードだとCR+LFはLFに変換されてしまうので、fgetcでもCRを読み取れませんよ。

CR+LFの改行でCRとLFの両方を読み込みたいならバイナリモードを使います。
バイナリモードにしたらfgetsでもCRとLFの両方を読み込みますけどね。

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 18:05
by あざらく
>>みけcatさん
>>ISLeさん

返答ありがとうございます。
別の方法として、別関数を新たに作る、という考えに変えました。

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 18:11
by ISLe()
エラーメッセージによるとウィンドウズではないっぽいですね。
LFのみ文化の処理系で、CRのみのテキストファイルを読み込みたいってことですか。

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月18日(木) 21:34
by ISLe()
既にトピックは解決済みなので、サンプルコードを投げておきます。

fgetc2関数およびfgets2関数は、CRのみ、LFのみ、CR+LFを'\n'に変換します。
それぞれfgetc関数およびfgets関数に置き換えて使えます。

コード:

#include <stdio.h>
#include <string.h>

int fgetc2(FILE *fp)
{
	int c1 = fgetc(fp);
	if (c1 == '\x0d') {
		c1 = '\n';
		int c2 = fgetc(fp);
		if (c2 != '\x0a' && c2 != EOF) {
			ungetc(c2, fp);
		}
	}
	else if (c1 == '\x0a') {
		c1 = '\n';
	}
	return c1;
}

char *fgets2(char *str, int n, FILE *fp)
{
	char *str_top = str;
	if (n <= 0) return NULL;
	if (n > 1) {
		int c = fgetc2(fp);
		if (c == EOF) return NULL;
		do {
			--n;
			*(str++) = c;
			if (c == '\n') break;
		} while (n > 1 && (c = fgetc2(fp)) != EOF);
	}
	*str = '\0';
	return str_top;
}

int main(int argc, char *argv[])
{
	FILE *fp;
	char buf[100];
	fp = fopen("foo.txt", "r"); /* "rb"もOK */
	if (fp) {
		while (fgets2(buf, 100, fp) != NULL) {
			int n = strlen(buf);
			if (n > 0 && buf[n-1] == '\n') buf[n-1] = '\0';
			printf("[%s]\n", buf);
		}
		fclose(fp);
	}
	return 0;
}

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月22日(月) 03:36
by かずま
あざらく さんが書きました:下に入力処理の関数のコードを載せました。
これで行うと、
*** stack smashing detected ***: ./a.out terminated
となり、エラーになります。
input() は int を返すことになっているのに、
before_data すなわち、char (*)[50] を返しています。
char before_data[100][50] は、input() のローカル変数であり自動変数です。
呼び出し元へ戻ると、このデータは使用できません。

ということで、次のようなコードを書いてみました。いかがでしょうか?

コード:

#include <stdio.h>

int input(char data[100][50])
{
    int i, j, c;
    FILE *fp = fopen("foo.txt", "r");
    if (!fp ) return -1;
    for (i = 0; i < 100; i++) {
        for (j = 0; j < 50; j++) {
            c = fgetc(fp);
            if (c == EOF || c == '\r' || c == '\n') break;
            data[i][j] = c;
        }
        if (c == '\r') {
            c = fgetc(fp);
            if (c != '\n') ungetc(c, fp);
        }
        data[i][j] = '\0';
        if (c == EOF && j == 0) break;
    }
    return i;
}

int main(void)
{
    FILE *fp;
    char data[100][50];
    int n, i;
    n = input(data);
    if (n < 0) return 1;
    for (i = 0; i < n; i++)
        puts(data[i]);
    return 0;
}

Re: fgetcで読み取ったものを2次元配列へ格納したい

Posted: 2015年6月22日(月) 06:55
by かずま
かずま さんが書きました:ということで、次のようなコードを書いてみました。いかがでしょうか?
9行目の 50 を 49 にしてください。
'\0' の追加分を忘れていました。