2010-10-26 22 views

Trả lời

65

Bạn nên không bao giờ sử dụng gets (hoặc scanf với kích thước chuỗi không bị chặn) vì thao tác này sẽ mở bạn đến tràn bộ đệm. Sử dụng các fgets với một xử lý stdin vì nó cho phép bạn giới hạn dữ liệu sẽ được đặt trong bộ đệm của bạn.

Dưới đây là một đoạn nhỏ tôi sử dụng cho đầu vào dòng từ người sử dụng:

#include <stdio.h> 
#include <string.h> 

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
static int getLine (char *prmpt, char *buff, size_t sz) { 
    int ch, extra; 

    // Get line with buffer overrun protection. 
    if (prmpt != NULL) { 
     printf ("%s", prmpt); 
     fflush (stdout); 
    } 
    if (fgets (buff, sz, stdin) == NULL) 
     return NO_INPUT; 

    // If it was too long, there'll be no newline. In that case, we flush 
    // to end of line so that excess doesn't affect the next call. 
    if (buff[strlen(buff)-1] != '\n') { 
     extra = 0; 
     while (((ch = getchar()) != '\n') && (ch != EOF)) 
      extra = 1; 
     return (extra == 1) ? TOO_LONG : OK; 
    } 

    // Otherwise remove newline and give string back to caller. 
    buff[strlen(buff)-1] = '\0'; 
    return OK; 
} 

này cho phép tôi để đặt kích thước tối đa, sẽ phát hiện nếu quá nhiều dữ liệu được nhập trên dòng, và sẽ tuôn phần còn lại của dòng cũng vì vậy nó không ảnh hưởng đến hoạt động đầu vào tiếp theo.

Bạn có thể thử nghiệm nó với một cái gì đó như:

// Test program for getLine(). 

int main (void) { 
    int rc; 
    char buff[10]; 

    rc = getLine ("Enter string> ", buff, sizeof(buff)); 
    if (rc == NO_INPUT) { 
     // Extra NL since my system doesn't output that on EOF. 
     printf ("\nNo input\n"); 
     return 1; 
    } 

    if (rc == TOO_LONG) { 
     printf ("Input too long [%s]\n", buff); 
     return 1; 
    } 

    printf ("OK [%s]\n", buff); 

    return 0; 
} 
+1

không phải thư viện hệ thống thực hiện quét ngăn chặn tràn lệnh (Tôi hiểu rằng trong chương trình nếu nhà phát triển không kiểm tra đầu vào có thể là tràn, nhưng thư viện hệ thống an toàn không?). – Marm0t

+8

Không, nếu bạn 'scanf ("% s ")' vào bộ đệm 20 byte và người dùng nhập một dòng 40 byte, bạn sẽ được đặt. Toàn bộ điểm của 'scanf' được quét định dạng và có ít hơn _unformatted_ hơn đầu vào của người dùng :-) – paxdiablo

+5

@ Marm0t - Hãy suy nghĩ theo cách này bằng cách xem xét câu hỏi sau: làm thế nào có thể thực hiện ngăn chặn tràn nếu tất cả nó được một con trỏ tới một lát bộ nhớ (được định kiểu là char *) 'mà không có tham số nào cho biết việc thực thi về kích thước của bộ đệm đích'? –

5

Trên một hệ thống POSIX, có lẽ bạn nên sử dụng getline nếu nó có sẵn.

Bạn cũng có thể sử dụng miền công cộng của Chuck Falconer ggets chức năng cung cấp cú pháp gần hơn với gets nhưng không có sự cố. (Trang web Chuck Falconer là không còn nữa, mặc dù archive.org has a copy, và tôi đã thực hiện my own page for ggets.)

+0

Với báo trước rằng nó không hoạt động như mong đợi với các tệp chỉ kết thúc bằng CR. Đó không phải là không phổ biến như bạn có thể tưởng tượng (ông nói, sau khi gặp phải một thư mục đầy đủ của họ). Đó là một cơ hội bị bỏ lỡ mà họ đã không cho phép getdelim/getline để có một danh sách các delimiters thay vì một int duy nhất. –

+0

@MauryMarkowitz Nó sẽ hoạt động trên một hệ thống sử dụng CR làm định dạng kết thúc dòng gốc của nó. Luồng chế độ văn bản sẽ chuyển đổi bất kỳ loại kết thúc dòng gốc thành '\ n'. – jamesdlin

15

Tôi nghĩ rằng cách tốt nhất và an toàn nhất để đọc chuỗi nhập vào bởi người dùng đang sử dụng getline()

Dưới đây là một ví dụ làm thế nào để làm điều này:

#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char *argv[]) 
{ 
    char *buffer = NULL; 
    int read; 
    unsigned int len; 
    read = getline(&buffer, &len, stdin); 
    if (-1 != read) 
     puts(buffer); 
    else 
     printf("No line read...\n"); 

    printf("Size read: %d\n Len: %d\n", read, len); 
    free(buffer); 
    return 0; 
} 
+1

read = getline (& đệm, & len, stdin); cho GCC cảnh báo ví dụ: gcc -Wall -c "getlineEx2.c" getlineEx2.c: Trong hàm main: getlineEx2.c: 32: 5: warning: truyền đối số 2 của getline từ kiểu con trỏ không tương thích [được bật theo mặc định] read = getline (& buffer, & len, stdin); ^ Trong tệp bao gồm từ /usr/include/stdio.h:29:0, từ getlineEx2.c: 24: /usr/include/sys/stdio.h:37:9: lưu ý: expected size_t * nhưng đối số là loại không dấu int * ssize_t _EXFUN (đường thẳng, (char **, size_t *, FILE *)); ^ Biên dịch hoàn tất thành công. – rpd

+1

Chỉ cần cập nhật mà getline bây giờ đòi hỏi len được size_t hoặc unsigned dài – Wes

+0

Downside: POSIX nhưng không ANSI C. –

1

Bạn có thể sử dụng chức năng scanf để đọc chuỗi

scanf("%[^\n]",name); 

tôi không biết về các lựa chọn khác tốt hơn để nhận chuỗi,

2

Tôi tìm thấy một giải pháp dễ dàng và thoải mái:

char*string_acquire(char*s,int size,FILE*stream){ 
    int i; 
    fgets(s,size,stream); 
    i=strlen(s)-1; 
    if(s[i]!='\n') while(getchar()!='\n'); 
    if(s[i]=='\n') s[i]='\0'; 
    return s; 
} 

nó dựa trên fgets nhưng miễn phí từ các nhân vật phụ (để thay thế fflush (stdin) '\ n' và stdin rằng không hoạt động trên tất cả các hệ điều hành, hữu ích nếu bạn có để có được chuỗi sau này).

0

Trên các 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); 

Giống như vậy:

size_t line_len; 
const char *line = fgetln(stdin, &line_len); 

Các line không kết thúc vô và chứa \n (hoặc bất kỳ nền tảng của bạn đang sử dụng) trong kết thúc. Nó sẽ trở thành không hợp lệ sau khi hoạt động I/O tiếp theo trên luồng. Bạn được phép sửa đổi bộ đệm line trả lại.

Các vấn đề liên quan