Каков самый простой способ прочитать всю строку в программе консоли C Введенный текст может иметь переменную длину, и мы не можем делать никаких предположений относительно его содержания.
Каков самый простой способ прочитать всю строку в программе консоли C Введенный текст может иметь переменную длину, и мы не можем делать никаких предположений относительно его содержания.
Ответы:
Вам нужно динамическое управление памятью и используйте эту 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;
}
Примечание : никогда не используйте получает! Он не выполняет проверку границ и может переполнить ваш буфер
fgetc_unlocked
если безопасность потоков не является проблемой, а производительность.
getline()
отличается от стандартной getline()
функции POSIX .
Очень простая, но небезопасная реализация для чтения строки для статического распределения:
char line[1024];
scanf("%[^\n]", line);
Более безопасная реализация без возможности переполнения буфера, но с возможностью не читать всю строку:
char line[1024];
scanf("%1023[^\n]", line);
Не «разница на единицу» между длиной, указанной при объявлении переменной, и длиной, указанной в строке формата. Это исторический артефакт.
gets
был полностью удален из стандарта
Итак, если вы искали аргументы команды, взгляните на ответ Тима. Если вы просто хотите прочитать строку с консоли:
#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 строк кода на самом деле, вы сделаете намного больше ошибок, чем если бы вы выбрали метод get;)
gets
больше не существует, поэтому в C11 это не работает.
getline
исполняемый пример
Упоминается в этом ответе, но вот пример.
Это POSIX 7 , он выделяет нам память и повторно использует выделенный буфер в цикле.
Pointer newbs, прочтите это: Почему первый аргумент 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
с произвольным ограничителем строки.
Он удваивает выделенную память всякий раз, когда требуется ее увеличение, и выглядит поточно-ориентированным.
Это требует некоторого расширения макроса, но вряд ли вы добьетесь большего успеха.
len
здесь, при чтении также предоставляется длина
man getline
. len
- это длина существующего буфера, 0
является магической и сообщает ему о выделении. Чтение - это количество прочитанных символов. Размер буфера может быть больше, чем read
.
Многие люди, как и я, приходят к этому посту с заголовком, совпадающим с искомым, хотя в описании говорится о переменной длине. В большинстве случаев мы заранее знаем длину.
Если вы заранее знаете длину, попробуйте следующее:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
источник: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Как было предложено, вы можете использовать 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', которая завершила предыдущий ввод до того, как буфер был расширен).
Как прочитать строчку из консоли на C?
Создание собственной функции - это один из способов, который поможет вам добиться чтения строки из консоли.
Я использую динамическое распределение памяти для выделения необходимого объема памяти
Когда мы собираемся исчерпать выделенную память, мы пытаемся удвоить размер памяти.
И здесь я использую цикл для сканирования каждого символа строки один за другим, используя getchar()
функцию, пока пользователь не введет '\n'
или EOF
символ
наконец, мы удаляем любую дополнительно выделенную память перед возвратом строки
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (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 memory
if( (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 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!
Некоторое время назад я столкнулся с той же проблемой, это было мое решение, надеюсь, это поможет.
/*
* 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;
}
goto
для обработки случая ошибки. Тем не менее, не думаете ли вы, что вы могли бы использовать его повторно tmp_buf
, вместо того, чтобы malloc
снова и снова повторять его в цикле?
has_err
для сообщения об ошибках делает эту функцию небезопасной для потоков и менее удобной в использовании. Не делай так. Вы уже указываете на ошибку, возвращая NULL. Есть также основания полагать, что распечатанные сообщения об ошибках не подходят для универсальной библиотечной функции.
В системах 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 );
}
Эта функция должна делать то, что вы хотите:
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 );
Надеюсь, это поможет.
fgets( buffer, sizeof(buffer), file );
не sizeof(buffer)-1
. fgets
оставляет место для завершающего нуля.
while (!feof(file))
это всегда неправильно, и это еще один пример ошибочного использования.