EOF là gì và làm thế nào để kích hoạt nó? [đóng cửa]


12

Đây là mã nguồn C của tôi.

Khi tôi xây dựng nó trong Ubuntu, nó bắt đầu có các ký tự nhưng tôi không biết làm thế nào để kết thúc chương trình, vì nó không kết thúc bằng cách nhập ENTERhoặc trả lại xe ngựa.

EOF có nghĩa là gì? Làm thế nào tôi có thể kích hoạt nó?

Nguồn này cũng có trong một cuốn sách của Dennis Ritchie:

#include <stdio.h>
    /* count digits, white space, others */
main ()
{
  int c, i, nwhite, nother;
  int ndigit[10];
  nwhite = nother = 0;
  for (i = 0; i < 10; ++i)
    ndigit[i] = 0;
  while ((c = getchar ()) != EOF)
    if (c >= '0' && c <= '9')
      ++ndigit[c - '0'];
    else if (c == ' ' || c == '\n' || c == '\t')
      ++nwhite;
    else
      ++nother;
  printf ("digits =");
  for (i = 0; i < 10; ++i)
    printf (" %d", ndigit[i]);
  printf (", white space = %d, other = %d\n", nwhite, nother);
}

4
trong ngôn ngữ C -1tương đương với EOF. Nó được định nghĩa /usr/include/stdio.hlà một hằng số vĩ mô
Edward Torvalds


@edwardtorvalds nhập -1làm đầu vào không hoạt động :)
Sergiy Kolodyazhnyy

Tôi nghĩ rằng cuốn sách Dennis Ritchie tương tự giải thích điều này.
andy256

Cũng có liên quan: unix.stackexchange.com/questions/110240/ trên (Không có câu trả lời nào được đăng cho câu hỏi này là hoàn toàn chính xác.)
fkraiem

Câu trả lời:


22

Tl; dr

Nói chung, bạn có thể "kích hoạt EOF" trong một chương trình đang chạy trong một thiết bị đầu cuối với tổ hợp phím CTRL+ Dngay sau lần xả đầu vào cuối cùng.


EOF có nghĩa là gì? Làm thế nào tôi có thể kích hoạt nó?

EOF có nghĩa là End-Of-File.

"Kích hoạt EOF" trong trường hợp này có nghĩa là "làm cho chương trình biết rằng sẽ không có thêm đầu vào nào được gửi".

Trong trường hợp này, vì getchar()sẽ trả về số âm nếu không có ký tự nào được đọc, việc thực thi bị chấm dứt.

Nhưng điều này không chỉ áp dụng cho chương trình cụ thể của bạn, nó áp dụng cho nhiều công cụ khác nhau.

Nói chung, "kích hoạt EOF" có thể được thực hiện bằng tổ hợp phím CTRL+ Dngay sau lần xả đầu vào cuối cùng (nghĩa là bằng cách gửi một đầu vào trống).

Ví dụ với cat:

% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
% 

Điều xảy ra dưới mui xe khi nhấn CTRL+ Dlà đầu vào được nhập kể từ lần xả đầu vào cuối cùng bị xóa; khi điều này xảy ra là một đầu vào trống, tòa nhà read()được gọi là trả về STDIN của chương trình 0, getchar()trả về một số âm ( -1trong thư viện GNU C) và đến lượt nó được hiểu là EOF 1 .


1 - /programming//a/1516177/4316166


2
Quá trình biên dịch hoạt động, vì phân định dấu phẩy không bị ràng buộc bởi cùng một dòng. Ngoài ra, giải thích tuyệt vời về EOF :)
Paulius ukys

@ PauliusŠukys Huh, bạn nói đúng. C của tôi là một chút gỉ. :)
kos

1
iirc EOF không được xác định là -1 trên mỗi tiêu chuẩn. Đó chỉ là những gì nó xảy ra trong glibc chẳng hạn.
larkey


