Cách đơn giản nhất để đọc toàn bộ dòng trong chương trình bảng điều khiển C Văn bản được nhập có thể có độ dài thay đổi và chúng tôi không thể đưa ra bất kỳ giả định nào về nội dung của nó.
Cách đơn giản nhất để đọc toàn bộ dòng trong chương trình bảng điều khiển C Văn bản được nhập có thể có độ dài thay đổi và chúng tôi không thể đưa ra bất kỳ giả định nào về nội dung của nó.
Câu trả lời:
Bạn cần quản lý bộ nhớ động và sử dụng fgets
chức năng này để đọc dòng của bạn. Tuy nhiên, dường như không có cách nào để xem nó đọc được bao nhiêu ký tự. Vì vậy, bạn sử dụng 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;
}
Lưu ý : Không bao giờ sử dụng được! Nó không kiểm tra giới hạn và có thể làm tràn bộ đệm của bạn
fgetc_unlocked
nếu an toàn luồng không phải là mối quan tâm mà là hiệu suất.
getline()
khác với getline()
chức năng tiêu chuẩn POSIX .
Một triển khai rất đơn giản nhưng không an toàn để đọc dòng cho phân bổ tĩnh:
char line[1024];
scanf("%[^\n]", line);
Một cách triển khai an toàn hơn, không có khả năng bị tràn bộ đệm, nhưng với khả năng không đọc được toàn bộ dòng, là:
char line[1024];
scanf("%1023[^\n]", line);
Không phải là 'chênh lệch một' giữa độ dài được chỉ định khai báo biến và độ dài được chỉ định trong chuỗi định dạng. Nó là một đồ tạo tác lịch sử.
gets
đã bị xóa khỏi tiêu chuẩn hoàn toàn
Vì vậy, nếu bạn đang tìm kiếm các đối số lệnh, hãy xem câu trả lời của Tim. Nếu bạn chỉ muốn đọc một dòng từ bảng điều khiển:
#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ó, nó không an toàn, bạn có thể chạy quá tải bộ đệm, nó không kiểm tra phần cuối của tệp, nó không hỗ trợ mã hóa và rất nhiều thứ khác. Trên thực tế, tôi thậm chí không nghĩ liệu nó có làm BẤT CỨ những thứ này hay không. Tôi đồng ý rằng tôi hơi khó chịu :) Nhưng ... khi tôi thấy một câu hỏi như "Làm thế nào để đọc một dòng từ bảng điều khiển trong C?", Tôi cho rằng một người cần một cái gì đó đơn giản, như get () chứ không phải 100 dòng mã như trên. Trên thực tế, tôi nghĩ, nếu bạn cố gắng viết 100 dòng mã đó trong thực tế, bạn sẽ mắc nhiều sai lầm hơn những gì bạn đã làm nếu bạn đã chọn;)
gets
không tồn tại nữa, do đó điều này không hoạt động trong C11.
getline
ví dụ có thể chạy được
Đã đề cập đến câu trả lời này nhưng đây là một ví dụ.
Nó là POSIX 7 , cấp phát bộ nhớ cho chúng tôi và sử dụng lại bộ đệm được cấp phát trên một vòng lặp một cách độc đáo.
Con trỏ newbs, hãy đọc phần này: Tại sao đối số đầu tiên của getline lại là con trỏ trỏ tới con trỏ "char **" thay vì "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;
}
triển khai glibc
Không có POSIX? Có thể bạn muốn xem cách triển khai glibc 2.23 .
Nó giải quyết thành getdelim
, đó là một tập hợp đơn giản của POSIX getline
với một dấu chấm hết dòng tùy ý.
Nó tăng gấp đôi bộ nhớ được cấp phát bất cứ khi nào cần tăng và trông an toàn cho luồng.
Nó yêu cầu một số mở rộng macro, nhưng bạn không chắc sẽ làm tốt hơn nhiều.
len
ở đây, khi đọc cung cấp độ dài quá
man getline
. len
là độ dài của bộ đệm hiện có, 0
là phép thuật và yêu cầu nó cấp phát. Đọc là số ký tự được đọc. Kích thước bộ đệm có thể lớn hơn read
.
Nhiều người, giống như tôi, đến bài đăng này với tiêu đề phù hợp với những gì được tìm kiếm, mặc dù phần mô tả nói về độ dài thay đổi. Đối với hầu hết các trường hợp, chúng tôi biết trước độ dài.
Nếu bạn biết độ dài trước tay, hãy thử dưới đây:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
nguồn: https://www.tutorialspoint.com/c_standard_library/c_ Chức năng_fgets.htm
Như được đề xuất, bạn có thể sử dụng getchar () để đọc từ bảng điều khiển cho đến khi trả về cuối dòng hoặc EOF, xây dựng bộ đệm của riêng bạn. Tăng bộ đệm động có thể xảy ra nếu bạn không thể đặt kích thước dòng tối đa hợp lý.
Bạn cũng có thể sử dụng fgets như một cách an toàn để lấy một dòng dưới dạng chuỗi C null kết thú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);
Nếu bạn đã sử dụng hết đầu vào bảng điều khiển hoặc nếu thao tác không thành công vì lý do nào đó, thì eof == NULL sẽ được trả về và bộ đệm dòng có thể không thay đổi (đó là lý do tại sao việc đặt ký tự đầu tiên thành '\ 0' là hữu ích).
fgets sẽ không điền quá dòng [] và nó sẽ đảm bảo rằng có một giá trị rỗng sau ký tự được chấp nhận cuối cùng khi trả về thành công.
Nếu đến cuối dòng, ký tự đứng trước '\ 0' kết thúc sẽ là '\ n'.
Nếu không có dấu chấm hết '\ n' trước phần cuối '\ 0' thì có thể có nhiều dữ liệu hơn hoặc yêu cầu tiếp theo sẽ báo cáo phần cuối của tệp. Bạn sẽ phải thực hiện một công cụ khác để xác định cái nào là cái nào. (Về vấn đề này, lặp với getchar () dễ dàng hơn.)
Trong mã ví dụ (đã cập nhật) ở trên, nếu dòng [sizeof (line) -1] == '\ 0' sau khi tạo thành công, bạn biết rằng bộ đệm đã được lấp đầy hoàn toàn. Nếu vị trí đó được tiến hành bởi '\ n', bạn biết rằng mình đã may mắn. Nếu không, có thêm dữ liệu hoặc phần cuối của tệp ở phía trước trong stdin. (Khi bộ đệm không được lấp đầy hoàn toàn, bạn vẫn có thể ở cuối tệp và cũng có thể không có '\ n' ở cuối dòng hiện tại. Vì bạn phải quét chuỗi để tìm và / hoặc loại bỏ bất kỳ '\ n' nào trước khi kết thúc chuỗi ('\ 0' đầu tiên trong bộ đệm), tôi có xu hướng thích sử dụng getchar () ngay từ đầu.)
Làm những gì bạn cần làm để đối phó với việc vẫn còn nhiều dòng hơn số lượng bạn đọc dưới dạng đoạn đầu tiên. Các ví dụ về tăng trưởng động một bộ đệm có thể được thực hiện để hoạt động với getchar hoặc fgets. Có một số trường hợp cạnh phức tạp cần chú ý (như nhớ có đầu vào tiếp theo bắt đầu lưu trữ tại vị trí của '\ 0' đã kết thúc đầu vào trước đó trước khi bộ đệm được mở rộng).
Làm thế nào để đọc một dòng từ bảng điều khiển trong C?
Xây dựng chức năng của riêng bạn, là một trong những cách sẽ giúp bạn đọc được dòng từ bảng điều khiển
Tôi đang sử dụng phân bổ bộ nhớ động để cấp phát lượng bộ nhớ cần thiết
Khi sắp hết bộ nhớ được cấp phát, chúng tôi cố gắng tăng gấp đôi dung lượng bộ nhớ
Và ở đây tôi đang sử dụng một vòng lặp để quét từng ký tự của chuỗi một bằng cách sử dụng getchar()
hàm cho đến khi người dùng nhập '\n'
hoặc EOF
ký tự
cuối cùng chúng tôi xóa bất kỳ bộ nhớ được cấp bổ sung nào trước khi trả về dòng
//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;
}
Bây giờ bạn có thể đọc toàn bộ dòng theo cách này:
char *line = NULL;
line = scan_line(line);
Đây là một chương trình ví dụ sử dụng scan_line()
hàm:
#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
}
đầu vào mẫu:
Twinkle Twinkle little star.... in the sky!
đầu ra mẫu:
Twinkle Twinkle little star.... in the sky!
Tôi đã gặp vấn đề tương tự cách đây một thời gian, đây là cách giải quyết của tôi, hy vọng nó sẽ hữu ích.
/*
* 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
để xử lý trường hợp lỗi. Tuy nhiên, bạn có nghĩ rằng bạn có thể sử dụng lại tmp_buf
, thay vì nhập malloc
nó với cùng một kích thước lặp đi lặp lại trong vòng lặp không?
has_err
để báo cáo lỗi làm cho chuỗi hàm này không an toàn và kém thoải mái khi sử dụng. Đừng làm theo cách đó. Bạn đã chỉ ra lỗi bằng cách trả về NULL. Cũng có thể nghĩ rằng các thông báo lỗi đã in không phải là một ý kiến hay trong một chức năng thư viện có mục đích chung.
Trên hệ thống BSD và Android, bạn cũng có thể sử dụng fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Như vậy:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
Cuối cùng line
không phải là null chấm dứt và chứa \n
(hoặc bất kỳ thứ gì nền tảng của bạn đang sử dụng). Nó trở nên không hợp lệ sau thao tác I / O tiếp theo trên luồng.
Một cái gì đó như thế này:
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;
}
Cách tốt nhất và đơn giản nhất để đọc một dòng từ bảng điều khiển là sử dụng hàm getchar (), theo đó bạn sẽ lưu trữ một ký tự tại một thời điểm trong một mảng.
{
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 );
}
Hàm này sẽ làm những gì bạn muốn:
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 );
Tôi hi vọng cái này giúp được.
fgets( buffer, sizeof(buffer), file );
không sizeof(buffer)-1
. fgets
để lại khoảng trống cho giá trị kết thúc null.
while (!feof(file))
luôn luôn sai và đây chỉ là một ví dụ khác về việc sử dụng sai.