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

时间:2010-04-22 19:21:19

标签: 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);
}

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

14 个答案:

答案 0 :(得分:354)

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

buffer[strcspn(buffer, "\n")] = 0;

如果您希望它也处理'\r'(例如,如果流是二进制):

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

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

请注意,即使没有换行符,这也可以正常工作,因为strcspn会停在'\0'。在这种情况下,整行只是将'\0'替换为'\0'

答案 1 :(得分:121)

略显丑陋的方式:

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

有点奇怪的方式:

strtok(Name, "\n");

请注意,如果用户输入空字符串(即仅按Enter),strtok函数将无法按预期工作。它使\n字符保持不变。

当然还有其他人。

答案 2 :(得分:82)

size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';

答案 3 :(得分: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';
  }

现在根据需要使用bufferlen

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


在某些情况下,原始buffer中的

fgets()将不会包含在"\n"中:
A)buffer的行太长,因此只有char之前的'\n'保存在buffer中。未读的字符仍保留在流中 B)文件中的最后一行没有以'\n'结尾。

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


其他一些答案的问题:

    {li>

    strtok(buffer, "\n");无法在'\n'buffer时移除"\n"。从这个answer - 在这个答案之后修改了警告这个限制。

  1. char读取的第一个fgets()'\0'的情况下,以下情况很少见。输入以嵌入式'\0'开头时会发生这种情况。然后buffer[len -1]变为buffer[SIZE_MAX]访问内存肯定超出合法范围buffer。黑客可能在愚蠢地阅读UTF16文本文件时尝试或找到的东西。写这个答案时,这是answer的状态。之后非OP对其进行了编辑,以包含此答案的""检查代码。

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
    
  2. sprintf(buffer,"%s",buffer);是未定义的行为:Ref。此外,它不会保存任何前导,分隔或尾随空格。现在deleted

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

答案 4 :(得分:3)

如果每行都有'\ n'

,则直接从fgets输出中删除'\ n'
line[strlen(line) - 1] = '\0';

否则:

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

答案 5 :(得分:1)

单个'\和'修剪,

void remove_new_line(char* string)
{
    size_t length = strlen(string);
    if((length > 0) && (string[length-1] == '\n'))
    {
        string[length-1] ='\0';
    }
}

进行多次'\ n'修剪,

void remove_multi_new_line(char* string)
{
  size_t length = strlen(string);
  while((length>0) && (string[length-1] == '\n'))
  {
      --length;
      string[length] ='\0';
  }
}

答案 6 :(得分:0)

蒂姆Čas通过调用fgets获得的字符串非常棒,因为你知道它们最后只包含一个换行符。

如果您处于不同的上下文中并且想要处理可能包含多个换行符的字符串,那么您可能正在寻找strrspn。它不是POSIX,意味着你不会在所有Unices上找到它。我根据自己的需要写了一篇。

/* Returns the length of the segment leading to the last 
   characters of s in accept. */
size_t strrspn (const char *s, const char *accept)
{
  const char *ch;
  size_t len = strlen(s);

more: 
  if (len > 0) {
    for (ch = accept ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        len--;
        goto more;
      }
    }
  }
  return len;
}

对于那些在C中寻找Perl chomp等效的人,我认为就是这样(chomp只删除尾随的换行符。)

line[strrspn(string, "\r\n")] = 0;

strrcspn函数:

/* Returns the length of the segment leading to the last 
   character of reject in s. */
size_t strrcspn (const char *s, const char *reject)
{
  const char *ch;
  size_t len = strlen(s);
  size_t origlen = len;

  while (len > 0) {
    for (ch = reject ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        return len;
      }
    }
    len--;
  }
  return origlen;
}

答案 7 :(得分:0)

如果使用getline是一个选项 - 不要忽视它的安全问题,如果你想支持指针 - 你可以避免字符串函数,因为getline返回字符数。像下面的东西

#include<stdio.h>
#include<stdlib.h>
int main(){
char *fname,*lname;
size_t size=32,nchar; // Max size of strings and number of characters read
fname=malloc(size*sizeof *fname);
lname=malloc(size*sizeof *lname);
if(NULL == fname || NULL == lname){
 printf("Error in memory allocation.");
 exit(1);
}
printf("Enter first name ");
nchar=getline(&fname,&size,stdin);
if(nchar == -1){ // getline return -1 on failure to read a line.
 printf("Line couldn't be read.."); 
 // This if block could be repeated for next getline too
 exit(1);
}
printf("Number of characters read :%zu\n",nchar);
fname[nchar-1]='\0';
printf("Enter last name ");
nchar=getline(&lname,&size,stdin);
printf("Number of characters read :%zu\n",nchar);
lname[nchar-1]='\0';
printf("Name entered %s %s\n",fname,lname);
return 0;
}

注意[ security issues ]getline不应该被忽略。

答案 8 :(得分:0)

我的新手方式;-)请告诉我是否正确。它似乎适用于我所有的情况:

#define IPT_SIZE 5

int findNULL(char* arr)
{
    for (int i = 0; i < strlen(arr); i++)
    {
        if (*(arr+i) == '\n')
        {
            return i;
        }
    }
    return 0;
}

int main()
{
    char *input = malloc(IPT_SIZE + 1 * sizeof(char)), buff;
    int counter = 0;

    //prompt user for the input:
    printf("input string no longer than %i characters: ", IPT_SIZE);
    do
    {
        fgets(input, 1000, stdin);
        *(input + findNULL(input)) = '\0';
        if (strlen(input) > IPT_SIZE)
        {
            printf("error! the given string is too large. try again...\n");
            counter++;
        }
        //if the counter exceeds 3, exit the program (custom function):
        errorMsgExit(counter, 3); 
    }
    while (strlen(input) > IPT_SIZE);

//rest of the program follows

free(input)
return 0;
}

