C语言 从 fgets() 输入中删除尾随换行符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2693776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Removing trailing newline character from fgets() input
提问by sfactor
I am trying to get some data from the user and send it to another function in gcc. The code is something like this.
我正在尝试从用户那里获取一些数据并将其发送到 gcc 中的另一个函数。代码是这样的。
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
However, I find that it has a newline \ncharacter in the end. So if I enter Johnit ends up sending John\n. How do I remove that \nand send a proper string.
但是,我发现它最后有一个换行符\n。所以如果我输入John它最终会发送John\n. 如何删除它\n并发送正确的字符串。
采纳答案by Jerry Coffin
The slightly ugly way:
稍微丑陋的方式:
char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
*pos = 'strtok(Name, "\n");
';
else
/* input too long for buffer, flag error */
The slightly strange way:
有点奇怪的方式:
buffer[strcspn(buffer, "\n")] = 0;
Note that the strtokfunction doesn't work as expected if the user enters an empty string (i.e. presses only Enter). It leaves the \ncharacter intact.
请注意,strtok如果用户输入空字符串(即仅按 Enter),该函数将不会按预期工作。它使\n角色完好无损。
There are others as well, of course.
当然,还有其他的。
回答by Tim ?as
Perhaps the simplest solution uses one of my favorite little-known functions, strcspn():
也许最简单的解决方案是使用我最喜欢的鲜为人知的函数之一strcspn():
buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...
If you want it to also handle '\r'(say, if the stream is binary):
如果您希望它也处理'\r'(例如,如果流是二进制的):
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n')
name[ln] = 'char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[--len] = 'size_t len = strlen(buffer);
if (buffer[len - 1] == '\n') { // FAILS when len == 0
buffer[len -1] = 'size_t len = strlen(buffer);
if (buffer[len - 1] == '\n') { // FAILS when len == 0
buffer[len -1] = 'line[strlen(line) - 1] = 'void remove_newline_ch(char *line)
{
int new_line = strlen(line) -1;
if (line[new_line] == '\n')
line[new_line] = 'void remove_new_line(char* string)
{
size_t length = strlen(string);
if((length > 0) && (string[length-1] == '\n'))
{
string[length-1] ='void remove_multi_new_line(char* string)
{
size_t length = strlen(string);
while((length>0) && (string[length-1] == '\n'))
{
--length;
string[length] ='#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)) = 'size_t sl = strlen(NAME);
';
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;
}
';
}
}
';
}
}
';
}
';
';
}
';
}
';
}
';
The function counts the number of characters until it hits a '\r'or a '\n'(in other words, it finds the first '\r'or '\n'). If it doesn't hit anything, it stops at the '\0'(returning the length of the string).
该函数计算字符数,直到遇到 a'\r'或 a '\n'(换句话说,它找到第一个'\r'或'\n')。如果它没有击中任何东西,它会停在'\0'(返回字符串的长度)处。
Note that this works fine even if there is no newline, because strcspnstops at a '\0'. In that case, the entire line is simply replacing '\0'with '\0'.
请注意,即使没有换行符,这也能正常工作,因为strcspn在'\0'. 在这种情况下,整行只是简单地替换'\0'为'\0'。
回答by James Morris
if(sl == 0)
{
// Skip the newline replacement process.
}
回答by chux - Reinstate Monica
Below is a fast approach to remove a potential '\n'from a string saved by fgets().
It uses strlen(), with 2 tests.
下面是'\n'从fgets().
它使用strlen(), 进行 2 次测试。
if(NAME[sl - 1] == '\n')
{
NAME[sl - 1] = 'if(sl > 0 && NAME[sl - 1] == '\n')
{
NAME[sl - 1] = 'size_t sl = strlen(NAME);
if(sl > 0 && NAME[sl - 1] == '\n')
{
NAME[sl - 1] = 'void fgets_newline_kill(char a[])
{
size_t sl = strlen(a);
if(sl > 0 && a[sl - 1] == '\n')
{
a[sl - 1] = 'printf("Enter your Name: ");
if (fgets(Name, sizeof Name, stdin) == NULL) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
else {
fgets_newline_kill(NAME);
}
';
}
}
';
}
';
}
';
}
Now use bufferand lenas needed.
现在,使用buffer和len需要。
This method has the side benefit of a lenvalue for subsequent code. It can be easily faster than strchr(Name, '\n'). RefYMMV, but both methods work.
此方法具有len为后续代码提供值的附带好处。它可以轻松地比strchr(Name, '\n'). 参考YMMV,但两种方法都有效。
buffer, from the original fgets()will not contain in "\n"under some circumstances:
A) The line was too long for bufferso only charpreceding the '\n'is saved in buffer. The unread characters remain in the stream.
B) The last line in the file did not end with a '\n'.
buffer, from the original在某些情况下fgets()不会包含在"\n":
A) 该行太长了,buffer所以只在char之前'\n'保存在buffer. 未读字符保留在流中。
B) 文件中的最后一行没有以'\n'.
If input has embedded null characters '\0'in it somewhere, the length reported by strlen()will not include the '\n'location.
如果 input'\0'在某处嵌入了空字符,则报告的长度strlen()将不包括该'\n'位置。
Some other answers' issues:
其他一些答案的问题:
strtok(buffer, "\n");fails to remove the'\n'whenbufferis"\n". From this answer- amended after this answer to warn of this limitation.The following fails on rare occasions when the first
charread byfgets()is'\0'. This happens when input begins with an embedded'\0'. Thenbuffer[len -1]becomesbuffer[SIZE_MAX]accessing memory certainly outside the legitimate range ofbuffer. Something a hacker may try or found in foolishly reading UTF16 text files. This was the state of an answerwhen this answer was written. Later a non-OP edited it to include code like this answer's check for"".#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]='
'; printf("Enter last name "); nchar=getline(&lname,&size,stdin); printf("Number of characters read :%zu\n",nchar); lname[nchar-1]='/* 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; }
'; printf("Name entered %s %s\n",fname,lname); return 0; }line[strrspn(string, "\r\n")] = 0;sprintf(buffer,"%s",buffer);is undefined behavior: Ref. Further, it does not save any leading, separating or trailing whitespace. Now deleted.[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 thestrlen()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 thisstrlen()approach. Else thestrcspn()is a fine alternative.
strtok(buffer, "\n");无法删除'\n'whenbufferis"\n"。从这个答案- 在这个答案之后修改以警告这个限制。以下罕见的情况下出现故障时,首先
char由读fgets()是'\0'。当输入以嵌入的'\0'. 然后buffer[len -1]变成buffer[SIZE_MAX]肯定访问的合法范围之外的内存buffer。黑客可能会在愚蠢地阅读 UTF16 文本文件时尝试或发现的东西。这是撰写此答案时的答案状态。后来非 OP 对其进行了编辑,以包含类似此答案检查""./* 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; }sprintf(buffer,"%s",buffer);是未定义的行为:Ref。此外,它不保存任何前导、分隔或尾随空格。现在删除了。[由于后来的好答案而进行编辑]
buffer[strcspn(buffer, "\n")] = 0;与该strlen()方法相比,除了性能之外,1 个班轮没有任何问题。考虑到代码正在执行 I/O - CPU 时间的黑洞,修整性能通常不是问题。如果以下代码需要字符串的长度或非常注重性能,请使用此strlen()方法。否则,这strcspn()是一个很好的选择。
回答by Amitabha
Direct to remove the '\n' from the fgets output if every line has '\n'
如果每一行都有 '\n',直接从 fgets 输出中删除 '\n'
##代码##Otherwise:
除此以外:
##代码##回答by BEPP
For single '\n' trimming,
对于单个 '\n' 修剪,
##代码##for multiple '\n' trimming,
对于多个 '\n' 修剪,
##代码##回答by Duck Ling
My Newbie way ;-) Please let me know if that's correct. It seems to be working for all my cases:
我的新手方式 ;-) 请告诉我这是否正确。它似乎适用于我的所有情况:
##代码##回答by RobertS supports Monica Cellio
The steps to remove the newline character in the perhaps most obvious way:
以最明显的方式删除换行符的步骤:
- Determine the length of the string inside
NAMEby usingstrlen(), headerstring.h. Note thatstrlen()does not count the terminating\0.
NAME使用strlen(), header确定内部字符串的长度string.h。请注意,strlen()不计算终止\0.
- Look if the string begins with or only includes one
\0character (empty string). In this caseslwould be0sincestrlen()as I said above doesn′t count the\0and stops at the first occurrence of it:
- 查看字符串是否以一个
\0字符开头或仅包含一个字符(空字符串)。在这种情况下sl,0因为strlen()正如我上面所说的\0,在第一次出现时不计算和停止:
- Check if the last character of the proper string is a newline character
'\n'. If this is the case, replace\nwith a\0. Note that index counts start at0so we will need to doNAME[sl - 1]:
- 检查正确字符串的最后一个字符是否为换行符
'\n'。如果是这种情况,请替换\n为\0。请注意,索引计数从开始,0因此我们需要执行以下操作NAME[sl - 1]:
Note if you only pressed Enter at the fgets()string request (the string content was only consisted of a newline character) the string in NAMEwill be an empty string thereafter.
请注意,如果您仅在fgets()字符串请求时按 Enter (字符串内容仅由换行符组成),NAME则此后的字符串将是空字符串。
- We can combine step 2. and 3. together in just one
if-statement by using the logic operator&&:
- 我们可以
if通过使用逻辑运算符将步骤 2. 和 3. 结合在一个语句中&&:
- The finished code:
- 完成的代码:
If you rather like a function for use this technique by handling fgetsoutput strings in general without retyping each and every time, here is fgets_newline_kill:
如果您更喜欢通过处理fgets输出字符串而不每次都重新键入来使用此技术的函数,这里是fgets_newline_kill:
In your provided example, it would be:
在您提供的示例中,它将是:
##代码##Note that this method does not work if the input string has embedded \0s in it. If that would be the case strlen()would only return the amount of characters until the first \0. But this isn′t quite a common approach, since the most string-reading functions usually stop at the first \0and take the string until that null character.
请注意,如果输入字符串中嵌入了\0s,则此方法不起作用。如果是这种情况strlen(),则只会返回第一个\0. 但这不是一种常见的方法,因为大多数字符串读取函数通常在第一个停止并读取\0字符串直到该空字符。
Aside from the question on its own. Try to avoid double negations that make your code unclearer: if (!(fgets(Name, sizeof Name, stdin) != NULL) {}. You can simply do if (fgets(Name, sizeof Name, stdin) == NULL) {}.
除了问题本身。尽量避免使您的代码更不清楚的双重否定:if (!(fgets(Name, sizeof Name, stdin) != NULL) {}. 你可以简单地做if (fgets(Name, sizeof Name, stdin) == NULL) {}。
回答by sjsam
If using getlineis an option - Not neglecting its security issues and if you wish to brace pointers - you can avoid string functions as the getlinereturns the number of characters. Something like below
如果 usinggetline是一个选项 - 不要忽视它的安全问题,并且如果您希望括号指针 - 您可以避免使用字符串函数作为getline返回字符数。像下面这样
Note: The [ security issues]with getlineshouldn't be neglected though.
注:在[安全性问题]有getline不应该被忽视,虽然。
回答by Philippe A.
Tim ?as one liner is amazing for strings obtained by a call to fgets, because you know they contain a single newline at the end.
Tim ?as one liner 对于通过调用 fgets 获得的字符串来说是惊人的,因为你知道它们在末尾包含一个换行符。
If you are in a different context and want to handle strings that may contain more than one newline, you might be looking for strrspn. It is not POSIX, meaning you will not find it on all Unices. I wrote one for my own needs.
如果您在不同的上下文中并且想要处理可能包含多个换行符的字符串,您可能正在寻找 strrspn。它不是 POSIX,这意味着您不会在所有 Unices 上找到它。我根据自己的需要写了一篇。
##代码##For those looking for a Perl chomp equivalent in C, I think this is it (chomp only removes the trailing newline).
对于那些在 C 中寻找 Perl chomp 等效项的人,我认为就是这样(chomp 只删除了尾随的换行符)。
##代码##The strrcspn function:
strrcspn 函数:
##代码##
