在流缓冲区中连续扫描字符串的最佳方法

时间:2013-01-28 15:31:46

标签: c callback stream buffer

我有这种情况,我的功能不断接收各种长度的数据。数据可以是任何东西。我想找到我在这个数据中寻找特定字符串的最佳方法。该解决方案将需要以某种方式缓冲以前的数据,但我无法解决问题。

以下是问题的一个示例:

DATA IN - > [\ X00 \ X00 \ X01 \ x23B] [] [LABLABLABLABLA \ x01TO] [KEN] [BLA \ X01] ...

如果每个[...]代表一个数据块而[]代表一个没有项目的数据块,那么扫描字符串TOKEN的最佳方法是什么?

更新 我意识到这个问题有点复杂。 []不是分隔符。我只是用它们来描述上面例子中的块的结构。此外,TOKEN不是静态字符串。它是可变长度的。我认为逐行读取的最佳方法是问题是如何将可变长度的流式缓冲区读入行中。

3 个答案:

答案 0 :(得分:2)

搜索TOKEN的最简单方法是:

  • 尝试从流
  • 中的位置0开始匹配“TOKEN”
  • 尝试从流
  • 中的位置1开始匹配“TOKEN”

所以你需要缓冲的是流中的一些字节,等于“TOKEN”的长度(5个字节,或者实际上是4个字节)。在每个位置尝试匹配“TOKEN”,这可能需要等到你的缓冲区中至少读取5个字节。如果匹配失败,请回退到您开始匹配的位置,再加上一个。因为你永远不会倒回超过你正在搜索的字符串的长度(减一),这就是你真正需要的所有缓冲区。

技术问题是,当您从流中连续读取时,如何维护5字节的缓冲数据。一种方法是所谓的“循环缓冲”。另一种方法,特别是如果令牌很小,是使用更大的缓冲区,每当你接近结束时,将你需要的字节复制到开头并重新开始。

如果你的函数是一个回调,为每个新数据块调用一次,那么你需要保持从一个调用到下一个调用的某种状态,以允许跨越两个块的匹配。如果你很幸运,那么你的回调API包含一个“用户数据指针”,你可以设置它指向你喜欢的任何包含缓冲区的结构。如果没有,您将需要全局或线程局部变量。

如果流具有高数据速率,那么您可能需要考虑使用KMP算法或其他方式加快速度。

答案 1 :(得分:0)

抱歉,我投票删除了我以前的答案,因为我对这个问题的理解不正确。我没有仔细阅读enouogh并认为[]是令牌分隔符。

对于您的问题,我建议基于一个简单的计数器构建一个小型状态机: 对于每个角色,您都会执行以下伪代码:

if (received_character == token[pos]) {
    ++pos;
    if (pos >= token_length) {
        token_received = 1;
    }
}
else {
    pos = 0; // Startover
}

这需要最少的处理器周期和最小的内存,因为除了刚收到的块之外你不需要缓冲任何内容。

答案 2 :(得分:-1)

如果针头包含在内存中,可以假设您可以分配同等大小的对象进行读取(例如char input_array[needle_size];)。

要开始搜索过程,请使用文件中的字节填充该对象(例如size_t sz = fread(input_array, 1, input_size, input_file);)并尝试匹配(例如if (sz == needle_size && memcmp(input_array, needle, needle_size) == 0) { /* matched */ }

如果匹配失败或您希望在成功匹配后继续搜索,请将位置向前推进一个字节(例如memmove(input_array, input_array + 1, input_size - 1); input_array[input_size - 1] = fgetc(input_file);,然后重试。

有人担心这个想法会在评论中复制太多字节。虽然我不认为这种关注具有重要的价值(因为没有重要价值的证据),但可以通过使用圆形阵列来避免副本;我们在pos % needle_size处插入新字符,并比较该边界之前和之后的区域,就好像它们分别是尾部和头部一样。例如:

void find_match(FILE *input_file, char const *needle, size_t needle_size) {
    char input_array[needle_size];
    size_t sz = fread(input_array, 1, needle_size, input_file);
    if (sz != needle_size) {
        // No matches possible
        return;
    }

    setvbuf(input_file, NULL, _IOFBF, BUFSIZ);
    unsigned long long pos = 0;
    for (;;) {
        size_t cursor = pos % needle_size;
        int tail_compare = memcmp(input_array, needle + needle_size - cursor, cursor),
            head_compare = memcmp(input_array + cursor, needle, needle_size - cursor);
        if (head_compare == 0 && tail_compare == 0) {
            printf("Match found at offset %llu\n", pos);
        }
        int c = fgetc(input_file);
        if (c == EOF) {
            break;
        }
        input_array[cursor] = c;
        pos++;
    }
}