c - 使用 C 程序(fopen、fread、fwrite)扫描 USB 驱动器

标签 c usb fopen eof fread

开始编写一个简单的 C 程序来扫描 USB 磁盘上的图像文件(BMP、Jpeg 等)。 我完成了将包含图像元数据的头文件。

我的问题是关于扫描 USB 驱动器的。程序如何知道它何时到达文件末尾。我把 USB 驱动器当作一个文件。我计划使用 fread 读取原始数据字节。

FILE usb_ptr = fopen(argv[1],"r");
if(usb_ptr == NULL){
    printf("error opening USB Drive for reading");
    fclose(usb_ptr);
}
  //I manually give the mount location, on fedora usb drives are mounted at
  //         /run/media/user1/USBDRIVE by default

struct header1 header1;
struct header2 header2;
struct colours colours;
int file_count=0;

fread(&header1,sizeof(header1),1,usb_ptr);
fread(&header2,sizeof(header2),1,usb_ptr);

复制U盘前几个字节后,检查是否找到BMP 文件,如果它在接下来的几个字节中不是 BMP 扫描等等。

 if (header1.signature != 0x4d42 || header1.data_offset != 54 ){
      int file_size = header1.file_size;
      file_count++;
  //there are more checks trying to keep this post short

1) 我计划重复此过程,直到到达文件末尾。但是如何确定 usb_ptr 何时结束(我完成了 USB 扫描)?

2) 我很确定 U 盘内存中会有“EOF”字符,我如何确定我已经到达磁盘末尾或只是读取了 U 盘上的一些随机字节?

3) 我应该换一种方式吗?

(上面的代码并不完整,只是一些片段,还有另一部分我将在 USB 磁盘上找到的图像复制到我的硬盘上这个程序几乎可以从驱动器中恢复图像希望以后添加更多文件类型)

谢谢。

最佳答案

我的评论,总结:

  1. fread 将执行它在"file"(在本例中为磁盘)末尾始终执行的操作,并返回读取的字节数,即最有可能为“0” (如果您按 512 字节读取)。

  2. EOF 不是您应该寻找的“字节”值,而是表示一个状态。使用 feof 显式测试,或者只检查 fread 的返回值。

  3. 目前您正在检查每个字节。但是数据不是以任何随机顺序存储的! U 盘按扇区存储数据,每个扇区长 512 字节:“扇区长 512 字节,用于与硬盘驱动器兼容”( wikipedia on USB flash drive )。

  4. 由于fragmentation,您不能假定连续扇区属于同一文件.如果一个文件是零散的,就没有自动的方法来以正确的顺序自动合并扇区……(手动操作通常是不可能的。只有当原始文件包含易于识别的数据时,我才会考虑这样做作为纯文本,并且内容极其重要:) .)

您可以读取一个扇区——512 字节——并在遇到 EOF 时停止。如果这个扇区以 BMP 的两个签名字节开头,您可以进一步检查它以验证它 是 BMP header ,如果是,您可以使用 BMP 结构数据来检查是否所有 next 扇区包含一个有效的 BMP 文件。这样做的唯一方法是:

  • 第一个扇区包含所有相关的 BMP 指标:data size 表示原始像素大小,您应该读取那么多的额外数据。
  • 使用BMP file specifications ,检查是否:
    • 宽度乘以高度乘以每个像素的字节数等于总大小
    • 数据不包含超出范围的值(但对于 24 位图像是不可能的)
    • 数据与每条扫描线的 DWORD 对齐

如果您接受 BMP 为“可能正确”,您可以将其保存到磁盘并通过肉眼验证它是否正确。然后:

  • 您 100% 确定此文件格式正确;或
  • 由于碎片化,另一个图像可能会从“内部”这个数据部分开始。

如果它不是格式正确的 BMP 图像,或者您想要彻底检查每个扇区,请继续扫描下一个扇区。如果你确定整个图像格式正确或者你想加快扫描速度,你可以跳过 (datasize+sectorsize-1)/sectorsize 扇区。

下面的简单 C 程序会扫描整个磁盘,如果它似乎指示 BMP 文件开始,它会以人类可读的形式打印出前 32 个字节。对于我的测试磁盘,它给出了以下输出:

42 4D D8 49 EE 0E E8 B9 7A BE F3 7C DF FD 7E F7 77 9F 7B FF 38 7F F0 3C 24 33 B3 66 AD 77 BD 6B | BM.I....z..|..~.w.{.8..<$3.f.w.k
42 4D 6E E6 E3 D3 48 37 A5 27 D7 6F EF 49 4E 13 E0 A7 DF 78 47 8E 5E 3C 95 B5 0A 16 D2 5C CE 3A | BMn...H7.'.o.IN....xG.^<.....\.:
42 4D 36 00 24 00 00 00 00 00 36 00 00 00 28 00 00 00 00 04 00 00 00 03 00 00 01 00 18 00 00 00 | BM6.$.....6...(.................
42 4D 49 2C 20 62 6F 64 79 20 6D 61 73 73 20 69 6E 64 65 78 3B 20 41 53 41 2C 20 41 6D 65 72 69 | BMI, body mass index; ASA, Ameri
42 4D 50 66 6F 67 6C 65 00 00 00 00 00 00 29 1E 00 01 DC F8 BC 84 91 AE BC 84 91 AE 00 04 00 00 | BMPfogle......).................

奇怪的是,最初它包含没有 BMP 文件,所以我复制了一个来测试。现在怎么会有不止一个候选人? (实际上还有 9 个。)首先,存在“误报”——“BMI”是一个很好的例子——但其次:如果某处有一个已删除 BMP 文件磁盘和它的第一个扇区刚好没有被覆盖,它也会被列出!

简短粗略的示例代码:

#include <stdio.h>

int main (int argc, char **argv)
{
    FILE *usb_ptr;
    unsigned char buffer[512];
    int i, j;

    if (argc == 1)
    {
        printf ("wot no stick?\n");
        return -1;
    }
    usb_ptr = fopen(argv[1],"rb");
    if(usb_ptr == NULL)
    {
        printf("error opening USB Drive for reading");
    }

    i = 0;
    while (1)
    {
        if (fread (buffer, 512,1, usb_ptr) < 1)
            break;
        i++;
        if (!(i & 127))
            printf ("%d sectors read..\r", i);
        if (buffer[0] == 'B' && buffer[1] == 'M')
        {
            for (j=0; j<32; j++)
                printf ("%02X ", buffer[j]);
            printf ("| ");
            for (j=0; j<32; j++)
            {
                if (buffer[j] >= ' ' && buffer[j] <= '~')
                    printf ("%c", buffer[j]);
                else
                    printf (".");
            }
            printf ("\n");
        }
    }

    fclose (usb_ptr);

    return 0;
}

(事后思考)对于 1Gb 的磁盘来说它非常慢.. 一次读取更多扇区可能会更快。 (测试..)是的,读取循环内的 10 个扇区的速度更快。

关于c - 使用 C 程序(fopen、fread、fwrite)扫描 USB 驱动器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20826773/

相关文章:

c - 没有硬 float 的 ARM Docker 交叉编译

usb - 硬件复位后的 stm32 USB-CDC

c# - 如何在 C# 中查找 USB 设备?

C - 获取打开的处理程序列表 - 重复条目

c - 为什么 fopen ("any_path_name",'r' ) 不返回 NULL?

c - 用C读取带有空格的文件.txt

c - 从socket读取到buffer,再从buffer逐行读取实现http语言

C 语言中可以有一个没有名字的函数吗?

python - 使用 ctypes 将 2d numpy 数组传递给 c

windows - Kinect USB 3.0 经常重连