Как вы это делаете, читаете персонажа и ищете назад, неэффективны.
Модуль Perl File::Readbackwards является хорошим для чтения. Perl IO очень близок к C, и код хорошо комментируется.
Основным алгоритмом является чтение и буферизация блоков и поиск строк внутри этого блока, но начиная с конца файла и перехода назад.
- Открыть файл.
- Ищите до конца.
- Ищите назад к предыдущему блоку.
- Прочитайте этот блок в буфер.
Как только у вас есть этот буфер, отсканируйте назад через буфер, пока не найдете новую строку. Теперь у вас есть полная линия. Если вам не удалось найти новую строку, прочитайте предыдущий блок и повторите попытку.
Это нетривиальная, так вот, как вы могли бы сделать это чтение назад один символ за один раз, пока не появится символ новой строки. Я использовал интерфейс fgets
для получения одной строки за раз.
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
должен быть проверен.
Примечание: Я написал эту часть до О.П. уточнил, что они хотели. Ну, это все еще довольно круто.
Теперь, когда у вас есть полная линия, вы можете разворачивать ее отдельно от чтения файла.
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
'Меня зовут Hello' не назад от' Hello My name is'. Вы имели в виду 'is name My Hello'? – Schwern
Скажите, что несколько строк - это Hello и мое имя, я хочу, чтобы мое имя было напечатано первым, а следующая строка была Hello, почти так же, как они переключались местами – JsmileyJ