Удаление завершающего символа новой строки из ввода 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
и отправьте правильную строку.
17 ответов
Немного безобразный способ:
char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
*pos = '\0';
else
/* input too long for buffer, flag error */
Немного странный способ:
strtok(Name, "\n");
Обратите внимание, что strtok
Функция не работает должным образом, если пользователь вводит пустую строку (т.е. нажимает только Enter). Это оставляет \n
персонаж не поврежден.
Конечно, есть и другие.
Возможно, самое простое решение использует одну из моих любимых малоизвестных функций, 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'
,
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n')
name[ln] = '\0';
Ниже приведен быстрый подход к удалению потенциальных '\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';
}
Сейчас использую buffer
а также len
по мере необходимости.
Этот метод имеет побочное преимущество len
значение для последующего кода. Это может быть легко быстрее, чем strchr(Name, '\n')
, Реф YMMV, но оба метода работают.
buffer
из оригинала fgets()
не будет содержать в "\n"
при некоторых обстоятельствах:
А) Линия была слишком длинной для buffer
так только char
предшествующий '\n'
сохраняется в buffer
, Непрочитанные символы остаются в потоке.
Б) Последняя строка в файле не заканчивается '\n'
,
Если ввод содержит нулевые символы '\0'
в нем где-то, длина сообщается strlen()
не будет включать '\n'
место нахождения.
Некоторые другие ответы на вопросы:
strtok(buffer, "\n");
не может удалить'\n'
когдаbuffer
является"\n"
, Из этого ответа - исправлены после этого ответа, чтобы предупредить об этом ограничении.Следующее терпит неудачу в редких случаях, когда первый
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'; }
sprintf(buffer,"%s",buffer);
не определено поведение: Ref. Кроме того, он не сохраняет начальные, разделяющие или конечные пробелы. Сейчас удалено.[Редактировать из-за хорошего последующего ответа] Нет проблем с 1 вкладышем
buffer[strcspn(buffer, "\n")] = 0;
кроме производительности по сравнению сstrlen()
подход. Производительность при обрезке обычно не является проблемой, учитывая, что код выполняет ввод-вывод - черная дыра процессорного времени. Если следующий код нуждается в длине строки или имеет высокую производительность, используйте этоstrlen()
подход. Остальноеstrcspn()
прекрасная альтернатива.
Прямое удаление '\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';
}
Для одиночного тримминга \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';
}
}
При использовании 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;
}
Шаги по удалению символа новой строки, пожалуй, наиболее очевидным способом:
- Определите длину струны внутри
NAME
используяstrlen()
, заголовокstring.h
. Обратите внимание, чтоstrlen()
не учитывает прекращение\0
.
size_t sl = strlen(NAME);
- Посмотрите, начинается ли строка с одного или включает только один
\0
символ (пустая строка). В этом случаеsl
было бы0
посколькуstrlen()
как я сказал выше, не учитывает\0
и останавливается при первом его появлении:
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()
строковый запрос (содержимое строки состояло только из символа новой строки) строка в NAME
после этого будет пустая строка.
- Мы можем объединить шаги 2 и 3 в один
if
-выражение с использованием логического оператора&&
:
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) {}
. Вы можете просто сделатьif (fgets(Name, sizeof Name, stdin) == NULL) {}
.
Я немного удивлен, что об этом никто не упомянул. В общем, вместо того, чтобы обрезать данные, которые вам не нужны, в первую очередь избегайте их записи. Если вам не нужна новая строка в буфере, не используйте fgets. Вместо этого используйте
getc
или же
fgetc
или же
scanf
. Возможно что-то вроде:
#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;
}
Чтобы расширить ответы @Jerry Coffin и @Tim Čas:
Эта версия по своей конструкции намного быстрее, чем (и версии, вероятно, самые быстрые из всех).
Внутреннее устройство должно перебирать строку, и, если оно реализовано разумно, оно делает это только один раз и где-то сохраняет длину строки. Затем при поиске ему также приходится использовать вложенный цикл for, проходящий через"\n"
нить.
Игнорируя такие вещи, как размер слова, которые учитывает реализация этих функций на уровне библиотеки, наивная реализация может выглядеть следующим образом:
char* my_strchr (const char *s, int c)
{
while(*s != '\0')
{
if(*s == c)
return (char*)s;
s++;
}
return NULL;
}
size_t my_strcspn (const char *s1, const char *s2)
{
size_t s2_length = strlen(s2);
size_t i;
for(i=0; s1[i] != '\0'; i++)
{
for(size_t j=0; j<s2_length; j++)
{
if(s1[i] == s2[j])
{
return i;
}
}
}
return i;
}
В случае , на каждый символ приходится две ветки. Один ищет нулевой терминатор, а другой сравнивает текущий символ с искомым.
В случае , он должен либо предварительно вычислить размер, как в моем примере, либо, альтернативно, перебрать его, ища ноль, а также ключ поиска. Последнее, по сути, именно то, что нужно, поэтому внутренний цикл можно было бы заменить на . Независимо от того, как мы это реализуем, будет много дополнительных ветвей.
Внимательный лингвист также может заметить отсутствие
restrict
вstrcspn
определение стандартной библиотеки. Это означает, что компилятору не разрешено предполагать, чтоs1
иs2
это разные строки. Это также блокирует некоторые оптимизации.
Версия будет быстрее обеих, так какstrlen
нужно только проверить нулевое завершение и ничего больше. Хотя, как упоминалось в ответе @chux — Восстановить Монику, в некоторых ситуациях это не сработает, поэтому он немного более хрупкий, чем другие версии.
Корень проблемы — плохой APIfgets
функция - если бы она была реализована лучше в те времена, она бы возвращала размер, соответствующий количеству фактически прочитанных символов, и это было бы здорово. Или, альтернативно, указатель на последний символ читается какstrchr
. Вместо этого стандартная библиотека тратила возвращаемое значение, возвращая указатель на первый символ переданной строки, что в некоторой степени полезно.
Это мое решение. Очень простой.
// 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';
}
}
Tim Č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;
}
Для тех, кто ищет Perl-аналог chomp в C, я думаю, что это так (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
for(int i = 0; i < strlen(Name); i++ )
{
if(Name[i] == '\n') Name[i] = '\0';
}
Вы должны попробовать. Этот код в основном перебирает строку до тех пор, пока не найдет \ n. Когда он будет найден, \ n будет заменен нулевым символом-терминатором
Обратите внимание, что вы сравниваете символы, а не строки в этой строке, поэтому нет необходимости использовать strcmp():
if(Name[i] == '\n') Name[i] = '\0';
так как вы будете использовать одинарные кавычки, а не двойные кавычки. Вот ссылка о одиночных и двойных кавычках, если вы хотите узнать больше
Попробуй это:
int remove_cr_lf(char *str)
{
int len =0;
len = strlen(str);
for(int i=0;i<5;i++)
{
if (len>0)
if (str[len-1] == '\n')
{
str[len-1] = 0;
len--;
}
if (len>0)
if (str[len-1] == '\r')
{
str[len-1] = 0;
len--;
}
}
return 0;
}
char name[1024];
unsigned int len;
printf("Enter your name: ");
fflush(stdout);
if (fgets(name, sizeof(name), stdin) == NULL) {
fprintf(stderr, "error reading name\n");
exit(1);
}
len = strlen(name);
if (name[len - 1] == '\n')
name[len - 1] = '\0';