从fgets()输入中删除尾随换行符

sfa*_*tor 210 c string gcc newline fgets

我试图从用户那里获取一些数据并将其发送到gcc中的另一个函数.代码是这样的.

printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
    fprintf(stderr, "Error reading Name.\n");
    exit(1);
}
Run Code Online (Sandbox Code Playgroud)

但是,我发现它最后有一个换行符\n.所以,如果我输入John它最终发送John\n.如何删除它\n并发送正确的字符串.

Tim*_*Čas 390

也许最简单的解决方案使用我最喜欢的一个鲜为人知的功能strcspn():

buffer[strcspn(buffer, "\n")] = 0;
Run Code Online (Sandbox Code Playgroud)

如果你想要它也处理'\r'(比如,如果流是二进制):

buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...
Run Code Online (Sandbox Code Playgroud)

该函数计算字符数,直到它击中a '\r'或a '\n'(换句话说,它找到第一个'\r'或者'\n').如果没有碰到任何东西,它会停在'\0'(返回字符串的长度).

请注意,即使没有换行,这也可以正常工作,因为strcspn在a处停止'\0'.在这种情况下,整条生产线只是替换'\0''\0'.

  • 这甚至可以处理罕见的`缓冲区`而不是_begins_和`'\ 0'`,这会导致`缓冲区[strlen(buffer) - 1] ='\ 0';`接近. (24认同)
  • @sidbushes:我知道你来自哪里,但我不能对特定条款的Google搜索结果负责.与谷歌交谈,而不是我. (6认同)
  • @chux:是的,我希望更多的人知道`strcspn()`.IMO中的一个更有用的功能.我决定今天编写和发布一堆像这样的常见C黑客; 使用`strcspn`和`strspn`的`strtok_r`实现是第一个:http://codepad.org/2lBkZk0w(*警告:*我不能保证它没有错误;它是仓促编写的,可能有一些).然而,我不知道我会在哪里发布它们,但是我打算以着名的"苦涩的黑客"的精神来实现它. (5认同)
  • @sidbushes:标题和内容中的问题都是从`fgets()`input*询问尾随换行符*.这也是第一个换行. (5认同)
  • 看看_robustly_ [trim`fgets()`]的方法(http://codereview.stackexchange.com/q/67608/29485).这个`strcspn()`似乎是_only_正确的单行.[`strlen`](http://stackoverflow.com/a/27729970/2410359)更快 - 虽然不那么简单. (2认同)
  • 这对于在 _first_ 换行符处终止字符串非常有用,但问题询问的是 _trailing_ 换行符。对于“fgets”来说,这种区别并不重要,但如果有人想在一般情况下使用它,那就很重要了。 (2认同)

Jer*_*fin 133

有点丑陋的方式:

char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
    *pos = '\0';
else
    /* input too long for buffer, flag error */
Run Code Online (Sandbox Code Playgroud)

有点奇怪的方式:

strtok(Name, "\n");
Run Code Online (Sandbox Code Playgroud)

请注意,strtok如果用户输入空字符串(即仅按Enter),则该功能无法按预期工作.它使\n角色完好无损.

当然还有其他一些.

  • 用strtok方式小心......不是线程安全的 (8认同)
  • 任何线程感知的C运行时库(也就是说,大多数目标都是多线程平台),`strtok()`将是线程安全的(它将使用线程本地存储来实现'inter-call'状态) .也就是说,使用非标准(但很常见的)`strtok_r()`变体通常更好. (6认同)
  • 请参阅我的答案,了解完全线程安全和可重入的变体,类似于您的`strtok`方法(并且它适用于空输入).实际上,实现`strtok`的好方法是使用`strcspn`和`strspn`. (2认同)
  • 如果您处于存在过长行风险的环境中,则处理其他情况非常重要.静默截断输入可能会导致非常具有破坏性的错误. (2认同)
  • 如果您喜欢单行并使用glibc,请尝试`*strchrnul(Name,'\n')='\ 0';`. (2认同)

Jam*_*ris 82

size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';
Run Code Online (Sandbox Code Playgroud)

  • 如果字符串为空,可能会抛出异常,不是吗?像索引超出范围. (7认同)
  • @James Morris在不寻常的情况下`fgets(buf,size,....)` - >`strlen(buf)== 0`.1)`fgets()`读作第一个`char` a`'\ 0'`.2)`size == 1` 3)`fgets()`返回`NULL`然后`buf`内容可以是任何东西.(虽然OP的代码测试为NULL)建议:`size_t ln = strlen(name); if(ln> 0 && name [ln-1] =='\n')name [ - ln] ='\ 0';` (4认同)
  • @EdwardOlamisan,但是该字符串永远不会为空。 (3认同)
  • 如果字符串为空怎么办?`ln`将为-1,除了`size_t`是无符号的,因此写入随机存储器.我想你想使用`ssize_t`并检查`ln`是否> 0. (2认同)
  • @ legends2k:搜索编译时值(尤其是`strlen`中的零值)可以比普通的char-by-char搜索更有效地实现.出于这个原因,我认为这个解决方案比`strchr`或`strcspn`更好. (2认同)

