你如何做到這一點,閱讀一個字符,並尋求反向,是低效的。
的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
。
注意:我寫這部分之前的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