1
EOF không 'nhất quán trong việc gửi "đầu vào trống"' và câu trả lời SO mà bạn trích dẫn không nói khác. Đó là một tín hiệu ra khỏi ban nhạc. Trong trường hợp của một thiết bị đầu cuối, nó được gửi bằng cách gõ Ctrl / d.
dùng207421

4

TL; DR : EOF không phải là một ký tự, nó là một macro được sử dụng để đánh giá lợi nhuận âm của chức năng đọc đầu vào. Người ta có thể sử dụng Ctrl+ Dđể gửi EOTký tự sẽ buộc hàm trả về-1

Mọi lập trình viên đều phải RTFM

Hãy để chúng tôi tham khảo "Hướng dẫn tham khảo CA" của Harbison và Steele, tái bản lần thứ 4. từ năm 1995, trang 317:

Số nguyên âm EOF là một giá trị không phải là mã hóa của "ký tự thực". . . Ví dụ: fget (phần 15.6) trả về EOF khi ở cuối tệp, vì không có "ký tự thực" nào được đọc.

Về cơ bản EOFkhông phải là một ký tự, mà là một giá trị nguyên được triển khai stdio.hđể thể hiện -1. Do đó, câu trả lời của kos là chính xác cho đến nay, nhưng đó không phải là về việc nhận đầu vào "trống". Lưu ý quan trọng là ở đây EOF đóng vai trò so sánh giá trị trả về (của getchar()), không biểu thị một ký tự thực tế. Các man getcharhỗ trợ đó:

GIÁ TRỊ TRẢ LẠI

fgetc (), getc () và getchar () trả về ký tự được đọc dưới dạng char không dấu cho một int hoặc EOF ở cuối tệp hoặc lỗi.

được () và fgets () trả về s thành công và NULL bị lỗi hoặc khi kết thúc tệp xảy ra trong khi không có ký tự nào được đọc.

ungetc () trả về c khi thành công hoặc EOF bị lỗi.

Xem xét whilevòng lặp - mục đích chính của nó là lặp lại hành động nếu điều kiện trong ngoặc là đúng . Nhìn lại:

while ((c = getchar ()) != EOF)

Về cơ bản, nó nói rằng hãy tiếp tục làm công cụ nếu c = getchar()trả về mã thành công ( 0hoặc cao hơn; đó là một điều phổ biến, hãy thử chạy lệnh thành công, sau echo $?đó thất bại echo $?và xem số chúng trả về). Vì vậy, nếu chúng tôi nhận được ký tự và xác nhận thành công C, mã trạng thái được trả về là 0, không thành công là -1. EOFđược định nghĩa là -1. Do đó khi điều kiện -1 == -1xảy ra, các vòng lặp dừng lại. Và khi nào thì điều đó sẽ xảy ra? Khi không còn nhân vật để nhận, khi c = getchar()thất bại. Bạn có thể viết while ((c = getchar ()) != -1)và nó vẫn hoạt động

Ngoài ra, hãy quay trở lại mã thực tế, đây là một đoạn trích từ stdio.h

/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif

Mã ASCII và EOT

Mặc dù ký tự EOF không phải là ký tự thực tế, tuy nhiên, vẫn tồn tại EOTký tự (Kết thúc truyền), có giá trị thập phân ASCII là 04; nó được liên kết với Ctrl+ Dphím tắt (cũng được biểu thị dưới dạng ký tự meta ^D). Sự kết thúc của bộ truyền phát được sử dụng để biểu thị việc đóng một luồng dữ liệu trở lại khi máy tính được sử dụng để kiểm soát các kết nối điện thoại, do đó đặt tên là "kết thúc truyền".

Vì vậy, có thể gửi giá trị ascii đó cho chương trình như vậy, lưu ý $'\04'đó là EOT:

skolodya@ubuntu:$ ./a.out  <<< "a,b,c $'\04'"                                  
digits = 1 0 0 0 1 0 0 0 0 0, white space = 2, other = 9

