Відповіді:
Вам потрібно динамічне управління пам’яттю та використовувати 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?", Я припускаю, що людині потрібно щось просте, як, наприклад, get (), а не 100 рядків коду як вище. Насправді, я думаю, якщо ви спробуєте насправді написати ці 100 рядків коду, ви зробили б набагато більше помилок, ніж ви зробили б, якби вибрали;)
gets
більше не існує, тому це не працює в C11.
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
з довільним лінійним термінатором.
Він подвоює виділену пам'ять, коли потрібно збільшити, і виглядає безпечним для потоків.
Це вимагає деякого розширення макросу, але ви навряд чи зробите набагато краще.
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 як безпечний спосіб отримати рядок у вигляді рядка з нульовим завершенням:
#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", можливо, більше даних або про наступний запит буде повідомлено про закінчення файлу. Вам доведеться зробити ще один фетт, щоб визначити, що це таке. (У зв'язку з цим циклічне використання за допомогою getchar () простіше.)
У наведеному вище (оновленому) прикладі коду, якщо рядок [sizeof (рядок) -1] == '\ 0' після успішних фетів, ви знаєте, що буфер був заповнений повністю. Якщо цю позицію виконує "\ 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))
це завжди неправильно, і це лише ще один приклад помилкового використання.