【问题标题】:c - strcmp not returning 0 for equal stringsc - strcmp 不为相等的字符串返回 0
【发布时间】:2015-10-18 01:56:31
【问题描述】:

因此,我已尝试广泛寻找解决方案,但只能真正找到其中一个字符串中缺少新行或空字节的帖子。我很确定这里不是这种情况。

我正在使用以下函数将单词与包含单词列表的文件进行比较,其中每行一个单词(函数中的字典)。代码如下:

int isWord(char * word,char * dictionary){
  FILE *fp;
  fp = fopen(dictionary,"r");
  if(fp == NULL){
    printf("error: dictionary cannot be opened\n");
    return 0;
  }
  if(strlen(word)>17){
    printf("error: word cannot be >16 characters\n");
    return 0;
  }
  char longWord[18];
  strcpy(longWord,word);
  strcat(longWord,"\n");
  char readValue[50] = "a\n";
  while (fgets(readValue,50,fp) != NULL && strcmp(readValue,longWord) != 0){
    printf("r:%sw:%s%d\n",readValue,longWord,strcmp(longWord,readValue));//this line is in for debugging
  }
  if(strcmp(readValue,longWord) == 0){
    return 1;
  }
  else{
    return 0;
  }
}

代码编译没有错误,该函数可以很好地读取字典文件,并将打印出现在其中的单词列表。我遇到的问题是,即使两个字符串相同,strcmp 也不会返回 0,因此该函数将为任何输入返回 false。

例如我得到:

r:zymoscope
w:zymoscope
-3

有什么想法吗?我觉得我一定遗漏了一些明显的东西,但在我的搜索中找不到任何东西。

【问题讨论】:

  • 我猜你是在一台 Windows 机器上,你已经阅读了带有 CRLF 行结尾的文件,并且你没有准确地去除结尾。在许多(大多数)代码集中,'\r''\n' 的值相差 3。奇怪的是,您有一个 printf() 打印所有这些数据,但格式字符串中没有 \n。您依赖于数据中的换行符,这似乎有点可疑。 (编写一个函数以十六进制打印字符串中的字节;在每个字符串上调用它;找出差异。)
  • 另外,您的文件永远不会关闭。 打开文件之前做所有可能的检查。完成后(或出现错误)关闭它。
  • 在使用fgets(readValue,50,fp) 后建议readValue[strcspn(readValue, "\r\n")] = 0; 消除行尾字符。
  • 好的;在某些方面,这种情况(Linux 读取文件可能——可能——在 Windows 上创建)更有意义。为自己提供我建议的字符串转储功能并使用它。您可以使用:static void dump_string(const char *tag, const char *string) { size_t len = strlen(string); printf("%s (%zu):", tag, len); size_t i; for (i = 0; i < len; i++) { printf(" %.2X", (unsigned char)string[i]); if (i % 16 == 15) putchar('\n'); } if (i % 16 != 0) putchar('\n'); } 并将其命名为:dump_string("r", readValue); dump_string("w", longWord); 或类似名称。
  • For future 1) 当打印麻烦的字符串时,使用printf("'%s'\n", bad_string); 来帮助识别前导和尾随空格、换行符等。 2) 不要认为strcmp() 是错误的,问为什么字符串不相等。

标签: c strcmp


【解决方案1】:

您正在阅读的字符串包含尾随字符,因此与您要与之比较的字符串不同。

删除尾随换行符(如果有,则删除 CR);那么您不需要在被比较的字符串中添加任何换行符或回车符:

int isWord(char *word, char *dictionary){
  FILE *fp;
  fp = fopen(dictionary, "r");
  if (fp == NULL){
    fprintf(stderr, "error: dictionary cannot be opened\n");
    return 0;
  }
  if (strlen(word) > 16){
    fprintf(stderr, "error: word cannot be >16 characters\n");
    return 0;
  }
  char readValue[50];
  while (fgets(readValue, 50, fp) != NULL){
    char *ep = &readValue[strlen(readValue)-1];

    while (*ep == '\n' || *ep == '\r'){
      *ep-- = '\0';
    }
    if (strcmp(readValue, word) == 0){
      return 1;
    }
  }
  return 0;
}

【讨论】:

  • 注意:readValue[strlen(readValue)-1] 在读取的第一个字符是空字符的极少数情况下会导致未定义的行为/错误。检查字符串长度是否大于 0 是解决此问题的一种方法。
  • readValue"\n""\r\n" 时,while (*ep == '\n' || *ep == '\r'){ *ep-- = '\0'; } *ep-- = '\0'; 是一个问题。
  • 而且,虽然他说得太客气了,但@chux 的回答确实避免了这两个问题(尽管可能有人认为在中间有 CR 的输入行过早停止,与CRLF 或 LF 行结束)。并且需要进行单独的strlen() 操作是更喜欢POSIX getline() 函数而不是fgets() 的原因;它返回它读取的数据的长度,如果您认为需要,它(除其他外)允许您读取过去的前导空字节。
  • 是的,我可以看到这两个都会有问题,我最初选择了这个答案,因为它似乎更笼统,因为它处理了不同的可能行尾。 @chux,现在看来最好简单地使用您的解决方案并检查每个可能的行结尾。
【解决方案2】:

我看到您正在将newline 附加到您的测试字符串以尝试处理fgets() 保留行尾的问题。从源头上解决这个问题要好得多。从文件读取后,您可以立即删除所有尾随内容。

readValue [ strcspn(readValue, "\r\n") ] = '\0';   // remove trailing newline etc

【讨论】:

  • 我要补充一点,这对于'\0' 终止的字符串是完全安全的。如果没有任何类型的行尾,strcspn() 返回字符串长度,从而无害地覆盖已经存在的'\0'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-27
  • 1970-01-01
相关资源
最近更新 更多