Vì vậy, chúng ta có thể nói rằng nó tồn tại, nhưng nó không thể in được

Lưu ý bên

Chúng ta thường quên rằng trước đây các máy tính không linh hoạt - các nhà thiết kế phải sử dụng mọi phím bàn phím có sẵn. Do đó, việc gửi EOTký tự bằng CtrlD vẫn là "gửi một ký tự", không giống như gõ vốn A, ShiftA, bạn vẫn tạo cho máy tính một đầu vào với các phím khả dụng. Do đó, EOT là một nhân vật thực sự theo nghĩa là nó đến từ người dùng, nó có thể đọc được bằng máy tính (mặc dù không thể in được, không thể nhìn thấy bởi con người), nó tồn tại trong bộ nhớ máy tính

Nhận xét của Byte Commander

Nếu bạn cố đọc từ / dev / null, điều đó cũng sẽ trả về EOF, phải không? Hay tôi đến đó bằng gì?

Vâng, chính xác là đúng, bởi vì /dev/nullkhông có ký tự thực tế nào được đọc, do đó nó c = getchar()sẽ trả về -1mã và chương trình sẽ thoát ngay lập tức. Một lần nữa lệnh không trả về EOF. EOF chỉ là biến không đổi bằng -1, mà chúng ta sử dụng để so sánh mã trả về của hàm getchar . EOFkhông tồn tại như một nhân vật, nó chỉ là một giá trị tĩnh bên trong stdio.h.

Bản giới thiệu:

# cat /dev/null shows there's no readable chars
DIR:/xieerqi
skolodya@ubuntu:$ cat /dev/null | cat -A        

# Bellow is simple program that will open /dev/null for reading. Note the use of literal -1                                   
   DIR:/xieerqi
skolodya@ubuntu:$ cat readNull.c                                               
#include<stdio.h>

void main()
{
   char c;
    FILE *file;
    file = fopen("/dev/null", "r");

    if (file) 
    {
    printf ("Before while loop\n");
        while ((c = getc(file)) != -1)
            putchar(c);
    printf("After while loop\n"); 
    fclose(file);
    }
}

DIR:/xieerqi
skolodya@ubuntu:$ gcc readNull.c -o readNull                                   

DIR:/xieerqi
skolodya@ubuntu:$ ./readNull
Before while loop
After while loop

Một cái đinh khác trong quan tài

Đôi khi người ta cố chứng minh rằng EOF là một ký tự có mã như thế này:

#include <stdio.h>
int main(void)
{
    printf("%c", EOF);
    return 0;
}

Vấn đề với đó là kiểu dữ liệu char có thể là một giá trị được ký hoặc không dấu. Ngoài ra, chúng là kiểu dữ liệu địa chỉ nhỏ nhất khiến chúng rất hữu ích trong các bộ vi điều khiển, nơi bộ nhớ bị hạn chế. Vì vậy, thay vì khai báo int foo = 25;, người ta thường thấy trong các bộ vi điều khiển có bộ nhớ nhỏ char foo = 25;hoặc một cái gì đó tương tự. Ngoài ra, ký tự có thể được ký hoặc không dấu .

Người ta có thể xác minh rằng kích thước tính theo byte với một chương trình như thế này:

#include <stdio.h>
int main(void)
{
    printf("Size of int: %lu\n",sizeof(int));
    printf("Sieze of char: %lu\n",sizeof(char));
    //printf("%s", EOF);
    return 0;
}

skolodya@ubuntu:$ ./EOF                                                        
Size of int: 4
Sieze of char: 1

Chính xác thì điểm là gì? Điểm đáng chú ý là EOF được định nghĩa là -1, nhưng kiểu dữ liệu char có thể in các giá trị nguyên .

ĐỒNG Ý . . Nếu chúng ta thử in char dưới dạng chuỗi thì sao?

#include <stdio.h>
int main(void)
{
    printf("%s", EOF);
    return 0;
}

