代码之家  ›  专栏  ›  技术社区  ›  C. Kim

Linux中的C编程:对于在文件中查找子字符串出现次数的程序,无法获得正确的输出

  •  -1
  • C. Kim  · 技术社区  · 6 年前

    我正在编写一个程序,该程序可以从写入缓冲区的文本文件(也可以从命令行读取)中的命令行中查找输入子字符串的出现次数。

    当我在bash中运行代码时,我得到了一个错误:Segmentation fault(内核转储)。 我仍在学习如何在这种环境下使用C进行编码,并对出现分段错误的原因有了一些想法(滥用动态内存分配?),但我找不到它的问题。我所能得出的结论是,问题来自for循环(我标记了代码中潜在错误的位置)。

    编辑:我通过更改 argv[j] argv[i] 但是,当我现在运行代码时,count1总是返回0,即使子字符串在文本文件中出现多次,并且我不确定是什么错误,即使我已经多次查看了代码。

    $ more foo.txt
    
    aabbccc
    
    $ ./main foo.txt a
    
    0
    

    #include <sys/types.h>
    #include <sys/uio.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
    
        FILE *fp; 
        long lsize; 
        char *buf;
        int count = 0, count1 = 0; 
        int i, j, k, l1, l2;   
    
        if (argc < 3) { printf("Error: insufficient arguments.\n"); return(1); };
    
        fp = fopen(argv[1], "r"); 
    
        if (!fp) { 
            perror(argv[1]); 
            exit(1); 
        }
    
        //get size of file 
        fseek(fp, 0L, SEEK_END);
        lsize = ftell(fp); 
        rewind(fp);
    
        //allocate memory for entire content
        buf = calloc(1, lsize+1);
    
        if (!buf) { 
            fclose(fp); 
            fputs("Memory alloc fails.\n", stderr); 
            exit(1); 
        }
    
        //copy the file into the buffer
        if (1 != fread(buf, lsize, 1, fp)) {
            fclose(fp);
            free(buf); 
            fputs("Entire read fails.\n", stderr); 
            exit(1); 
        }
    
        l1 = strlen(buf);
    
        //error is somewhere here
        for (i = 2; i < argc; i++) {
            for (j = 0; j < l1;) {
                k = 0; 
                count = 0; 
                while ((&buf[j] == argv[k])) {
                    count++;
                    j++; 
                    k++; 
                }
                if (count == strlen(argv[j])) {
                    count1++; 
                    count = 0; 
                }
                else
                    j++; 
            }
            printf("%d\n", count1);
        }
    
        fclose(fp); 
    
        return 0; 
    }
    
    1 回复  |  直到 6 年前
        1
  •  0
  •   Pablo    6 年前

    fread(buf, lsize, 1, fp) 将读取1块 lsize 但是,字节数 fread 不关心内容,不会添加 '\0' -正在终止的字节 字符串,所以 l1 = strlen(buf); 产生未定义的行为 因此,可以忽略结果(并且您的计数也有错误)。 请注意,文件末尾通常没有以0结尾的字节, 这甚至适用于包含文本的文件,它们通常以 换行符。

    您必须自己设置0终止字节:

    if (1 != fread(buf, lsize, 1, fp)) {
        fclose(fp);
        free(buf); 
        fputs("Entire read fails.\n", stderr); 
        exit(1); 
    }
    
    buf[lsize] = '0';
    

    您可以使用 strstr 要获取子字符串的位置,请执行以下操作:

    for(i = 2; i < argc; ++i)
    {
        char *content = buf;
        int count = 0;
    
        while((content = strstr(content, argv[i])))
        {
            count++;
            content++; // point to the next char in the substring
        }
    
        printf("The substring '%s' appears %d time(s)\n", argv[i], count);
    
    }
    

    你的计数错了,有一些错误。此比较

    &buf[j] == argv[k]
    

    是错误的,您正在比较指针,而不是内容。你必须使用 strcmp 比较字符串。在这种情况下,您必须使用 strncmp 因为你 只想匹配子字符串:

    while(strncmp(&buf[j], argv[k], strlen(argv[k])) == 0)
    {
        // substring matched
    }
    

    但这也是错误的,因为你在递增 k 同样,这将 给你下一个论点,最后你可能会读到超出 argv 如果子字符串长于参数数。基于您的 代码,则必须比较字符:

    while(buf[j] == argv[i][k])
    {
        j++;
        k++;
    }
    

    您必须增加 counter 仅当子字符串匹配时,如 这是:

    l1 = strlen(buf);
    
    for (i = 2; i < argc; i++) {
        int count = 0;
        int k = 0; // running index for inspecting argv[i]
        for (j = 0; j < l1; ++j) {
            while(buf[j + k] == argv[i][k])
                k++;
    
            // if all characters of argv[i] 
            // matched, argv[i][k] will be the
            // 0-terminating byte
            if(argv[i][k] == 0)
                count++;
    
            // reset running index for argv[i]
            // go to next char if buf
            k = 0;
        }
    
        printf("The substring '%s' appears %d time(s)\n", argv[i], count);
    }