执行 fgets 剥离换行的宏

Macro to do fgets stripping newline

给定以下两个语句使用 fgets 并去除换行符:

puts("Enter the name.");
fgets(temp.name, 40, stdin);
temp.name[strcspn(temp.name, "\n")] = 0;

下面的宏是否足以代替这个?

#define FGETS(str, len)  fgets(str, len, stdin); str[strcspn(str, "\n")] = 0
FGETS(temp.name, 40);

有什么不足或可以改进的地方吗?

一些备注:

  • 当一个宏由多个指令组成时,最好用“do { ... } while(0)”将其括起来,以便能够在没有括号的“if”语句中使用它,防止一些算术运算对宏的操作...
  • 宏的参数必须用括号括起来,因为它们可能不是简单的变量而是一些表达式;
  • fgets() returns 如果输入 EOF 或出错则为 NULL;

因此有以下命题:

#define FGETS(str, len) do {  if (fgets((str), (len), stdin) != NULL) (str)[strcspn((str), "\n")] = 0; else (str)[0] = '[=10=]'; } while(0)

然后您可以将其用作:

if (expression)
  FGETS(str, len);
else
  do_something_else;
...
FGETS(buf + 34, LEN);

N.B.:这个 page 提供了很多制作安全宏的技巧。

#define READLINE(str, len) do { if(fgets((str), (len), stdin) != NULL) { (str)[strcspn((str), "\n")] = 0; } else { /*TODO: Handle error*/ } } while (0)

注意,我特地从 FGETS 重命名了它,因为它硬编码了 stdin 作为“文件”部分。在我看来,称它为 FGETS 是一种误导。此外,为参数添加了括号 (pretty much a must for macros), wrapped it in a do - while(0) (here's why) 和处理错误的基本框架。

通常,我会 do { } while (0); 把戏。

但是,如果您想要一个替代 fgets 的宏,您可以在其中透明地测试 return 值,怎么样:

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

#define FGETS(_buf,_len,_xf) \
    ({ \
        char *_cp = fgets(_buf,_len,_xf); \
        if (_cp != NULL) \
            _buf[strcspn(_buf,"\n")] = 0; \
        _cp; \
    })

#define FGETOF(_buf,_xf) \
    FGETS(_buf,sizeof(_buf),_xf)

int
main(void)
{
    char buf[100];

    while (1) {
        if (FGETOF(buf,stdin) == NULL)
            break;
        printf("buf: '%s'\n",buf);
    }

    return 0;
}

多行宏没问题,但可以使用 inline 函数

清理[速度不受影响]:
#include <stdio.h>
#include <string.h>

static inline char *
xfgets(char *buf,size_t siz,FILE *xf)
{
    char *cp;

    cp = fgets(buf,siz,xf);

    if (cp != NULL)
        buf[strcspn(buf,"\n")] = 0;

    return cp;
}

#define FGETS(_buf,_len,_xf) \
    xfgets(_buf,_len,_xf)

#define FGETOF(_buf,_xf) \
    FGETS(_buf,sizeof(_buf),_xf)

int
main(void)
{
    char buf[100];

    while (1) {
        if (FGETOF(buf,stdin) == NULL)
            break;
        printf("buf: '%s'\n",buf);
    }

    return 0;
}

文件input/output是时间的后坑。在干净的函数上使用宏几乎没有什么好处。

也许也有做提示的功能?

代码使用每个 C2x priciple 的前导尺寸。

char *getaline(int size, char *s, const char *prompt) {
  if (prompt) {
    fputs(prompt, stdout);
  }
  if (fgets(s, size, stdin)) {
    // Perhaps add detection of failing to read the whole line?

    s[strcspn(s, "\n")] = '[=10=]';
    return s;
  }
  if (size > 0) {
    *s = '[=10=]';
  }
  return NULL;
}