Rõ ràng là một lỗi, nhưng dù sao, lỗi sẽ cho chúng ta biết một điều thú vị:

skolodya @ ubfox: $ gcc EOF.c -o EOF
EOF.c: Trong chức năng 'main': EOF.c: 4: 5: cảnh báo: định dạng '% s' mong đợi đối số của loại 'char *', nhưng đối số 2 có gõ 'int' [-Wformat =] printf ("% s", EOF);

Giá trị hex

In EOF dưới dạng giá trị hex sẽ cho giá trị FFFFFFFF16 bit (8 byte), hai lời khen của a -1.

#include <stdio.h>
int main(void)
{
    printf("This is EOF: %X\n", EOF);
    printf("This is Z: %X\n",'Z');
    return 0;
}

Đầu ra:

DIR:/xieerqi
skolodya@ubuntu:$ ./EOF                                                        
This is EOF: FFFFFFFF
This is Z: 5A

Một điều tò mò khác xảy ra với đoạn mã sau:

#include <stdio.h>
int main(void)
{
   char c;
   if (c = getchar())
    printf ("%x",c);
    return 0;
}

Nếu một lần nhấn Shift+ A, chúng ta sẽ nhận được giá trị hex 41, rõ ràng giống như trong bảng ASCII. Nhưng đối với Ctrl+ D, chúng ta có ffffffff, một lần nữa - giá trị trả về của getchar()được lưu trữ trong c.

DIR:/xieerqi
skolodya@ubuntu:$ gcc  EOF.c -o ASDF.asdf                                      

DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
A
41
DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
ffffffff

Tham khảo các ngôn ngữ khác

Lưu ý rằng ngôn ngữ khác tránh sự nhầm lẫn này, bởi vì chúng hoạt động để đánh giá trạng thái thoát hàm, không so sánh nó với macro. Làm thế nào để một người đọc tệp trong Java?

    File inputFile  = new File (filename);
    Scanner readFile = new Scanner(inputFile);
    while (readFile.hasNext())
        { //more code bellow  }

Còn trăn thì sao?

with open("/etc/passwd") as file:
     for line in file:
          print line

Điểm tuyệt vời, thực sự là một nhân vật được gửi bằng cách nào đó tại một số điểm.
kos

Tôi nghĩ rằng nhân vật EOF là một thứ đã bị mất trong bản dịch, vì nó không phải là một nhân vật thực sự, nhưng EOT là một nhân vật ascii thực tế. Đi đi!
Sergiy Kolodyazhnyy

1
Nếu bạn cố đọc từ /dev/nullđó, điều đó cũng sẽ trả về EOF, phải không? Hay tôi đến đó bằng gì?
Chỉ huy Byte

@ByteCommander cho phép tìm hiểu. Làm mèo / dev / null | mèo -A.
Sergiy Kolodyazhnyy

@ByteCommander đã thêm phần giải quyết nhận xét của bạn
Sergiy Kolodyazhnyy

2

EOF là viết tắt của cuối tập tin . Mặc dù tôi không biết cách kích hoạt biểu tượng sau, bạn có thể chạy chương trình sau thông qua đường ống một tệp, sẽ gửi tín hiệu EOF ở cuối:

echo "Some sample text" | ./a.out

a.outnguồn tổng hợp của bạn ở đâu


1
Đã nêu lên điều này, tuy nhiên trên một ghi chú bên EOF không phải là một ký tự, tôi nghĩ rằng quan niệm sai lầm xuất phát từ thực tế được báo hiệu thông qua tổ hợp phím CTRL, thường là cách để nhập các ký tự không in được. Theo tôi hiểu, tất cả những gì thực sự xảy ra là tất cả các đầu vào bị xóa và là đầu vào bị xóa trống read()(syscall) sẽ trở lại 0, được hiểu là EOF: stackoverflow.com/a/1516177/4316166
kos

@kos, bạn nói đúng, đó là một tín hiệu.
Paulius ukys
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.