如何检查字符是 EOF 还是 \n

How to check a char is EOF or \n

提问人:ident 提问时间:9/19/2023 最后编辑:chqrlieident 更新时间:9/20/2023 访问量:97

问:

我想数一数段落中有多少句话,句子定义为

  1. 一个句子以后跟两个空格结尾,例如."a. "

  2. 句子以 结尾,后跟换行符 (),例如.'\n'"a.\n"

  3. 句子以 in 结尾,如后跟 .."a."EOF

我的做法如下

我尝试使用 和 检查 和 .strncmp(&s, "\n", 1) == 0strncmp(&s, "\0", 1) == 0\nEOF

但是,它未能检查和\nEOF

如何检查一个字符是 C 语言还是 C 语言?EOF\n

int main() {
    char s;
    int count = 0;
    while (scanf("%c", &s) != EOF) {
        if (strncmp(&s, ".", 1) == 0) {
            scanf("%c", &s);
            if (strncmp(&s, "\n", 1) == 0) {
                count++;
            } else
            if (strncmp(&s, "\0", 1) == 0) {
                count++;
                printf("%d", count);
                return 0;
            } else
            if (strncmp(&s, " ", 1) == 0) {
                scanf("%c", &s);
                if (strncmp(&s, " ", 1) == 0) {
                    count++;
                }
            }
        }
    }
    printf("%d", count);
    return 0;
}
c 字符串 字符

评论

2赞 Some programmer dude 9/19/2023
而不是例如,为什么不简单地呢?strncmp(&s,".",1)==0y == '.'
2赞 Some programmer dude 9/19/2023
也许你真正需要的是 getchar 函数?但请注意,它返回一个 int,这对于与 value 进行比较很重要。intEOF
0赞 Support Ukraine 9/19/2023
所以一个后跟一个空格就不是一个句子!?.
1赞 pmg 9/19/2023
EOF与 不兼容。在“常规”中,计算机可以设置为任何不同的值。 不是这些值中的任何一个。 不是一个字符,是一个信号......就像“你的冰箱里有多少酸奶?”一样,不能接受“错误的问题......我卖掉了我的冰箱,“:)charchar256EOFEOFEOF

答:

3赞 Lundin 9/19/2023 #1
  • EOF的类型,因此您无法可靠地检查 A 是否为 is,因为该值可能超出范围。intcharEOF
  • char具有实现定义的符号,并且通常定义为负数,因此 A 很可能仅出于这个原因就永远无法成立。默认情况下是 char 签名还是未签名?EOFcharEOF
  • 这就是为什么像这样的函数实际上不会返回一个 ,而是返回一个 .是的,这是一些严重腐烂的 API,但自 1970 年代初以来,功能一直如此。getcharcharint

评论

0赞 Lundin 9/19/2023
一般的最佳实践是避免出于快速和肮脏的调试等目的。初学者不应该浪费大量时间在学习大脑受损的 API 到古老的控制台 I/O 函数上,因为他们可以更好地将时间花在学习实际编程上。stdio.h
1赞 chux - Reinstate Monica 9/20/2023
“Best practice is to avoid stdio.h” --> 如果没有,您如何推荐便携式程序读取文件或打印到?stdio.hstdout
0赞 Lundin 9/20/2023
@chux-恢复Monica 就像你设计一个便携式的东西一样:写一个HAL。在某些 API 函数之上编写一个精简的 HAL 并不是火箭科学。无论如何,你都需要一个,因为 stdio.h 对文件属性、文件搜索、目录等一无所知。好吧,并不是所有的 stdio.h 都设计得非常糟糕,只有大约 99% 的设计。 是一个不错的小功能。puts
0赞 Lundin 9/20/2023
万一有人能为任何编程语言命名任何其他库,这些编程语言对人类造成的物理和/或金钱损害几乎与 stdio.h 一样多,不过我很想听听它。windows.h 和 unistd.h 可能是候选者 - 它们的设计也非常糟糕。但它们仍然远未达到 stdio.h。
1赞 Support Ukraine 9/19/2023 #2
  1. 当您使用读取字符时,您无法检查 EOF。逐个阅读字符时,最好使用 .该函数返回一个允许您检查 EOF。scanf("%c", &s)getchargetcharint

  2. 用于比较您不想要的字符。字符可以使用 直接比较。strncmp==

为了使逻辑简单,我建议使用状态变量,即记住您之前读过的内容。例如:

#define DOT           0  // Last character was '.'
#define DOT_SPACE     1  // Last two characters was '. '
#define NO_MORE_INPUT 2  // Last character was EOF
#define OTHER         3  // Other (i.e. at start or middle of a sentence)  

当你读完一个字符时,你使用当前状态和字符值来确定 1) 这个句尾和 2) 什么是新状态。

