306

我正在尝试从用户那里获取一些数据并将其发送到 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并发送正确的字符串。

4

14 回答 14

544

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

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

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

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

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

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

于 2015-02-11T18:54:56.823 回答
210

优雅的方式:

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

略丑的方式:

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

稍微奇怪的方式:

strtok(Name, "\n");

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

当然,还有其他人。

于 2010-04-22T19:26:52.803 回答
84
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';
于 2010-04-22T19:26:55.620 回答
22

下面是一种'\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'). 参考 YMMV,但两种方法都有效。


buffer,从原来的在某些情况下fgets()将不包含: A)该行太长,因此仅将前面的保存在 中。未读字符保留在流中。 B) 文件中的最后一行没有以. "\n"
bufferchar'\n'buffer
'\n'

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


其他一些答案的问题:

  1. strtok(buffer, "\n");无法删除'\n'when bufferis "\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';
    }
    
  3. sprintf(buffer,"%s",buffer);是未定义的行为:Ref。此外,它不保存任何前导、分隔或尾随空格。现在删除了。

  4. [编辑由于后面的好回答buffer[strcspn(buffer, "\n")] = 0;]与方法相比,除了性能之外,1 班轮没有问题strlen()。考虑到代码正在执行 I/O - CPU 时间的黑洞,修剪中的性能通常不是问题。如果下面的代码需要字符串的长度或者对性能有很高的要求,请使用这种strlen()方法。否则strcspn()是一个很好的选择。

于 2015-01-01T10:29:35.873 回答
11

如果每一行都有 '\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';
}
于 2013-06-30T01:15:09.803 回答
2

对于单个 '\n' 修剪,

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';
  }
}
于 2015-03-16T05:45:21.433 回答
1

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

#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;
}
于 2019-02-06T14:15:51.193 回答
1

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

  1. NAME使用strlen(), header确定内部字符串的长度string.h。注意strlen()不计算终止\0
size_t sl = strlen(NAME);

  1. 查看字符串是否以一个字符开头或仅包含一个\0字符(空字符串)。在这种情况下sl0因为strlen()正如我上面所说的那样,它不计算\0并在第一次出现时停止:
if(sl == 0)
{
   // Skip the newline replacement process.
}

  1. 检查正确字符串的最后一个字符是否为换行符'\n'。如果是这种情况,请替换\n\0. 请注意,索引计数从开始,0所以我们需要这样做NAME[sl - 1]
if(NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

请注意,如果您仅在fgets()字符串请求时按 Enter(字符串内容仅由换行符组成),NAME则此后的字符串将是一个空字符串。


  1. if我们可以使用逻辑运算符将步骤 2. 和 3. 组合在一个语句中&&
if(sl > 0 && NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

  1. 完成的代码:
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请注意,如果输入字符串中嵌入了 s,则此方法不起作用。如果是这种情况,则strlen()只会返回第一个\0. 但这并不是一种很常见的方法,因为大多数字符串读取函数通常会在第一个字符处停止并读取\0字符串直到那个空字符。

除了问题本身。尽量避免使您的代码更不清楚的双重否定:if (!(fgets(Name, sizeof Name, stdin) != NULL) {}. 你可以简单地做if (fgets(Name, sizeof Name, stdin) == NULL) {}

于 2020-03-05T18:16:49.383 回答
1

通常,与其修剪不需要的数据,不如避免一开始就编写它。如果您不希望缓冲区中有换行符,请不要使用 fgets。相反,请使用getcorfgetcscanf。也许是这样的:

#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
        char Name[256];
        char fmt[32];
        if( snprintf(fmt, sizeof fmt, "%%%zd[^\n]", sizeof Name - 1) >= (int)sizeof fmt ){
                fprintf(stderr, "Unable to write format\n");
                return EXIT_FAILURE;
        }
        if( scanf(fmt, Name) == 1 ) {
                printf("Name = %s\n", Name);
        }
        return 0;
}

请注意,这种特殊方法将使换行符不被读取,因此您可能希望使用格式字符串"%255[^\n]%*c"来丢弃它(例如,sprintf(fmt, "%%%zd[^\n]%%*c", sizeof Name - 1);),或者可能在 scanf 后面加上getchar().

于 2020-11-25T17:19:30.137 回答
0

如果 usinggetline是一个选项——不要忽视它的安全问题,并且如果你希望括号指针——你可以避免使用字符串函数,因为它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;
}

注意[安全问题]getline应该被忽视。

于 2017-11-25T07:31:30.387 回答
0

这是我的解决方案。非常简单。

// Delete new line
// char preDelete[256]  include "\n" as newline after fgets

char deletedWords[256];
int iLeng = strlen(preDelete);
int iFinal = 0;
for (int i = 0; i < iLeng; i++) {
    if (preDelete[i] == '\n') {

    }
    else {
        deletedWords[iFinal]  = preDelete[i];
        iFinal++;
    }
    if (i == iLeng -1 ) {
        deletedWords[iFinal] = '\0';
    }
}
于 2021-12-11T04:16:54.350 回答
-1

Tim Čas one liner 对于通过调用 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;
}
于 2015-11-18T17:43:26.260 回答
-2

下面的函数是我在 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

于 2016-02-18T00:41:19.600 回答
-2
 for(int i = 0; i < strlen(Name); i++ )
{
    if(Name[i] == '\n') Name[i] = '\0';
}

你应该试一试。这段代码基本上循环遍历字符串,直到找到'\n'。找到时,'\n' 将被空字符终止符 '\0' 替换

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

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

因为您将使用单引号而不是双引号。如果您想了解更多信息,这里有一个关于单引号和双引号的链接

于 2017-09-17T12:21:00.160 回答