【问题标题】:file size and buffer overshoot文件大小和缓冲区过冲
【发布时间】:2018-05-11 10:38:56
【问题描述】:

我有一个从 SD 卡打开文件的函数,使用文件大小来设置缓冲区的大小,将信息块写入该缓冲区,然后使用该信息执行某些操作,如以下代码所示:

char filename = "filename.txt";
uint16_t duration;
uint16_t pixel;
int q = 0;
int w = 0;
bool largefile;
File f;
int readuntil;
long large_buffer;

f = SD.open(filename);

if(f.size() > 3072) {
    w = 3072;
} else {
    w = f.size();
}
uint8_t buffer[w];

while(f.available()) {
    f.read(buffer, sizeof(buffer));

    while(q < sizeof(buffer)) {
        doStuffWithInformation(buffer[q++]);
    }
    q=0;
}
f.close;

这适用于较小的文件大小,但任何超过 3072 的硬限制缓冲区大小(我根据经验得出,它只是可以安全地提交给此函数的内存量)都会遇到问题。较大的文件可以正常读取,直到它们到达while(f.available()) 的最后一个循环,在那里它们读取文件的末尾,但随后继续读取缓冲区,缓冲区的尾部填充了最后一个循环的数据,没有被覆盖通过最新的f.read()。如何确保 while(f.available()) 函数的最后一个循环仅适用于当前循环期间写入缓冲区的信息?我现在唯一的想法是解决文件大小的因素,并将缓冲区大小设置为小于 3072 的最大因素,但这似乎每次调用此函数时都需要运行。有没有一个优雅的解决方案让我眼前一亮?

【问题讨论】:

  • uint8_t buffer[w]; -- 这不是有效的 C++。 C++ 中的数组的大小由常量表达式表示,而不是运行时变量或运行时计算表达式。如果您想要 C++ 中的标准动态数组声明,请使用 std::vector&lt;uint8_t&gt; buffer;
  • Paul,这方面的代码工作得很好。也许这与处于 arduino 环境中有关。我真的不知道,因为 arduino 是我唯一的 C++ 经验。
  • @PaulMcKenzie OP 不能使用 vector,它是 arduino。 Arduino不支持很多STL(因为不支持动态分配)。在 OP 的情况下,VLA 是一个正确的解决方案。
  • available() 告诉您可以阅读多少,因此使用它来跟踪缓冲区的哪些部分是有效的。我认为File.read() 会返回实际读取的字节数,但文档似乎没有区分单字节调用和填充缓冲区的调用。
  • @RetiredNinja 我只花了 25 分钟梳理了一遍,事实上,它确实返回了读取的字节数 - 请参阅我的答案。文档是错误的,很可能(根据为 Arduino 打开的票证)稍后添加了缓冲版本的读取,但没有人注意正确更新文档 - 新功能只是添加到描述中。我没有留下深刻的印象:D

标签: c++ arduino arduino-esp8266


【解决方案1】:

您的程序运行不正确,因为不能保证 f.read() 读取整个缓冲区。此外,当您读取文件的最后一个块时肯定会发生这种情况,除非文件大小是缓冲区大小的一个因素(在您的情况下为 3072)。

虽然 Arduino 规范 (https://www.arduino.cc/en/Reference/FileRead) 没有这样说,但SD.read 函数返回读取的字节数。在此处查看库的代码:https://github.com/arduino-libraries/SD/blob/master/src/utility/SdFile.cppint16_t SdFile::read(void* buf, uint16_t nbyte)

知道了这一点,您应该如下更改循环(同时将其重写为 for 循环以提高可读性并删除上面的 q 定义):

while(f.available()) {
    uint16_t sz = f.read(buffer, sizeof(buffer));

    for (uint16_t q = 0; q < sz; ++q) {
        doStuffWithInformation(buffer[q]);
    }
}

附带说明一下,现在,当您具备此逻辑时,您可以取消可变长度数组并使用大小为 512 的固定缓冲区 - SD 卡上的标准扇区大小。最有可能的是,它在读取方面会产生相同的性能,而在sizeof 方面的性能会稍好一些,这将成为编译时常数而不是运行时计算。这也使您的程序更简单。这使得以下代码:

f = SD.open(filename);
...
uint8_t buffer[512];

【讨论】:

  • 谢谢谢尔盖!我会尽快试试这个。
  • @user3596565 最近是一些随机投票的受害者,如果它有效,我将不胜感激接受答案:)
猜你喜欢
  • 2020-09-05
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
  • 2013-06-14
  • 2010-12-05
  • 1970-01-01
  • 2022-11-11
相关资源
最近更新 更多