Как прочитать строку из консоли в C?
Какой самый простой способ прочитать полную строку в консольной программе на C? Введенный текст может иметь переменную длину, и мы не можем делать никаких предположений относительно его содержания.
16 ответов
Вам необходимо динамическое управление памятью и использовать 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;
}
Примечание: никогда не используйте, получает! Он не выполняет проверку границ и может переполнить ваш буфер
Если вы используете библиотеку GNU C или другую POSIX-совместимую библиотеку, вы можете использовать getline()
и передать stdin
к нему для файлового потока.
Очень простая, но небезопасная реализация для чтения строки для статического размещения:
char line[1024];
scanf("%[^\n]", line);
Более безопасная реализация, без возможности переполнения буфера, но с возможностью не читать всю строку:
char line[1024];
scanf("%1023[^\n]", line);
Не "разница на единицу" между указанной длиной, объявляющей переменную, и длиной, указанной в строке формата. Это исторический артефакт.
Итак, если вы искали командные аргументы, взгляните на ответ Тима. Если вы просто хотите прочитать строку из консоли:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Да, это небезопасно, вы можете выполнить переполнение буфера, он не проверяет конец файла, он не поддерживает кодировки и многое другое. На самом деле, я даже не думал, сделал ли это что-нибудь из этого. Я согласен, что вроде как облажался:) Но... когда я вижу вопрос типа "Как прочитать строку из консоли в C?", Я предполагаю, что человеку нужно что-то простое, вроде gets(), а не 100 строк кода как выше. На самом деле, я думаю, что если бы вы попытались написать эти 100 строк кода на самом деле, вы бы сделали гораздо больше ошибок, чем сделали бы, если бы вы выбрали;)
getline
работоспособный пример
Упоминается в этом ответе, но вот пример.
Это POSIX 7, он выделяет нам память и прекрасно использует выделенный буфер в цикле.
Новички указателя, прочитайте это: почему первый аргумент getline является указателем на указатель "char**" вместо "char*"?
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
while (read != -1) {
puts("enter a line");
read = getline(&line, &len, stdin);
printf("line = %s", line);
printf("line length = %zu\n", read);
puts("");
}
free(line);
return 0;
}
реализация glibc
Нет POSIX? Может быть, вы хотите посмотреть на реализацию glibc 2.23.
Это разрешает getdelim
, который является простым надмножеством POSIX getline
с произвольным ограничителем строки.
Он удваивает выделенную память всякий раз, когда требуется увеличение, и выглядит поточно-ориентированным.
Это требует некоторого расширения макроса, но вы вряд ли добьетесь большего успеха.
Возможно, вам придется использовать символьный цикл (getc()), чтобы избежать переполнения буфера и не обрезать ввод.
Как и предполагалось, вы можете использовать getchar() для чтения из консоли до тех пор, пока не будет возвращен конец строки или EOF, создав собственный буфер. Динамическое увеличение буфера может произойти, если вы не можете установить разумный максимальный размер строки.
Вы также можете использовать fgets в качестве безопасного способа получения строки в виде строки с нулевым символом в конце:
#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'.
Если перед окончанием "\ 0" нет завершающего символа "\ n", возможно, имеется больше данных или что следующий запрос сообщит об окончании файла. Вы должны будете сделать еще один fgets, чтобы определить, что есть что. (В этом отношении цикл с getchar() проще.)
В приведенном выше (обновленном) примере кода, если строка [sizeof(строка)-1] == '\0' после успешного выполнения fgets, вы знаете, что буфер был заполнен полностью. Если за этой позицией стоит '\ n', вы знаете, что вам повезло. В противном случае в stdin будет больше данных или конец файла. (Когда буфер заполнен не полностью, вы все равно можете находиться в конце файла, и в конце текущей строки также не может быть '\n'. Так как вам нужно сканировать строку, чтобы найти и / или исключить любой \ n перед концом строки (первый \ 0 в буфере), я склонен предпочесть использовать getchar() в первую очередь.)
Делайте то, что вам нужно, чтобы справиться с тем, что все еще больше строк, чем количество, которое вы прочитали в качестве первого чанка. Можно привести примеры динамически растущего буфера для работы с getchar или fgets. Есть некоторые хитрые крайние случаи, которые нужно учитывать (например, помнить, что следующий ввод должен быть сохранен в позиции '\0', которая закончила предыдущий ввод перед расширением буфера).
Многие люди, как и я, приходят на этот пост с заголовком, совпадающим с тем, что ищут, хотя в описании говорится о переменной длине. В большинстве случаев мы заранее знаем длину.
Если вы знаете длину перед рукой, попробуйте ниже:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
источник: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Как прочитать строку из консоли в C?
Создание вашей собственной функции- это один из способов, который поможет вам добиться чтения строки из консоли в C.
Я использую динамическое распределение памяти, чтобы выделить только достаточный объем памяти, необходимый для хранения всех символов строки вместе с
'\0'
персонаж.И здесь я использую цикл для сканирования каждого символа строки один за другим, используя
getchar()
функция, пока пользователь не войдет'\n'
или жеEOF
персонаж//the function to read lines of variable length char* scan_line(char *line) { int ch; //as getchar() returns `int` if( (line = malloc(sizeof(char))) == NULL) //allocating memory { //checking if allocation was successful or not printf("unsuccessful allocation"); exit(1); } line[0]='\0'; for(int index = 0; ( (ch = getchar())!='\n' ) && (ch != EOF) ; index++) { if( (line = realloc(line, (index + 2)*sizeof(char))) == NULL ) { //checking if reallocation was successful or not printf("unsuccessful reallocation"); exit(1); } line[index] = (char) ch; //type casting `int` to `char` line[index + 1] = '\0'; //inserting null character at the end } return line; }
Теперь вы можете прочитать полную строку следующим образом:
char *line = NULL; line = scan_line(line);
Вот пример программы, использующей scan_line()
функция:
#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* 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
}
образец ввода:
Twinkle Twinkle little star.... in the sky!
образец вывода:
Twinkle Twinkle little star.... in the sky!
Существует простой синтаксис, подобный регулярному выражению, который можно использовать внутри scanf для получения всей строки в качестве входных данных.
scanf("%[^\n]%*c", str);
^\n указывает на ввод до тех пор, пока не встретится новая строка. Затем с помощью %*c он считывает символ новой строки, и здесь использованный * указывает, что этот символ новой строки отбрасывается.
Образец кода
#include <stdio.h>
int main()
{
char S[101];
scanf("%[^\n]%*c", S);
printf("%s", S);
return 0;
}
Я столкнулся с той же проблемой некоторое время назад, это было мое решение, надеюсь, это поможет.
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ 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;
}
На системах BSD и Android вы также можете использовать fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Вот так:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
line
не имеет нулевого завершения и содержит \n
(или то, что ваша платформа использует) в конце. Он становится недействительным после следующей операции ввода-вывода в потоке.
Что-то вроде этого:
unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
char * strbfr;
int c;
unsigned int 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 console
if(strbfr==NULL) goto error;
}
strbfr[i] = '\0';
*pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
return 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 );
}
Вот минимальная реализация для этого, приятно то, что она не будет сохранять '\n', однако вы должны указать размер для чтения в целях безопасности:
#include <stdio.h>
#include <errno.h>
int sc_gets(char *buf, int n)
{
int count = 0;
char c;
if (__glibc_unlikely(n <= 0))
return -1;
while (--n && (c = fgetc(stdin)) != '\n')
buf[count++] = c;
buf[count] = '\0';
return (count != 0 || errno != EAGAIN) ? count : -1;
}
Тест с:
#define BUFF_SIZE 10
int main (void) {
char buff[BUFF_SIZE];
sc_gets(buff, sizeof(buff));
printf ("%s\n", buff);
return 0;
}
NB: вы ограничены INT_MAX, чтобы найти возврат вашей строки, чего более чем достаточно.
Эта функция должна делать то, что вы хотите:
char* readLine( FILE* file )
{
char buffer[1024];
char* result = 0;
int length = 0;
while( !feof(file) )
{
fgets( buffer, sizeof(buffer), file );
int len = strlen(buffer);
buffer[len] = 0;
length += len;
char* tmp = (char*)malloc(length+1);
tmp[0] = 0;
if( result )
{
strcpy( tmp, result );
free( result );
result = tmp;
}
strcat( result, buffer );
if( strstr( buffer, "\n" ) break;
}
return result;
}
char* line = readLine( stdin );
/* Use it */
free( line );
Надеюсь, это поможет.