Каков самый простой способ прочитать всю строку в программе консоли C Введенный текст может иметь переменную длину, и мы не можем делать никаких предположений относительно его содержания.
не могли бы вы уточнить, пожалуйста? как @Tim сказал ниже, это сбивает с толку то, о чем вы просите :)
Уоррен
Ответы:
81
Вам нужно динамическое управление памятью и используйте эту fgetsфункцию для чтения вашей строки. Однако, похоже, нет никакого способа увидеть, сколько символов он прочитал. Итак, вы используете fgetc:
char* getline(void){char* line = malloc(100),* linep = line;size_t lenmax =100, len = lenmax;int c;if(line == NULL)return NULL;for(;;){
c = fgetc(stdin);if(c == EOF)break;if(--len ==0){
len = lenmax;char* linen = realloc(linep, lenmax *=2);if(linen == NULL){
free(linep);return NULL;}
line = linen +(line - linep);
linep = linen;}if((*line++= c)=='\n')break;}*line ='\0';return linep;}
Примечание : никогда не используйте получает! Он не выполняет проверку границ и может переполнить ваш буфер
Предостережение - там нужно проверить результат перераспределения. Но если это не удается, то, скорее всего, есть более серьезные проблемы.
Тим
4
Вероятно, вы могли бы немного повысить эффективность, выполнив fgets с буфером и проверив, есть ли у вас символ новой строки в конце. Если вы этого не сделаете, перераспределите буфер накопления, скопируйте в него и снова fgets.
Пол Томблин,
3
Эта функция требует исправления: строка "len = lenmax;" после realloc должен либо предшествовать realloc, либо должен быть "len = lenmax >> 1;" - или какой-либо другой эквивалент, учитывающий тот факт, что половина длины уже используется.
Мэтт Галлахер,
1
@Johannes, отвечая на ваш вопрос, подход @ Paul МОЖЕТ быть быстрее в большинстве (т.е. реентерабельных) реализаций libc, поскольку ваш подход неявно блокирует stdin для каждого символа, тогда как его блокирует его один раз для каждого буфера. Вы можете использовать менее переносимый, fgetc_unlockedесли безопасность потоков не является проблемой, а производительность.
vladr
3
Обратите внимание, что это getline()отличается от стандартной getline()функции POSIX .
Джонатан Леффлер,
28
Если вы используете библиотеку GNU C или другую POSIX-совместимую библиотеку, вы можете использовать ее getline()и передать stdinей для потока файлов.
Итак, если вы искали аргументы команды, взгляните на ответ Тима. Если вы просто хотите прочитать строку с консоли:
#include<stdio.h>int main(){char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);return0;}
Да, это небезопасно, вы можете делать переполнение буфера, он не проверяет конец файла, он не поддерживает кодировки и многое другое. На самом деле я даже не подумал, сделал ли он что-нибудь из этого. Согласен, я напортачил :) Но ... когда я вижу вопрос типа «Как прочитать строку с консоли на C?», Я предполагаю, что человеку нужно что-то простое, например, gets (), а не 100 строк кода как указано выше. На самом деле, я думаю, что если вы попытаетесь написать эти 100 строк кода на самом деле, вы сделаете намного больше ошибок, чем если бы вы выбрали метод get;)
Это не позволяет использовать длинные строки ... - что, я думаю, и составляет суть его вопроса.
Тим
2
-1, gets () использовать не следует, поскольку он не выполняет проверку границ.
расслабьтесь
7
С другой стороны, если вы пишете программу для себя и вам просто нужно прочитать ввод, это нормально. Степень защиты, которая требуется программе, является частью спецификации - вы не должны каждый раз ставить ее в приоритет.
Мартин Беккет,
4
@Tim - я хочу сохранить всю историю :)
Пол Капустин
4
Проголосовали против. getsбольше не существует, поэтому в C11 это не работает.
Антти Хаапала
11
Возможно, вам потребуется использовать посимвольный цикл (getc ()), чтобы убедиться, что у вас нет переполнения буфера и не усечение ввода.
Какова цель lenздесь, при чтении также предоставляется длина
Абдул
@ Абдул см man getline. len- это длина существующего буфера, 0является магической и сообщает ему о выделении. Чтение - это количество прочитанных символов. Размер буфера может быть больше, чем read.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
6
Многие люди, как и я, приходят к этому посту с заголовком, совпадающим с искомым, хотя в описании говорится о переменной длине. В большинстве случаев мы заранее знаем длину.
Если вы заранее знаете длину, попробуйте следующее:
char str1[1001]={0};
fgets(str1,1001, stdin);// 1000 chars may be read
Как было предложено, вы можете использовать getchar () для чтения с консоли до тех пор, пока не будет возвращен конец строки или EOF, создав свой собственный буфер. Динамическое увеличение буфера может происходить, если вы не можете установить разумный максимальный размер строки.
Вы также можете использовать fgets как безопасный способ получить строку как строку C с завершающим нулем:
#include<stdio.h>char line[1024];/* Generously large value for most situations */char*eof;
line[0]='\0';/* Ensure empty line if no input delivered */
line[sizeof(line)-1]=~'\0';/* Ensure no false-null at end of buffer */
eof = fgets(line,sizeof(line), stdin);
Если вы исчерпали ввод консоли или если операция по какой-то причине завершилась неудачно, возвращается eof == NULL и строковый буфер может не измениться (поэтому удобно установить первый символ в '\ 0').
fgets не переполняет строку [] и гарантирует, что после последнего принятого символа при успешном возврате будет ноль.
Если был достигнут конец строки, символ, предшествующий завершающему «\ 0», будет «\ n».
Если нет завершающего символа '\ n' перед окончанием '\ 0', возможно, есть больше данных или что следующий запрос сообщит о конце файла. Вам нужно будет выполнить еще одну операцию, чтобы определить, что есть что. (В этом отношении выполнение цикла с помощью getchar () проще.)
В (обновленном) примере кода выше, если строка [sizeof (line) -1] == '\ 0' после успешных fgets, вы знаете, что буфер был заполнен полностью. Если в этой позиции стоит символ '\ n', значит, вам повезло. В противном случае в stdin впереди либо больше данных, либо конец файла. (Когда буфер не заполнен полностью, вы все еще можете находиться в конце файла, а также может не быть '\ n' в конце текущей строки. Поскольку вам нужно сканировать строку, чтобы найти и / или исключить любой символ '\ n' перед концом строки (первый '\ 0' в буфере), я предпочитаю использовать getchar () в первую очередь.)
Сделайте то, что вам нужно, чтобы иметь дело с тем, что строки все еще больше, чем сумма, которую вы читаете в качестве первого фрагмента. Примеры динамического увеличения буфера можно заставить работать с getchar или fgets. Есть несколько сложных крайних случаев, на которые следует обратить внимание (например, не забыть, что следующий ввод начинается с позиции '\ 0', которая завершила предыдущий ввод до того, как буфер был расширен).
Когда мы собираемся исчерпать выделенную память, мы пытаемся удвоить размер памяти.
И здесь я использую цикл для сканирования каждого символа строки один за другим, используя getchar()функцию, пока пользователь не введет '\n'или EOFсимвол
наконец, мы удаляем любую дополнительно выделенную память перед возвратом строки
//the function to read lines of variable lengthchar* scan_line(char*line){int ch;// as getchar() returns `int`long capacity =0;// capacity of the bufferlong length =0;// maintains the length of the stringchar*temp = NULL;// use additional pointer to perform allocations in order to avoid memory leakswhile(((ch = getchar())!='\n')&&(ch != EOF)){if((length +1)>= capacity){// resetting capacityif(capacity ==0)
capacity =2;// some initial fixed length else
capacity *=2;// double the size// try reallocating the memoryif((temp = realloc(line, capacity *sizeof(char)))== NULL )//allocating memory{
printf("ERROR: unsuccessful allocation");// return line; or you can exit
exit(1);}
line = temp;}
line[length]=(char) ch;//type casting `int` to `char`}
line[length +1]='\0';//inserting null character at the end// remove additionally allocated memoryif((temp = realloc(line,(length +1)*sizeof(char)))== NULL ){
printf("ERROR: unsuccessful allocation");// return line; or you can exit
exit(1);}
line = temp;return line;}
Теперь вы можете прочитать всю строку таким образом:
char*line = NULL;
line = scan_line(line);
Вот пример программы, использующей scan_line()функцию:
#include<stdio.h>#include<stdlib.h>//for dynamic allocation functionschar* scan_line(char*line){..........}int main(void){char*a = NULL;
a = scan_line(a);//function call to scan the line
printf("%s\n",a);//printing the scanned line
free(a);//don't forget to free the malloc'd pointer}
Некоторое время назад я столкнулся с той же проблемой, это было мое решение, надеюсь, это поможет.
/*
* Initial size of the read buffer
*/#define DEFAULT_BUFFER 1024/*
* Standard boolean type definition
*/typedefenum{false=0,true=1}bool;/*
* Flags errors in pointer returning functions
*/bool has_err =false;/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/char*readLine(FILE*file){char*buffer = NULL;char*tmp_buf = NULL;bool line_read =false;int iteration =0;int offset =0;if(file == NULL){
fprintf(stderr,"readLine: NULL file pointer passed!\n");
has_err =true;return NULL;}while(!line_read){if((tmp_buf = malloc(DEFAULT_BUFFER))== NULL){
fprintf(stderr,"readLine: Unable to allocate temporary buffer!\n");if(buffer != NULL)
free(buffer);
has_err =true;return NULL;}if(fgets(tmp_buf, DEFAULT_BUFFER, file)== NULL){
free(tmp_buf);break;}if(tmp_buf[strlen(tmp_buf)-1]=='\n')/* we have an end of line */
line_read =true;
offset = DEFAULT_BUFFER *(iteration +1);if((buffer = realloc(buffer, offset))== NULL){
fprintf(stderr,"readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err =true;return NULL;}
offset = DEFAULT_BUFFER * iteration - iteration;if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER)== NULL){
fprintf(stderr,"readLine: Cannot copy to buffer\n");
free(tmp_buf);if(buffer != NULL)
free(buffer);
has_err =true;return NULL;}
free(tmp_buf);
iteration++;}return buffer;}
Ваш код станет НАМНОГО проще, если вы будете использовать его gotoдля обработки случая ошибки. Тем не менее, не думаете ли вы, что вы могли бы использовать его повторно tmp_buf, вместо того, чтобы mallocснова и снова повторять его в цикле?
Shahbaz
Использование одной глобальной переменной has_errдля сообщения об ошибках делает эту функцию небезопасной для потоков и менее удобной в использовании. Не делай так. Вы уже указываете на ошибку, возвращая NULL. Есть также основания полагать, что распечатанные сообщения об ошибках не подходят для универсальной библиотечной функции.
Джонатан Леффлер,
0
В системах BSD и Android вы также можете использовать fgetln:
lineНе нуль и содержит \n(или независимо от вашей платформы используется) , в конце концов. Он становится недействительным после следующей операции ввода-вывода в потоке.
Да, функция есть. Предостережение о том, что он не предоставляет строку с завершающим нулем, достаточно велико и проблематично, поэтому, вероятно, лучше не использовать его - это опасно.
Джонатан Леффлер
0
Что-то вроде этого:
unsignedint getConsoleInput(char**pStrBfr)//pass in pointer to char pointer, returns size of buffer{char* strbfr;int c;unsignedint i;
i =0;
strbfr =(char*)malloc(sizeof(char));if(strbfr==NULL)goto error;while((c = getchar())!='\n'&& c != EOF ){
strbfr[i]=(char)c;
i++;
strbfr =(void*)realloc((void*)strbfr,sizeof(char)*(i+1));//on realloc error, NULL is returned but original buffer is unchanged//NOTE: the buffer WILL NOT be NULL terminated since last//chracter came from consoleif(strbfr==NULL)goto error;}
strbfr[i]='\0';*pStrBfr = strbfr;//successfully returns pointer to NULL terminated bufferreturn i +1;
error:*pStrBfr = strbfr;return i +1;}
Лучший и самый простой способ прочитать строку из консоли - использовать функцию getchar (), с помощью которой вы будете сохранять по одному символу за раз в массиве.
{char message[N];/* character array for the message, you can always change the character length */int i =0;/* loop counter */
printf("Enter a message: ");
message[i]= getchar();/* get the first character */while( message[i]!='\n'){
message[++i]= getchar();/* gets the next character */}
printf("Entered message is:");for( i =0; i < N; i++)
printf("%c", message[i]);return(0);
Ответы:
Вам нужно динамическое управление памятью и используйте эту
fgets
функцию для чтения вашей строки. Однако, похоже, нет никакого способа увидеть, сколько символов он прочитал. Итак, вы используете fgetc:Примечание : никогда не используйте получает! Он не выполняет проверку границ и может переполнить ваш буфер
источник
fgetc_unlocked
если безопасность потоков не является проблемой, а производительность.getline()
отличается от стандартнойgetline()
функции POSIX .Если вы используете библиотеку GNU C или другую POSIX-совместимую библиотеку, вы можете использовать ее
getline()
и передатьstdin
ей для потока файлов.источник
Очень простая, но небезопасная реализация для чтения строки для статического распределения:
Более безопасная реализация без возможности переполнения буфера, но с возможностью не читать всю строку:
Не «разница на единицу» между длиной, указанной при объявлении переменной, и длиной, указанной в строке формата. Это исторический артефакт.
источник
gets
был полностью удален из стандартаИтак, если вы искали аргументы команды, взгляните на ответ Тима. Если вы просто хотите прочитать строку с консоли:
Да, это небезопасно, вы можете делать переполнение буфера, он не проверяет конец файла, он не поддерживает кодировки и многое другое. На самом деле я даже не подумал, сделал ли он что-нибудь из этого. Согласен, я напортачил :) Но ... когда я вижу вопрос типа «Как прочитать строку с консоли на C?», Я предполагаю, что человеку нужно что-то простое, например, gets (), а не 100 строк кода как указано выше. На самом деле, я думаю, что если вы попытаетесь написать эти 100 строк кода на самом деле, вы сделаете намного больше ошибок, чем если бы вы выбрали метод get;)
источник
gets
больше не существует, поэтому в C11 это не работает.Возможно, вам потребуется использовать посимвольный цикл (getc ()), чтобы убедиться, что у вас нет переполнения буфера и не усечение ввода.
источник
getline
исполняемый примерУпоминается в этом ответе, но вот пример.
Это POSIX 7 , он выделяет нам память и повторно использует выделенный буфер в цикле.
Pointer newbs, прочтите это: Почему первый аргумент getline является указателем на указатель «char **» вместо «char *»?
реализация glibc
Нет POSIX? Возможно, вы захотите взглянуть на реализацию glibc 2.23 .
Он разрешается в
getdelim
, который является простым расширением POSIXgetline
с произвольным ограничителем строки.Он удваивает выделенную память всякий раз, когда требуется ее увеличение, и выглядит поточно-ориентированным.
Это требует некоторого расширения макроса, но вряд ли вы добьетесь большего успеха.
источник
len
здесь, при чтении также предоставляется длинаman getline
.len
- это длина существующего буфера,0
является магической и сообщает ему о выделении. Чтение - это количество прочитанных символов. Размер буфера может быть больше, чемread
.Многие люди, как и я, приходят к этому посту с заголовком, совпадающим с искомым, хотя в описании говорится о переменной длине. В большинстве случаев мы заранее знаем длину.
Если вы заранее знаете длину, попробуйте следующее:
источник: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
источник
Как было предложено, вы можете использовать getchar () для чтения с консоли до тех пор, пока не будет возвращен конец строки или EOF, создав свой собственный буфер. Динамическое увеличение буфера может происходить, если вы не можете установить разумный максимальный размер строки.
Вы также можете использовать fgets как безопасный способ получить строку как строку C с завершающим нулем:
Если вы исчерпали ввод консоли или если операция по какой-то причине завершилась неудачно, возвращается eof == NULL и строковый буфер может не измениться (поэтому удобно установить первый символ в '\ 0').
fgets не переполняет строку [] и гарантирует, что после последнего принятого символа при успешном возврате будет ноль.
Если был достигнут конец строки, символ, предшествующий завершающему «\ 0», будет «\ n».
Если нет завершающего символа '\ n' перед окончанием '\ 0', возможно, есть больше данных или что следующий запрос сообщит о конце файла. Вам нужно будет выполнить еще одну операцию, чтобы определить, что есть что. (В этом отношении выполнение цикла с помощью getchar () проще.)
В (обновленном) примере кода выше, если строка [sizeof (line) -1] == '\ 0' после успешных fgets, вы знаете, что буфер был заполнен полностью. Если в этой позиции стоит символ '\ n', значит, вам повезло. В противном случае в stdin впереди либо больше данных, либо конец файла. (Когда буфер не заполнен полностью, вы все еще можете находиться в конце файла, а также может не быть '\ n' в конце текущей строки. Поскольку вам нужно сканировать строку, чтобы найти и / или исключить любой символ '\ n' перед концом строки (первый '\ 0' в буфере), я предпочитаю использовать getchar () в первую очередь.)
Сделайте то, что вам нужно, чтобы иметь дело с тем, что строки все еще больше, чем сумма, которую вы читаете в качестве первого фрагмента. Примеры динамического увеличения буфера можно заставить работать с getchar или fgets. Есть несколько сложных крайних случаев, на которые следует обратить внимание (например, не забыть, что следующий ввод начинается с позиции '\ 0', которая завершила предыдущий ввод до того, как буфер был расширен).
источник
Создание собственной функции - это один из способов, который поможет вам добиться чтения строки из консоли.
Я использую динамическое распределение памяти для выделения необходимого объема памяти
Когда мы собираемся исчерпать выделенную память, мы пытаемся удвоить размер памяти.
И здесь я использую цикл для сканирования каждого символа строки один за другим, используя
getchar()
функцию, пока пользователь не введет'\n'
илиEOF
символнаконец, мы удаляем любую дополнительно выделенную память перед возвратом строки
Теперь вы можете прочитать всю строку таким образом:
Вот пример программы, использующей
scan_line()
функцию:образец ввода:
образец вывода:
источник
Некоторое время назад я столкнулся с той же проблемой, это было мое решение, надеюсь, это поможет.
источник
goto
для обработки случая ошибки. Тем не менее, не думаете ли вы, что вы могли бы использовать его повторноtmp_buf
, вместо того, чтобыmalloc
снова и снова повторять его в цикле?has_err
для сообщения об ошибках делает эту функцию небезопасной для потоков и менее удобной в использовании. Не делай так. Вы уже указываете на ошибку, возвращая NULL. Есть также основания полагать, что распечатанные сообщения об ошибках не подходят для универсальной библиотечной функции.В системах BSD и Android вы также можете использовать
fgetln
:Вот так:
line
Не нуль и содержит\n
(или независимо от вашей платформы используется) , в конце концов. Он становится недействительным после следующей операции ввода-вывода в потоке.источник
Что-то вроде этого:
источник
Лучший и самый простой способ прочитать строку из консоли - использовать функцию getchar (), с помощью которой вы будете сохранять по одному символу за раз в массиве.
}
источник
Эта функция должна делать то, что вы хотите:
Надеюсь, это поможет.
источник
fgets( buffer, sizeof(buffer), file );
неsizeof(buffer)-1
.fgets
оставляет место для завершающего нуля.while (!feof(file))
это всегда неправильно, и это еще один пример ошибочного использования.