2012-06-21 40 views
5

Tôi đã gặp vấn đề này khi tôi muốn kiểm tra những gì tôi nhập là số. Hàm scanf sẽ trả về 1 nếu tôi nhập thành công một số. Vì vậy, đây là những gì tôi đã viết:điều gì sẽ xảy ra khi bạn nhập những thứ như 12ab vào scanf ("% d", & argu)?

int argu; 
while(scanf("%d",&argu)!=1){ 
    printf("Please input a number!\n"); 
} 

Nhưng khi tôi nhập những thứ như abcd vào nó, vòng lặp sẽ đi mãi mãi và không dừng lại cho lời nhắc.

Tôi đã tìm kiếm trực tuyến và thấy rằng nó có liên quan đến bộ nhớ cache và tôi cần dọn dẹp bộ nhớ cache để scanf có thể nhận dữ liệu mới. Vì vậy, tôi đã thử fflush nhưng nó không hoạt động.

Sau đó, tôi thấy điều này:

int argu,j; 
while(scanf("%d",&argu)!=1){ 
    printf("Please input a number!\n"); 
    while((j=getchar())!='\n' && j != '\n'); 
} 

Sau đó, khi những điều tôi đầu vào như 'abcd' nó làm việc tốt và nó nhắc nhở cho đầu vào của tôi. Nhưng khi tôi nhập những thứ như '12ab', nó sẽ không hoạt động trở lại.

Vì vậy, có cách nào tôi có thể kiểm tra đầu vào cho scanf ("% d", & argu) thực sự là một số và nhắc cho đầu vào khác nếu không?

EDIT:

Tôi thấy câu trả lời và giải quyết vấn đề của tôi bằng cách sử dụng while(*eptr != '\n').

Lưu ý rằng chức năng fgets thực sự đọc '\ n' đối với mảng và fget thì không. Vì vậy hãy cẩn thận.

Trả lời

4

Tốt hơn là đọc toàn bộ dòng, sử dụng fgets() và sau đó kiểm tra nó, thay vì cố gắng phân tích cú pháp "đang di chuyển" từ luồng đầu vào.

Dễ dàng bỏ qua đầu vào không hợp lệ theo cách đó.

Sử dụng fgets() và sau đó chỉ strtol() để chuyển đổi thành một số, nó sẽ giúp bạn dễ dàng xem liệu có dữ liệu sau dấu sau hay không.

Ví dụ:

char line[128]; 

while(fgets(line, sizeof line, stdin) != NULL) 
{ 
    char *eptr = NULL; 
    long v = strtol(line, &eptr, 10); 
    if(eptr == NULL || !isspace(*eptr)) 
    { 
    printf("Invalid input: %s", line); 
    continue; 
    } 
    /* Put desired processing code here. */ 
} 
+0

sử dụng mã như fgets (s, 10, stdin)? – Gnijuohz

+0

Cảm ơn! Tôi không biết strtol. Đã học hỏi được rất nhiều! – Gnijuohz

+0

xin lỗi, bạn có thể giải thích 'if (eptr == NULL ||! Isspace (* eptr))'? khi sử dụng mã mà bạn cung cấp, các đầu vào như '12 34 'được chấp nhận nhưng chúng không được. khi tôi nhập '1234' thì eptr sẽ trỏ đến đâu và làm thế nào về '12ab' và '12 34 '? – Gnijuohz

1

tôi sẽ gợi ý để có được đầu vào như là một chuỗi và kiểm tra các ký tự không phải số trong đó. Nếu đầu vào là chuỗi chuyển đổi hợp lệ thành int theo sscanf(str,"%d",&i); hoặc lỗi khác về ngoại giao.

1

Chỉ cần gọi scanf ("% * [^ \ n] \ n") bên trong vòng lặp và nó sẽ hủy "bộ nhớ cache".

+0

Tôi đã thử điều đó. Nó không hoạt động. – Gnijuohz

+0

Đó là do hành vi của 'fflush' không được xác định cho luồng đầu vào. Xem [tiêu chuẩn ngôn ngữ C] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf), mục 7.19.5.2, mục 2. –

+0

Cả hai đều đúng. Hy vọng chỉnh sửa, mặc dù, bây giờ là chính xác (dựa trên tiêu chuẩn). – Spidey

2

Nhưng khi tôi nhập những thứ như abcd vào nó, vòng lặp sẽ đi mãi mãi và không dừng cho lời nhắc.

Đó là bởi vì nếu scanf gặp phải một ký tự không khớp với trình biến đổi chuyển đổi, nó sẽ để nó trong luồng đầu vào. Về cơ bản, những gì đang xảy ra là scanf đọc ký tự a từ luồng đầu vào, xác định rằng đó không phải là đối sánh hợp lệ cho thông số chuyển đổi %d và sau đó đẩy nó trở lại luồng đầu vào. Thời gian tiếp theo thông qua vòng lặp nó làm điều tương tự. Và một lần nữa. Và một lần nữa. Và một lần nữa.

fflush không phải là giải pháp tốt vì không được xác định để hoạt động trên luồng đầu vào.

Đối với đầu vào "12ab", scanf sẽ đọc và chuyển đổi "12", để lại "ab" trong luồng đầu vào.

Giải pháp tốt nhất là đọc tất cả các đầu vào của bạn dưới dạng văn bản, sau đó chuyển đổi thành kiểu số sử dụng strtol (cho giá trị tích phân) và strtod (cho giá trị thực). Ví dụ:

char input[SIZE]; // assume SIZE is big enough for whatever input we get 
int value; 

if (fgets(input, sizeof input, stdin) != NULL) 
{ 
    char *chk; 
    int tmp = (int) strtol(input, &chk, 10); 
    if (isspace(*chk) || *chk == 0) 
    value = tmp; 
    else 
    printf("%s is not a valid integer string\n", input); 
} 

trỏ đến ký tự đầu tiên trong luồng đầu vào không phải là chữ số thập phân. Nếu ký tự này không phải là khoảng trắng hoặc 0 terminator, thì chuỗi đầu vào không phải là một số nguyên hợp lệ. Điều này sẽ phát hiện và từ chối các đầu vào như "12ab" cũng như "abcd".

scanf là một giải pháp tốt nếu bạn biết đầu vào của mình luôn được định dạng và hoạt động tốt. Nếu có khả năng đầu vào của bạn không phải là hoạt động tốt, hãy sử dụng fgets và chuyển đổi nếu cần.

0

Gọi scanf("%*[^\n]\n") bên trong vòng lặp. Điều này là đủ để loại bỏ mọi thứ liên quan đến bộ nhớ cache.

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