因此,对于每个州,您都需要类似的东西:

      if (c == '.')
      {
        ++count; ??
        state = ??
      }
      else if (c == ' ')
      {
        ++count; ??
        state = ??
      }
      else if (c == '\n')
      {
        ++count; ??
        state = ??
      }
      else if (c == EOF)
      {
        ++count; ??
        state = ??
      }
      else
      {
        ++count; ??
        state = ??
      }

下面的示例代码使用了比所需更多的行,但这并不重要。重要的是要了解原理:

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

#define DOT           0
#define DOT_SPACE     1
#define NO_MORE_INPUT 2
#define OTHER         3

int main(void) {
  int state = OTHER;
  int c;
  int count=0;
  while(state != NO_MORE_INPUT)
  {
    c = getchar();
    switch(state)
    {
      case OTHER:
          if (c == '.')
          {
            state = DOT;
          }
          else if (c == ' ')
          {
            state = OTHER;
          }
          else if (c == '\n')
          {
            state = OTHER;
          }
          else if (c == EOF)
          {
            state = NO_MORE_INPUT;
          }
          else
          {
            state = OTHER;
          }
          break;

      case DOT:
          if (c == '.')
          {
            state = DOT;
          }
          else if (c == ' ')
          {
            state = DOT_SPACE;
          }
          else if (c == '\n')
          {
            ++count;
            state = OTHER;
          }
          else if (c == EOF)
          {
            ++count;
            state = NO_MORE_INPUT;
          }
          else
          {
            state = OTHER;
          }
          break;

      case DOT_SPACE:
          if (c == '.')
          {
            state = DOT;
          }
          else if (c == ' ')
          {
            ++count;
            state = OTHER;
          }
          else if (c == '\n')
          {
            state = OTHER;
          }
          else if (c == EOF)
          {
            state = NO_MORE_INPUT;
          }
          else
          {
            state = OTHER;
          }
          break;

      default:
          // Error
          exit(1);
    }
  }

  printf("%d\n", count);
  return 0;
}

评论

3赞 Jonathan Leffler 9/19/2023
“当你使用 读取字符时,你无法检查 EOF”——这并不完全准确,尽管你不能在调用后查看以检测 EOF。正确的工作方式是 .请注意,应根据预期值(1 个成功读取字符)检查返回值,而不是查找 EOF。在格式字符串中单独使用时,实际上无关紧要,但通常可以返回正 N(对于成功分配的转换次数)或 0(没有成功的转换)或 EOF(错误)。scanf("%c", &s)sif (scanf("%s", &s) != 1) { …EOF or read error… }%cscanf()
0赞 ident 9/20/2023
谢谢。它以许多不同的方式启发了我。
1赞 chux - Reinstate Monica 9/20/2023 #3

OP 的代码失败至少有以下原因:

  • scanf("%c", &s);缺少返回值检查,因此可能会错过 的返回值。EOF

  • After 为 true,如果读取的下一个字符为 ,则代码不会重置以正确检查候选结束序列。if (strncmp(&s, ".", 1) == 0) {'.'


弱编码

strncmp(&s, ".", 1) == 0是一种令人费解的执行方式。s == '.'

行数可能超过 INT_MAX

考虑更广泛的类型。

考虑 getchar()

代替 ,请使用 。 通常包含 256 个字符中的 1 个:0-255 或负值。那么测试就这么简单了。scanf("%c", &s)int ch = getchar()chEOFEOFch == EOF

附加一个 '\n'

以 .'\n'


相反,避免编写复杂的代码,并尝试编写尽可能接近要求的代码。

#include <stdio.h>

int main(void) {
  unsigned long long count = 0;
  int prior[2] = {EOF, EOF};  // 2 prior characters.

  for (;;) {
    int ch = getchar();

    // sentence ends in . followed by two spaces, such as "a.  "
    if (prior[1] == '.' && prior[0] == ' ' && ch == ' ') {
      count++;
    }

    // sentence ends in . followed by a newline ('\n'), such as "a.\n"
    else if (prior[0] == '.' && ch == '\n') {
      count++;
    }

    // sentence ends in . at end of file, such as "a." followed by EOF.
    else if (ch == EOF) {
      if (prior[0] == '.') {
        count++;
      }
      break;
    }

    prior[1] = prior[0];
    prior[0] = ch;
  }

  printf("%llu\n", count);
  return 0;
}

也许 OP 还想添加一个 A-Z,a-z 检测。很容易用更大的 , .一个测试的更改示例:prior[]<ctype.h>

if (isalpha(prior[2]) && prior[1] == '.' && prior[0] == ' ' && ch == ' ') {
  count++;
}

评论

0赞 chux - Reinstate Monica 9/20/2023
@ident提示:在执行编码要求的代码附近添加注释。它对以后的审查很有用。
0赞 ident 9/20/2023
注意吧!谢谢