どのようにして文字を読み、後方に向かっているのかは、非効率的です。
PerlモジュールFile::Readbackwardsは読みやすいです。 PerlのIOはCに非常に近く、コードはよくコメントされています。
基本的なアルゴリズムは、ブロックを読み込んでバッファリングし、そのブロック内の行を見つけることですが、ファイルの最後から後方に移動します。
- ファイルを開きます。
- 最後に検索してください。
- 前のブロックに戻る。
- ブロックをバッファに読み込みます。
バッファーを取得したら、改行が見つかるまでバッファーを逆方向にスキャンします。今すぐあなたは完全なラインを持っています。改行が見つからない場合は、前のブロックを読み、もう一度やり直してください。
これは自明なことではありません。改行を見るまで、一度に1文字ずつ後方に読む方法は次のとおりです。私はfgets
インターフェイスを使って一度に1行を取得しました。
char *fgets_backwards(char *str, int size, FILE *fp) {
/* Stop if we're at the beginning of the file */
if(ftell(fp) == 0) {
return NULL;
}
int i;
/* Be sure not to overflow the string nor read past the start of the file */
for(i = 0; ftell(fp) != 0 && i < size; i++) {
/* Back up one character */
fseek(fp, -1, SEEK_CUR);
/* Read that character */
str[i] = (char)fgetc(fp);
/* We have the whole line if we see a newline, except at the start.
This happens before we back up a character so the newline will
appear on the next line. */
if(str[i] == '\n' && i != 0) {
break;
}
/* Back up the character we read. */
fseek(fp, -1, SEEK_CUR);
}
/* Null terminate, overwriting the previous line's newline */
str[i] = '\0';
return str;
}
これらの行は逆順に表示されるので、逆順に表示されます。それだけで十分です。
void reverse(char *start) {
size_t len = strlen(start);
for(char *end = &start[len-1]; start < end; start++, end--) {
char tmp = start[0];
start[0] = end[0];
end[0] = tmp;
}
}
すべて一緒にそれを置く...
fseek(fp, 0, SEEK_END);
char line[1024];
while(fgets_backwards(line, 1024, fp) != NULL) {
reverse(line);
printf("%s", line);
}
注、私はエラーチェックについてのずさんでした。 fseek
への各呼び出しを確認する必要があります。
注:OPは、彼らが何を望むかを明らかにする前に、私はこの部分を書きました。まあ、それはまだかなりクールです。
これで完全な行が得られました。これで、ファイルを読むこととは別に逆に取り組むことができます。
char *reverse_by_word(char *string) {
size_t len = strlen(string);
/* Allocate enough space to store string, and a null */
char *reversed = malloc(len * sizeof(char));
/* Initialize reversed to be an empty string so strcat knows where to start */
/* There's no need to initialize the rest of the string,
/* the garbage from malloc will be overwritten */
reversed[0] = '\0';
/* Read the string backwards, character by characer */
for(int i = (int)len - 1; i >= 0; i--) {
/* If we see a space... */
if(isspace(string[i])) {
/* Add the word after it to reversed */
strcat(reversed, &string[i+1]);
/* Faithfully reproduce the whitespace after the word */
strncat(reversed, &string[i], 1);
/* Chop the string off at the space */
string[i] = '\0';
}
}
return reversed;
}
これは破壊的なバージョンで、string
はヌルバイトで細かくなります。これを非破壊的に行うことは可能ですが、後で編集することがあります。
input
ので
とreversed
はない境界を持つstrcpy
が安全である使用、同じ長さです。
これをテストして、すべての空白を忠実に再現できます。
#include <assert.h>
int main() {
char input[] = " Hello My\tname is ";
char *reversed = reverse_by_word(input);
printf("'%s'\n", reversed);
assert(strcmp(reversed, " is name\tMy Hello ") == 0);
}
ここで非破壊バージョンがあります。基本的には同じ考えですが、すでにヌルバイトで印刷したところに印を付けるのではなく、last_idx
で覚えています。
char *reverse_by_word(const char *string) {
size_t len = strlen(string);
char *reversed = malloc(len * sizeof(char));
reversed[0] = '\0';
/* Read the string backwards, character by characer */
int last_idx = (int)len;
for(int i = (int)len - 1; i >= 0; i--) {
/* If we see a space... */
if(isspace(string[i])) {
/* Add the word before it, stop at the last word we saw. */
strncat(reversed, &string[i+1], last_idx - i - 1);
/* Faithfully reproduce the whitespace. */
strncat(reversed, &string[i], 1);
/* Remember the last place we printed up to. */
last_idx = i;
}
}
return reversed;
}
複数の行がある場合はどうしますか? – Schwern
「私の名前はこんにちは」は、「こんにちは私の名前です」から後ろにはありません。あなたは '私のハローという名前ですか? – Schwern
複数の行がこんにちは、私の名前は、私の名前は、私の欲しいと言っています私の名前が最初に印刷され、次の行がこんにちは、ほぼ同じように置き換えられました – JsmileyJ