chu*_*ica 16

以下是'\n'从保存的字符串中删除潜在的快速方法fgets().
它使用strlen()2次测试.

char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {

  size_t len = strlen(buffer);
  if (len > 0 && buffer[len-1] == '\n') {
    buffer[--len] = '\0';
  }
Run Code Online (Sandbox Code Playgroud)

现在使用bufferlen根据需要.

此方法具有len后续代码值的附带好处.它可以比它快得多strchr(Name, '\n'). 参考 YMMV,但两种方法都有效.


buffer,从原来fgets()不会包含在"\n"某些情况下:
A)线太长,buffer所以只有char'\n'保存之前buffer.未读的字符仍保留在流中.
B)文件中的最后一行没有结束'\n'.

如果输入'\0'在某处嵌入了空字符,则报告的长度strlen()将不包括该'\n'位置.


其他一些答案的问题:

  1. strtok(buffer, "\n");无法删除的'\n'时候buffer"\n".从这个答案 - 在这个答案之后修改了警告这个限制.

  2. 以下罕见的情况下出现故障时,首先char通过阅读fgets()'\0'.输入以嵌入式开头时会发生这种情况'\0'.然后在合法范围之外肯定buffer[len -1]buffer[SIZE_MAX]访问内存buffer.黑客可能在愚蠢地阅读UTF16文本文件时尝试或找到的东西.写这个答案时,这就是答案的状态.后来一个非OP编辑它包括像这个答案的检查代码"".

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. sprintf(buffer,"%s",buffer);是未定义的行为:参考.此外,它不会保存任何前导,分隔或尾随空格.现在已删除.

  4. [编辑由于后来的好回答 ]与方法buffer[strcspn(buffer, "\n")] = 0;相比,除了性能之外,1衬垫没有问题strlen().鉴于代码正在进行I/O(CPU时间的黑洞),修剪性能通常不是问题.如果以下代码需要字符串的长度或具有高度的性能意识,请使用此strlen()方法.否则这strcspn()是一个很好的选择.


Ami*_*bha 10

如果每一行都有 '\n',直接从 fgets 输出中删除 '\n'

line[strlen(line) - 1] = '\0';
Run Code Online (Sandbox Code Playgroud)

除此以外:

void remove_newline_ch(char *line)
{
    int new_line = strlen(line) -1;
    if (line[new_line] == '\n')
        line[new_line] = '\0';
}
Run Code Online (Sandbox Code Playgroud)

  • @esker 不,不会。插入一个 `n` 并不会神奇地增加安全性,在这种情况下它实际上会使代码更加危险。与 `strncpy` 类似,这是一个非常不安全的函数。您链接到的帖子是不好的建议。 (4认同)
  • 这对于空字符串是不安全的,它将写入索引 -1。不要用这个。 (4认同)
  • 对链接问题中第一个答案的评论指出“请注意,strlen()、strcmp() 和 strdup() 是安全的。'n' 选项为您提供了额外的功能。” (3认同)
  • 对于空字符串 (`""`),这会失败。此外`strlen()` 返回`size_t` 而不是`int`。 (3认同)