首页 > 解决方案 > 反转文件中的单词

问题描述

有人可以提示我我做错了什么吗?我尝试编译它,但我不确定这里有什么问题:

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

int main()
{
  int k, i, j, len;
  FILE *f;
  char aux, str[100];

  f = fopen("rev.txt", "r");
  len = strlen(str);
  if (f == NULL) {
    printf("File not found!");
    return 0;
  }

  while (fgets(str, 80, f) != NULL) {
    for (j = 0; j < len; j++)
      if ((isalpha(str[j]) != 1) || (j == len - 1))
        if (j < len - 1)
          k = j - 1;
        else
          k = j;
    i = 0;
    while (i < k) {
      aux = str[i];
      str[i] = str[k];
      str[k] = aux;
      i++;
      k--;
    }

    printf("%s",str);
  }
}

因此,在上面的代码中,我尝试从名为 rev 的文件中反转单词,但每当我运行它时,它都会打印出奇怪的字符。有什么帮助吗?

标签: c

解决方案


正确缩进代码并打开警告会揭示问题。你正在使用forif没有块。虽然这是合法的,但很容易出错。尤其是...

    for(j=0;j<len;j++)
        if ((isalpha(str[j])!=1) || (j==len-1))
    if(j<len-1)
        k=j-1;
    else
        k=j;
    i=0;

老实说,我不确定这里的正确支撑是什么。我的编译器已经警告过“悬空的 else”,所以有些事情是不对的。

test.c:21:9: warning: add explicit braces to avoid dangling else [-Wdangling-else]
        else

我怀疑你是这个意思。

    for(j=0;j<len;j++) {
        if((isalpha(str[j])!=1) || (j==len-1)) {
            if(j<len-1) {
                k=j-1;
            }
            else {
                k=j;
            }
        }
    }

无论哪种方式,始终使用块。总是在编译时出现警告。我用-Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic.


另一个问题是这样的:

len = strlen(str);

此时str未初始化,因此它将包含垃圾。len将是那个垃圾的长度。

len也永远不会改变,但是str每行读取的内容都会改变。相反,您需要在每次调用后检查str循环内部的长度。fgets

while(fgets(str,80, f) != NULL) {
    len = strlen(str);
    ...
}

您的str缓冲区为 100,但您只允许fgets80 个字符。为避免这种情况,请使用sizeof(str)而不是硬编码。注意:这仅适用于堆栈分配的内存。

while(fgets(str, sizeof(str), f) != NULL) {
    len = strlen(str);

当您使用它时,没有理由对行缓冲区小气。它只分配一次。80 或 100 对于行缓冲区来说非常小。给它 4096 字节以允许很长的行。


通过这些修复,您的代码可以正常工作,但我们可以对其进行改进。特别是整个for循环似乎没有必要。我怀疑它所做的只是试图将换行符保留在反转字符串的末尾。无需查看整个字符串。fgets逐行读取,如果有换行符,它将始终位于末尾。

// Convert the length to an index
k = strlen(str) - 1;

// Leave the trailing newline alone
if( str[k] == '\n' ) {
    k--;
}

有了它,加上更好的变量名,只根据需要声明变量,并使用正确的类型,我们得到......

while(fgets(str, sizeof(str), f) != NULL) {
    // Check if someone snuck a null byte into the file.
    if( !*str ) {
        continue;
    }

    // Convert the length to an index
    size_t back = strlen(str) - 1;

    // Leave the trailing newline alone
    if( str[back] == '\n' ) {
        back--;
    }

    // Swap characters
    size_t front = 0;
    while(front < back) {
        char tmp   = str[front];
        str[front] = str[back];
        str[back]  = tmp;
        front++;
        back--;
    }

    printf("%s",str);
}

这可以使用指针而不是索引进一步简化。

while(fgets(str, sizeof(str), f) != NULL) {
    // A pointer to the last character of str
    char *back = &str[strlen(str) - 1];

    // Leave the trailing newline alone
    if( *back == '\n' ) {
        back--;
    }

    // Swap characters
    for(
        char *front = str;
        front < back;
        front++, back--
    ) {
        char tmp = *front;
        *front   = *back;
        *back    = tmp;
    }

    printf("%s",str);
}

推荐阅读