答案 9 :(得分:0)

以可能最明显的方式删除换行符的步骤:

  1. 使用faceCascade = cv2.CascadeClassifier( os.path.join(cv2.data.haarcascades, "haarcascade_frontalface_default.xml") ) ,标头NAME确定strlen()中字符串的长度。请注意,string.h不计入终结点strlen()
\0

  1. 查看字符串以开头还是仅包含一个size_t sl = strlen(NAME); 字符(空字符串)。在这种情况下,\0将是sl,因为如上所述,0不计算strlen()并在第一次出现时停止:
\0

  1. 检查正确字符串的最后一个字符是否为换行符if(sl == 0) { // Skip the newline replacement process. } 。在这种情况下,将'\n'替换为\n。请注意,索引计数从\0开始,因此我们需要做0
NAME[sl - 1]

请注意,如果仅在if(NAME[sl - 1] == '\n') { NAME[sl - 1] = '\0'; } 字符串请求中按Enter(字符串内容仅由换行符组成),fgets()中的字符串此后将为空字符串。


  1. 我们可以使用逻辑运算符NAME将第2步和第3步仅合并成一个if语句:
&&

  1. 完成的代码:
if(sl > 0 && NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

如果您更喜欢通过处理size_t sl = strlen(NAME); if(sl > 0 && NAME[sl - 1] == '\n') { NAME[sl - 1] = '\0'; } 输出字符串而不使用每次重新输入的方式来使用此技术的函数,则为fgets

fgets_newline_kill

在您提供的示例中,它将是:

void fgets_newline_kill(char a[])
{
    size_t sl = strlen(a);

    if(sl > 0 && a[sl - 1] == '\n')
    {
       a[sl - 1] = '\0';
    }
}

请注意,如果输入字符串中嵌入了printf("Enter your Name: "); if (fgets(Name, sizeof Name, stdin) == NULL) { fprintf(stderr, "Error reading Name.\n"); exit(1); } else { fgets_newline_kill(NAME); } ,则此方法不起作用。如果是这种情况,\0将只返回字符数量,直到第一个strlen()。但这不是一种很常见的方法,因为大多数读取字符串的函数通常在第一个\0处停止,然后将字符串提取到该空字符为止。

除了问题本身。尝试避免使代码不清楚的双重否定:\0。您只需执行if (!(fgets(Name, sizeof Name, stdin) != NULL) {}

答案 10 :(得分:0)

我很惊讶没有人提到这一点。通常,不要修剪不需要的数据,而要避免首先写入它们。如果您不希望换行符出现在缓冲区中,请不要使用fgets。而是使用getcfgetcscanf。也许像这样:

#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
        char Name[256];
        char fmt[32];
        int rc;
        sprintf(fmt, "%%%zd[^\n]", sizeof Name - 1);
        if( (rc = scanf(fmt, Name)) == 1 ) {
                printf("Name = %s\n", Name);
        }
        return rc == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
}

答案 11 :(得分:-1)

下面的函数是我在Github上维护的字符串处理库的一部分。它会从字符串中删除和不需要的字符,正是您想要的

int zstring_search_chr(const char *token,char s){
    if (!token || s=='\0')
        return 0;

    for (;*token; token++)
        if (*token == s)
            return 1;

    return 0;
}

char *zstring_remove_chr(char *str,const char *bad) {
    char *src = str , *dst = str;
    while(*src)
        if(zstring_search_chr(bad,*src))
            src++;
        else
            *dst++ = *src++;  /* assign first, then incement */

    *dst='\0';
        return str;
}

示例用法可以是

Example Usage
      char s[]="this is a trial string to test the function.";
      char const *d=" .";
      printf("%s\n",zstring_remove_chr(s,d));

  Example Output
      thisisatrialstringtotestthefunction

您可能想要检查其他可用功能,甚至可以参与项目:) https://github.com/fnoyanisi/zString

答案 12 :(得分:-1)

 for(int i = 0; i < strlen(Name); i++ )
{
    if(Name[i] == '\n') Name[i] = '\0';
}

你应该试一试。此代码基本上循环遍历字符串,直到找到&#39; \ n&#39;。当它找到&#39; \ n&#39;将由空字符终结符&#39; \ 0&#39;

替换

请注意,您要比较此行中的字符而不是字符串,然后不需要使用strcmp():

if(Name[i] == '\n') Name[i] = '\0';

因为您将使用单引号而不是双引号。 Here's如果您想了解更多信息,可以使用关于单引号和双引号的链接

答案 13 :(得分:-1)

试试这个:

<html>
    <head>
        <title>Document</title>
        <!-- jquery -->
        <script src="assests/jquery/jquery.min.js"></script>
        <!-- jquery ui -->
        <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
        <script type="text/javascript" src="http://code.jquery.com/ui/1.10.1/jquery-ui.min.js"></script>
        <script type="text/javascript">
            $('#clientName').autocomplete({
                source: 'php_action/fetchClient.php',
                minLength: 2,
                response: function(event, ui) {
                    // ui.content is the array that's about to be sent to the response callback.
                    if (ui.content.length === 0) {
                        $("#empty-message").text("No results found");
                        }
                    else {
                        $("#empty-message").empty();
                    }
                }
            });
        </script>
    </head>
    <body>
        <input type="text" id="clientName" class="form-control" autocomplete="on">
    </body>

</html>