【问题标题】:C strings string comparisons always result in falseC字符串字符串比较总是导致错误
【发布时间】:2019-07-17 18:24:22
【问题描述】:

我正在尝试在文件中查找字符串。我通过修改 getline 手册页中的代码 sn-p 编写了以下内容。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    FILE * fp;
    char * line = NULL;
    char *fixed_str = "testline4";
    size_t len = 0;
    ssize_t read;

    fp = fopen("test.txt", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);

        if (strcmp(fixed_str,line)==0)
            printf("the match is found\n");
    }
    //printf("the len of string is %zu\n", strlen(fixed_str));

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
} 

问题是 strcmp 的结果总是错误的,尽管 getline 成功且正确地迭代了文件中的所有行。 由于换行符,fixed_str 的长度为 9,文件中相等字符串的长度为 10(我对吗?)。但是在strncmp 的帮助下比较 9 个字符仍然会产生错误的结果。我也排除了大写和空格的可能性,所以我认为我做错了什么

test.txt如下

test line1
test line2
test line3
testline4
string1
string2
string3
first name

我尝试了所有条目但没有成功

注意:在我的实际程序中,我必须从另一个文件中读取fixed_str

【问题讨论】:

  • if (strcmp(fixed_str,line))==0) - 这无法编译。有两个(s 和三个)s。
  • 向我们展示您的strncmp 尝试。
  • 如果您更正@EugeneSh。推荐并使用 strncmp 它应该可以工作
  • @incompetent 将printf("%s", line); 替换为printf("&lt;%s&gt;", line); 并仔细查看输出。还向我们展示一个最小的test.txt 文件,它可以重现问题-
  • 哇哦... “在我的实际程序中...” - 好的,但是 this 代码是否存在同样的问题?您读取输入的方式可能会完全根据输入法的语义改变结果。您的 实际 代码可能完全表现出不同的问题 - 只是具有相同的可观察症状。您至少应该发布一个片段,显示您如何接受输入以及输入文件包含的内容 - 或者更好的是,只讨论这段代码,如果解决方案不适用于“真实”代码,则发布一个不同的问题。

标签: c linux string file


【解决方案1】:

来自getline() man page(我的重点):

getline() 从流中读取整行,存储地址 将包含文本的缓冲区放入 *lineptr。缓冲区为空- 终止并且包括换行符,如果找到的话。

您的fixed_str 没有换行符。

因此去除任何换行符(例如):

char* nl = strrchr( line, '\n' ) ;
if(nl != NULL) *nl = `\0` ;

或者更有效,因为getline() 返回行长(在您的情况下为read):

if(line[read - 1] == '\n' ) line[read - 1] = `\0` ;

'\n' 添加到fixed_str 可能看起来更简单,但这不是一个好主意,因为文件中的最后(或唯一)行没有,但可能会匹配。

使用strncmp() 如您的问题中所述应该有效,但没有看到尝试很难评论,但无论如何它是一个有缺陷的解决方案,因为它会匹配 所有例如:

testline4
testline4 and some more
testline4 12345.

如果fixed_str 取自控制台或文件输入而不是常量,则输入方法和数据源可能会导致问题,以及替代行尾约定的可能性。为了使其更健壮,您可以这样做:

// Strip any LF or CR+LF line end from fixed_str
char* line_end = strpbrk( fixed_str, "\r\n" ) ;
if( line_end != NULL ) *line_end = '\0' ;  

// Strip any LF or CR+LF line end from line
line_end = strpbrk( line, "\r\n" ) ;
if( line_end != NULL ) *line_end = '\0' ;  

或者@AndrewHenle 指出的更简单(即更好)的解决方案:

// Strip any LF or CR+LF line end from fixed_str
fixed_str[strcspn(line, "\r\n")] = '\0';

// Strip any LF or CR+LF line end from line
line[strcspn(line, "\r\n")] = '\0';

这样可以比较任一输入,而不管行以无结尾、CR 或 CR+LF 结尾,甚至两个输入之间的行结尾可能不同。

【讨论】:

  • 注意,fgets 也会发生同样的事情。请看Removing trailing newline character from fgets() input
  • @incompetent :请注意,如果文件具有替代行终止符,例如 \r (CR) 或 \r\n (CR+LF) - 如果您需要允许这样做(例如,如果文本文件可能是在 Windows 系统上准备的),您将需要额外的 line 处理。
  • 我的文件是在 Linux 上创建的,但一个不确定因素是 fixed_str 将从另一个文件中获取。对于这种情况有什么建议或注意事项吗?
  • @incompetent :是的,根据我对问题的评论,问题可能会完全改变,具体取决于您阅读输入的方式和输入文件内容。例如,如果您的fixed_str 输入也使用getline(),则输入可能有也可能没有相同的换行符。我可能会在 both 字符串上使用strpbrk(),使用"\r\n" 作为分隔符,并将它们替换为\0,使其成为文本文件类型和输入法不可知论。
  • @incompetent :我已经添加到答案中,以涵盖来自不同来源的输入或具有模糊行尾的输入法的可能性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-28
  • 1970-01-01
  • 2013-12-08
  • 2020-01-22
  • 2021-11-19
  • 2016-02-08
相关资源
最近更新 更多