C语言 fread()和read()获取不同的数据

jhkqcmku  于 2023-05-16  发布在  其他
关注(0)|答案(1)|浏览(106)

我有一个程序来检测键盘。
首先,我使用read()从键盘设备读取struct input_event,它工作得很好。
然后我尝试用fread()替换read()。数据错误。
demo在下面。要复制这个bug,您需要将/dev/input/event0与您的键盘开发人员交换,并使用sudo运行它。

#include <unistd.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <linux/input.h>
#include <stdlib.h>

#define USE_FREAD

static void check_keyboard (int fd)
{
    static struct input_event event;
    
#ifdef USE_READ
    {
        //use read(). it works well.
        if (read (fd, &event, sizeof (event)) == -1)
            perror ("read()"), exit (EXIT_FAILURE);
    }
    
#else
#ifdef USE_FREAD
    {
        //use fread(). it gets wrong data
        int cloned = dup (fd);
        if (cloned == -1)
            perror ("dup()"), exit (EXIT_FAILURE);
    
        FILE *file = fdopen (cloned, "r");
        if (file == NULL)
            perror ("fdopen()"), exit (EXIT_FAILURE);
    
        if (fread (&event, sizeof (event), 1, file) != 1) {
            if (feof (file))
                fprintf (stderr, "fread() got EOF"), exit (EXIT_FAILURE);
            else if (ferror (file))
                fprintf (stderr, "fread() failed\n"), exit (EXIT_FAILURE);
        }
    
        if (fclose (file) != 0)
            perror ("fclose()"), exit (EXIT_FAILURE);
    
        puts ("fread() over");
    }
#endif
#endif
    
    if (event.type != EV_KEY)
        return;
    switch (event.value) {
        case 0:
            puts ("key released");
            break;
        case 1:
            printf ("key pressed, code:%d\n", event.code);
            break;
        case 2:
            puts ("repeat automatically");
            break;
    }
}

int main()
{
    int fd = open ("/dev/input/event0", O_RDONLY);
    if (fd == -1)
        perror ("open()"), exit (EXIT_FAILURE);
        
    for (; ;) {
        check_keyboard (fd);
    }
    
    if (close (fd) != 0)
        perror ("close()"), exit (EXIT_FAILURE);
}

如果定义了USE_READ,则可以检测键盘事件。并且它只在定义了USE_FREAD的情况下打印“fread()over”。
我尝试使用gdb来查看event的值。但还有许多其他事件混淆了数据。
我的环境:debian 12,gcc 12.2.0,kernel 6.1.0-7-amd64 .

vuktfyat

vuktfyat1#

我已经根据评论中的建议解决了。
只需要使用setvbuf()禁用缓冲区:

#ifdef USE_FREAD
    {
        //use fread(). it gets right data now.
        int cloned = dup (fd);
        if (cloned == -1)
            perror ("dup()"), exit (EXIT_FAILURE);
    
        FILE *file = fdopen (cloned, "r");
        if (setvbuf (file, NULL, _IONBF, 0) != 0)
            perror ("setvbuf()"), exit (EXIT_FAILURE);
    
        if (fread (&event, sizeof (event), 1, file) != 1) {
            if (feof (file))
                fprintf (stderr, "fread() got EOF"), exit(EXIT_FAILURE);
            else if (ferror (file))
                fprintf (stderr, "fread() failed\n"), exit (EXIT_FAILURE);
        }
    
        if (fclose (file) != 0)
            perror ("fclose()"), exit (EXIT_FAILURE);
    }
#endif

顺便说一下,请检查返回值。

相关问题