c - 用C读取文件

标签 c string

我正在C程序中读取一个文件,并将其中的每个单词与通过命令行参数输入的单词进行比较。但是我会撞车,我不知道怎么了。如何跟踪此类错误?我的情况怎么了?
我的编辑者是克朗。代码编译得很好。运行时会显示“分段错误”。
这是密码。

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

int main(int argc, char* argv[])
{
    char* temp = argv[1];
    char* word = strcat(temp, "\n");

    char* c = "abc";
    FILE *input = fopen("/usr/share/dict/words", "r");

    while (strcmp(word, c))
    {
        char* duh = fgets(c, 20, input);
        printf("%s", duh); 
    }

    if (!strcmp (word, c))
    {
        printf("FOUND IT!\n");
        printf("%s\n%s", word, c);  
    }

    fclose(input);    
}

最佳答案

这里的问题是,您试图用C语言来处理字符串,就像在另一种语言(如C++或Java)中一样,它们是可调整大小的向量,您可以很容易地将任意数量的数据附加或读取到其中。
C字符串的级别要低得多。它们只是一个字符数组(或者指向这样一个数组的指针;不管怎样,数组都可以像指向它们在C中的第一个元素的指针一样对待),字符串被视为该数组中的所有字符,直到第一个空字符。这些数组是固定大小的;如果您想要一个任意大小的字符串,您需要使用malloc()自己分配它,或者使用您想要的大小在堆栈上分配它。
这里有一点让人困惑的是,您使用的是非标准类型string。考虑到上下文,我假设它来自您的cs50.h,并且只是char *的typedef。如果您实际使用char *而不是string,它可能会减少混淆;使用typedef会掩盖实际情况。
让我们从第一个问题开始。

    string word = strcat(argv[1], "\n");

strcat()将第二个字符串附加到第一个字符串上;它从第一个字符串的空终止符开始,并将其替换为第二个字符串的第一个字符,依此类推,直到它在第二个字符串中达到空值为止。为了使其工作,包含第一个字符串的缓冲区需要有足够的空间来容纳第二个字符串。如果没有,则可能覆盖任意其他内存,这可能导致程序崩溃或出现各种其他意外行为。
这是一个例子。假设argv[1]包含单词hello,缓冲区的空间正好与此所需的空间相同。在它是一些其他的数据之后;为了举例,我已经填写了other,虽然它实际上不是那样的,但它可以是任何东西,并且它可能重要,也可能不重要:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \0| o | t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+

现在,如果使用strcat()附加"\n",您将得到:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \n| \0| t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+

您可以看到我们已经覆盖了other之后的hello数据。这可能会引起各种各样的问题。要解决这个问题,您需要将argv[1]复制到一个新字符串中,该字符串有足够的空间再加上一个字符(不要忘记后面的null)。您可以调用strlen()来获取字符串的长度,然后为\n添加1,为后面的null添加1,以获取所需的长度。
实际上,与其试图在命令行中输入的单词中添加\n,不如建议从输入的单词中删除\n,或者使用strncmp()比较除最后一个字符以外的所有字符(即\n)。一般来说,在C语言中最好避免附加字符串,因为附加字符串意味着需要分配内存和复制内容,这样做很容易出错,而且效率也很低。更高级的语言通常会为您处理细节,使附加字符串更容易,尽管仍然同样效率低下。
编辑后,您将此更改为:
    char* temp = argv[1];
    char* word = strcat(temp, "\n");

然而,这也有同样的问题。char *是指向字符数组的指针。您的temp变量只是复制指针,而不是实际值;它仍然指向同一个缓冲区。这是一个例子;我编地址是为了演示,在真实的机器中,这些东西之间会有更多的对象,但这应该足够演示了。
+------------+---------+-------+
|    name    | address | value |
+------------+---------+-------+
| argv       |    1000 |  1004 |-------+
| argv[0]    |    1004 |  1008 | --+ <-+
| argv[1]    |    1006 |  1016 | --|---+
| argv[0][0] |    1008 |   'm' | <-+   |
| argv[0][1] |    1009 |   'y' |       |
| argv[0][2] |    1010 |   'p' |       |
| argv[0][3] |    1011 |   'r' |       |
| argv[0][4] |    1012 |   'o' |       |
| argv[0][5] |    1013 |   'g' |       |
| argv[0][6] |    1014 |     0 |       |
| argv[1][0] |    1016 |   'w' | <-+ <-+
| argv[1][1] |    1017 |   'o' |   |
| argv[1][2] |    1018 |   'r' |   |
| argv[1][3] |    1019 |   'd' |   |
| argv[1][4] |    1020 |     0 |   |
+------------+---------+-------+   |

现在,当您创建temp变量时,您所做的只是将argv[1]复制到一个新的char *中:
+------------+---------+-------+   | 
|    name    | address | value |   |
+------------+---------+-------+   |
| temp       |    1024 |  1016 | --+
+------------+---------+-------+

另外,如果不检查argv[1]是否大于1,也不应该尝试访问argc。如果有人没有传入任何参数,则argv[1]本身对访问无效。
我将继续下一个问题。
    string c = "abc";

    // ...

        char* duh = fgets(c, 20, input);

这里,您指的是静态字符串"abc"。一个出现在源代码中的字符串,比如"abc",进入程序内存中一个特殊的只读部分。记住我说的话;string这里只是一种说法。所以char *实际上只是一个指向内存只读部分的指针;它只有足够的空间来存储您在文本中提供的字符(4,用于c和终止字符串的空字符)。abc将存储正在读取的字符串的位置作为其第一个参数,将其第二个参数作为其具有的空间量。因此,您尝试将最多20个字节读取到一个只读缓冲区中,该缓冲区只能容纳4个字节。
您需要为堆栈上的读取分配空间,例如使用:
char c[20];

或者动态地使用fgets()
char *c = malloc(20);

关于c - 用C读取文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13444944/

相关文章:

c - 带有 Arduino 的 AVR-GCC

c - 在密码学、C 编程方面需要帮助

javascript - 使用 Jquery 将值附加到字符串中的特定元素

c - 在 C 中获取 Shell 命令的结果

c - 在 C 中,确保多段代码的汇编指令数是固定的

C变量赋值给数组元素

c - C 中的指针和字符串

从字符串末尾删除多个非字母字符

C++ char变量2字

c++ - 使用istringstream()区分 "0"和非数字字符串