从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并发送一个适当的字符串。

501265 次浏览

优雅的方式:

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");

注意,如果用户输入空字符串(即只按Enter), strtok函数将不能正常工作。它保留\n字符完整。

当然,还有其他原因。

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

如果每一行都有'\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';
}
下面是一个从fgets()保存的字符串中删除潜在'\n'的快速方法 它使用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,但两种方法都有效。


在某些情况下,来自原始fgets()buffer将不包含在"\n"中 A)对于buffer来说行太长了,所以只有'\n'之前的char被保存在buffer中。未读字符保留在流中 B)文件的最后一行没有以'\n'结尾。< / p >

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


其他一些回答的问题:

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

  2. fgets()读取的第一个char'\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); is undefined behavior: Ref. Further, it does not save any leading, separating or trailing whitespace. Now deleted.

  4. [Edit due to good later answer] There are no problems with the 1 liner buffer[strcspn(buffer, "\n")] = 0; other than performance as compared to the strlen() approach. Performance in trimming is usually not an issue given code is doing I/O - a black hole of CPU time. Should following code need the string's length or is highly performance conscious, use this strlen() approach. Else the strcspn() is a fine alternative.

也许最简单的解决方案是使用我最喜欢的一个鲜为人知的函数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'

对于单个“\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';
}
}

Tim Čas one line对于调用fgets获得的字符串来说非常神奇,因为您知道它们在末尾包含一个换行符。

如果您处于不同的上下文中,并且希望处理可能包含多个换行符的字符串,则可能需要查找strrspn。它不是POSIX,这意味着您不会在所有unix上找到它。我为自己的需要写了一个。

/* 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;
}

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

 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';

因为你将使用单引号而不是双引号。这是一个关于单引号和双引号的链接,如果你想知道更多

如果使用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;
}

: [安全问题]getline不应该被忽视。

我的新手方式;-)请让我知道这是正确的。这似乎对我所有的案子都有效:

#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;
}

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

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

  1. 查看字符串是否以或仅包含一个\0字符(空字符串)开始。在这种情况下,sl将是0,因为正如我上面所说的,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. 我们可以结合第二步。和3。通过使用逻辑运算符&&,将它们组合在一个__abc0语句中:
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);
}

注意,如果输入字符串中嵌入了__abc0,则此方法无效。如果是这样的话,strlen()将只返回第一个\0之前的字符数。但这不是一个很常见的方法,因为大多数字符串读取函数通常停止在第一个\0,并采取字符串直到空字符。

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

一般来说,与其删除不想要的数据,不如从一开始就避免写入数据。如果你不想在缓冲区中使用换行符,不要使用fgets。相反,使用getcfgetcscanf。也许是这样的:

#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()

这就是我的解。非常简单。

// 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';
}
}