きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

fwriteのシークが移動しない!?

事案

  • あるファイルのm行目のn列目以降(任意の位置)に文字列を挿入したい。
  • そのために、ファイル全体を挿入する文字列分だけずらしたい。
  • 末尾からn[byte]ごとread & writeを繰り返して移動することにした。

コード(の一部)

これはC++ではなくCであるため、stdio.hを使用している。

// 現在のオフセットからn[byte]分ずらす
writing_size = 1;
fseek(fp, 0, SEEK_END);
current_offset = ftell(fp);

while(current_offset > insert_offset) {
	// 現在の位置を読み出す分だけ戻す
	current_offset -= writing_size;
	fseek(fp, current_offset, SEEK_SET);

	// 現在の位置からn[byte]読み出し
printf("'%c'を取得し、%dへ移動し、\n", buffer[0], ftell(fp));
	fread(buffer, sizeof(char), writing_size, fp);

	printf("%dへ移動し、\n", ftell(fp));
	// 読み出し終えた位置からn[byte]書き出す
	fwrite(buffer, sizeof(char), writing_size, fp);
	printf("%dで書き終わる。\n\n", ftell(fp));
}

予想される結果

8から読み出し、
'9'を取得し、9へ移動し、
10で書き終わる。

7から読み出し、
'8'を取得し、8へ移動し、
9で書き終わる。
(以下続く)

実際の結果

8から読み出し、
'9'を取得し、9へ移動し、
9で書き終わる。

7から読み出し、
'8'を取得し、8へ移動し、
9で書き終わる。

どうやら最初の書き込みだけ失敗しているようだ。次のように読み出した分だけ先にfseekすれば問題なく動く。

// 現在のオフセットからn[byte]分ずらす
writing_size = 3;
fseek(fp, 0, SEEK_END);
current_offset = ftell(fp);

while(current_offset > insert_offset) {
	int size;

	// 現在の位置を読み出す分だけ戻す
	current_offset -= writing_size;
	fseek(fp, current_offset, SEEK_SET);

	// 現在の位置からn[byte]読み出し
	printf("%dから読み出し、\n", current_offset);
	fread(buffer, sizeof(char), writing_size, fp);
	fseek(fp, current_offset + writing_size, SEEK_SET);
	printf("'%c'を取得し、%dへ移動し、\n", buffer[0], ftell(fp));
	// 読み出し終えた位置からn[byte]書き出す
	fwrite(buffer, sizeof(char), writing_size, fp);
	printf("%dで書き終わる。\n\n", ftell(fp));
}

// '*'で埋める
fseek(fp, insert_offset, SEEK_SET);
{
	size_t i;
	for(i = 0; i < writing_size; ++i) {
		putc('*', fp);
	}
}

実行結果は、

8から読み出し、
'9'を取得し、9へ移動し、
10で書き終わる。

7から読み出し、
'8'を取得し、8へ移動し、
9で書き終わる。
(以下続く)

writeの前に……

buffer[0] = 'X';としてきちんと上書きされているか確かめたところ上書きされていない。どうやら読み込んだ後に連続して書き込むことができないようだ。

完成

m行n列の位置に適当な文字列を挿入する例。元ネタは2ちゃんねるの宿題スレより。

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

#define BUFFER_SIZE 1024
#define WRITING_SIZE 256

int main(void) {
	size_t row, column;
	char buffer[BUFFER_SIZE];
	char writing_string[WRITING_SIZE];
	FILE* fp;
	long insert_offset;
	long current_offset;
	size_t current_row;
	size_t writing_size;

	// 行数の取得
	printf("行: ");
	scanf_s("%d", &row);

	// 列数の取得
	printf("列: ");
	scanf_s("%d", &column);

	// 書き込む文字列
	printf("文字列: ");
	scanf_s("%s", writing_string, WRITING_SIZE);

	// 行の移動
	fopen_s(&fp, "./test.txt", "rb+");
	if(fp == NULL) {
		return -1;
	}

	// 行・列からオフセットを取得
	current_row = 1; // 1-orign
	insert_offset = ftell(fp);
	while(fgets(buffer, BUFFER_SIZE, fp)) {
		// 現在の行が指定された行に該当
		if(current_row == row) {
			insert_offset += column; // 0-orign
			break;
		}

		insert_offset = ftell(fp);
		++current_row;
	}
	
	// 現在のオフセットからn[byte]分ずらす
	writing_size = strlen(writing_string);
	fseek(fp, 0, SEEK_END);
	current_offset = ftell(fp);

	while(current_offset > insert_offset) {
		// 現在の位置を読み出す分だけ戻す
		current_offset -= writing_size;
		fseek(fp, current_offset, SEEK_SET);

		// 現在の位置からn[byte]読み出し
		fread(buffer, sizeof(char), writing_size, fp);
		fseek(fp, current_offset + writing_size, SEEK_SET);
		// 読み出し終えた位置からn[byte]書き出す
		fwrite(buffer, sizeof(char), writing_size, fp);
	}

	// 文字列でで埋める
	fseek(fp, insert_offset, SEEK_SET);
	fwrite(writing_string, sizeof(char), writing_size, fp);

	fclose(fp);

	getchar();
	return 0;
}

追記

freadした後に

fseek(fp, 0, SEEK_CUR);

で現在の位置を確定させればその位置からfwriteができるようだ。なお、clearerrで各指示子をクリアしてもだめだった。