如何在字节数组中搜索模式

How to search in a BYTE array for a pattern?

本文关键字:搜索 模式 数组 字节数 字节      更新时间:2023-10-16

我有一个字节数组:

BYTE Buffer[20000];这个数组包含以下数据:

00 ffffffffffff0010ac4c4053433442341401030a2f1e78eeee95a3544c99260f5054a54b00714f8180b3000101010101010101010121399030621a274068b03600da281100001c000000ff003457314d44304353423443530a000000fc0044454c4c2050323231300a2020000000fd00384b1e5310000a20202020202000fa

我的问题是如何搜索这个数组的模式,如"000000FC"?我真的不认为这是重要的,但我需要索引,我可以找到我的模式。

既然你是用c++写的,那就用c++的方式写吧:

char a[] = { 0, 0, 0, 0xFC };
char Buffer[20000] = ...
std::string needle(a, a + 4);
std::string haystack(Buffer, Buffer + 20000);  // or "+ sizeof Buffer"
std::size_t n = haystack.find(needle);
if (n == std::string::npos)
{
    // not found
}
else
{
    // position is n
}

您也可以使用算法直接搜索数组:

#include <algorithm>
#include <iterator>
auto it = std::search(
    std::begin(Buffer), std::end(Buffer),
    std::begin(a), std::end(a));
if (it == std::end(Buffer))
{
    // not found
}
else
{
    // subrange found at std::distance(std::begin(Buffer), it)
}

或者,在c++ 17中,可以使用字符串视图:

std::string_view sv(std::begin(Buffer), std::end(Buffer));
if (std::size_t n = sv.find(needle); n != sv.npos)
{
    // found at position n
}
else
{
    // not found
}

您想要memmem(该代码使用GPL许可)。

然而,你自己卷应该不难。像memmem的实现一样,您需要一个循环,使用memchr在干草堆中找到针的第一个字符,并使用memcmp测试每次命中并查看是否所有针都在那里。

可以在std::search()中使用原始指针。

例如:

#include <algorithm>
BYTE Buffer[20000] = { 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFC };
PBYTE pBufferLast = Buffer + sizeof(Buffer);
BYTE Pattern[] = { 0x00, 0x00, 0x00, 0xFC };
PBYTE pPatternLast = Pattern + sizeof(Pattern);
PBYTE pOccurrence = std::search(Buffer, pBufferLast, Pattern, pPatternLast);
BOOL fFound = (pOccurrence != pBufferLast);

由于C++17, std::search()可以使用Boyer-Moore搜索(boyer_moore_searcher)等

试试这个,只是需要它:

// Returns a pointer to the first byte of needle inside haystack, 
static uint8_t* bytes_find(uint8_t* haystack, size_t haystackLen, uint8_t* needle, size_t needleLen) {
    if (needleLen > haystackLen) {
        return false;
    }
    uint8_t* match = memchr(haystack, needle[0], haystackLen);
    if (match != NULL) {
        size_t remaining = haystackLen - ((uint8_t*)match - haystack);
        if (needleLen <= remaining) {
            if (memcmp(match, needle, needleLen) == 0) {
                return match;
            }
        }
    }
    return NULL;
}

这是一个使用C缓冲区的简单/幼稚的解决方案:

const char *find_needle(const char *haystack, size_t haystack_length, const char *needle, size_t needle_length) {
    for (size_t haystack_index = 0; haystack_index < haystack_length; haystack_index++) {
        bool needle_found = true;
        for (size_t needle_index = 0; needle_index < needle_length; needle_index++) {
            const auto haystack_character = haystack[haystack_index + needle_index];
            const auto needle_character = needle[needle_index];
            if (haystack_character == needle_character) {
                continue;
            } else {
                needle_found = false;
                break;
            }
        }
        if (needle_found) {
            return &haystack[haystack_index];
        }
    }
    return nullptr;
}

一个更有效的解决方案是使用Knuth-Morris-Pratt算法,但实现也更复杂。

编辑:


使用C++17,我发现最快的解决方案是使用std::search和boyer_moore_horspool_searcher。

'Tarion'他发布的函数几乎是完美的。但是它做了一个'memcmp'(再次检查第一个字符),它已经用'memchr'做了。如果更多的第一个字符将在数组中,然后不总是指针字节,它就不起作用了。这里你看到了两个帖子,把它复制到你的c++项目中然后调用函数testt ()

你可以根据自己的意愿修改它。还要知道,char*只是一个char[]数组,但作为一个指针。所以如果你在一个char变量前面看到一个'&'它只是使用char*作为一个普通的char[]通过索引通过返回指向该索引的指针。另外,要重新定义不是固定大小的数组,请使用'malloc' c++函数。永远不要听从c++程序员的建议,让你使用C字符串函数来搜索二进制数据。他们太差劲了。只使用c++函数。

如何使用。:搜索字节程序的c++(第1/2部分和第2/2部分)为您的查找字节函数并将其复制到您的c++项目中,您必须复制它所有(也在白色区域上方的标题行,以及在白色区域中看到'}'字符的第2/2部分下方),我不能上传它所有因为StackOverflow帖子错误,我很着急。

c++搜索字节例程(PART 1/2) -将下面的代码复制到你的c++项目中

// Returns a int with the starting position of the bytes found
int FindBytesPosInCharPointer(char* bytSourceBuffer, size_t lngSourceBufferStartPos, size_t lngSourceBufferTotalLen, char* bytBytesToFind, size_t lngBytesToFindLen) {
    
//when calling this function bytSourceBuffer must always point to index [0]
//use the lngSourceBufferStartPos to use a starting point index
    if (lngBytesToFindLen <= 0 || lngBytesToFindLen > lngSourceBufferTotalLen - lngSourceBufferStartPos) {
        return -1;
    }
    if (lngSourceBufferStartPos < 0) {
        return -1;
    }
    if (lngSourceBufferStartPos >= lngSourceBufferTotalLen) {
        return -1;
    }
    if (lngSourceBufferTotalLen <= 0) {
        return -1;
    }
    //memchr returns a pointer to the array
    size_t lngFoundPos = 0;
    size_t lngFirstPosFound = 0;
    size_t lngCurStartPos = 0;
    char* lngPointerPos;
    int intMustExitLoop = 0;

    //this can change underway but used only internal
    lngCurStartPos = lngSourceBufferStartPos;

    //find first byte
    lngPointerPos = NULL;
    lngPointerPos = (char*)memchr(&bytSourceBuffer[lngCurStartPos], bytBytesToFind[0], lngSourceBufferTotalLen - lngCurStartPos);
    //validate
    if (lngPointerPos == NULL) {
        //karakter niet gevonden
        lngFoundPos = -1;
        lngFirstPosFound = -1;
    }
    else {
        //current position where char has been found
        lngFoundPos = (lngPointerPos - &bytSourceBuffer[lngCurStartPos]);
        lngFirstPosFound = lngFoundPos; //we need this to skip bytes
        //zoeken
        if (lngBytesToFindLen == 1) {
            //we only want to find 1 char, so we dont need to use memcmp
            //is done now
        }
        else {
            //check if (remaining) bytBytesToFind bytes matching
            if (lngBytesToFindLen <= ((lngSourceBufferTotalLen - lngCurStartPos) - lngFoundPos)) {  //(lngSourceBufferLen - lngCurStartPos) = remaining bytes
                //memcmp = 0 is blocks zijn hetzelfde
                if (memcmp(lngPointerPos + 1, &bytBytesToFind[1], lngBytesToFindLen - 1) == 0) {
                    //BYTES MATCHING, lngFoundPos will return the result
                }
                else {
                    //MessageBox(NULL, "BLOCKS DO NOT MATCH", "Info", 0);
                    lngFoundPos = -1;
                }
            }
            else {
                //MessageBox(NULL, "NOT ENOUGH BYTES", "Info", 0);
                lngFoundPos = -1;
            }

            //validate the result, if bytes did not match we start the loop
            if (lngFoundPos == -1) {
                //new position to start searching from
                lngCurStartPos = lngFirstPosFound + 1;
                lngFirstPosFound = -1;

                //validate if startpos is still valid
                if (lngCurStartPos < 0 || (lngSourceBufferStartPos + lngCurStartPos) >= lngSourceBufferTotalLen) {
                    lngFoundPos = -1;
                }
                else {
                    if (lngBytesToFindLen > lngSourceBufferTotalLen - (lngSourceBufferStartPos + lngCurStartPos)) {
                        lngFoundPos = -1;
                    }
                    else {
                        //condition
                        intMustExitLoop = 0;
                        //start loop
                        do {
                            //validate if startpos is still valid
                            if (lngCurStartPos < 0 || (lngSourceBufferStartPos + lngCurStartPos) >= lngSourceBufferTotalLen) {
                                lngFoundPos = -1;
                                intMustExitLoop = 1;
                                break;
                            }
                            else {
                                if (lngBytesToFindLen > lngSourceBufferTotalLen - (lngSourceBufferStartPos + lngCurStartPos)) {
                                    lngFoundPos = -1;
                                    intMustExitLoop = 1;
                                    break;
                                }
                                else {
                                    //search for first byte again, memchr returns a pointer
                                    lngPointerPos = NULL;
                                    lngPointerPos = (char*)memchr(&bytSourceBuffer[lngCurStartPos], bytBytesToFind[0], lngSourceBufferTotalLen - lngCurStartPos);
                                    //afhandelen
                                    if (lngPointerPos == NULL) {
                                        //character not found
                                        lngFoundPos = -1;
                                        intMustExitLoop = 1;
                                        break;
                                    }
                                    else {
                                        //this is the current position where character has been found
                                        lngFoundPos = (lngPointerPos - &bytSourceBuffer[lngCurStartPos]);
                                        lngFirstPosFound = lngFoundPos; //we need this to skip bytes

                                        //MessageBoxA(NULL, std::to_string(lngFirstPosFound).c_str(), "Caption", 0);

                                        //check if (remaining) bytBytesToFind bytes matching
                                        if (lngBytesToFindLen <= ((lngSourceBufferTotalLen - (lngSourceBufferStartPos + lngCurStartPos)) - lngFoundPos)) {  //(lngSourceBufferLen - lngCurPos) = remaining bytes
                                            //memcmp = 0 is blocks zijn hetzelfde
                                            if (memcmp(lngPointerPos + 1, &bytBytesToFind[1], lngBytesToFindLen - 1) == 0) {
                                                //BYTES MATCHING
                                                //This is the real position in the array from starting point lngSourceBufferStartPos
                                                lngFoundPos = (lngPointerPos - &bytSourceBuffer[lngCurStartPos]) + (lngCurStartPos - lngSourceBufferStartPos);
                                                //is done now
                                                intMustExitLoop = 1;
                                                break;
                                            }
                                            else {
                                                //MessageBoxA(NULL, "BLOCKS DO NOT MATCH", "Caption", 0);
                                                //will loop again for next search
                                                lngFoundPos = -1;
                                                lngCurStartPos = lngCurStartPos + (lngFirstPosFound + 1);
                                                lngFirstPosFound = -1;
                                            }
                                        }
                                        else {
                                            //MessageBoxA(NULL, "NOT ENOUGH BYTES", "Caption", 0);
                                            //is meteen klaar dan
                                            lngFoundPos = -1;
                                            lngFirstPosFound = -1;
                                            intMustExitLoop = 1;
                                            break;
                                        }
                                    }
                                }
                            }
                        } while (intMustExitLoop == 0);
                    }
                }
            }
        }
    }
    //-1 is nothing found
    return lngFoundPos;
}

c++搜索字节例程(第2/2部分)-将下面的代码复制到你的c++项目中(将这些行放到testt函数中,或者您喜欢的任何东西中)。然后调用函数。我在向StackOverflow添加函数名时遇到了问题,但第一行应该是"int testt (){"最后两行应该是&;return 1;&;和"}"

//test
char* bytTest;
bytTest = (char*)malloc(10);
bytTest[0] = 0;
bytTest[1] = 0;
bytTest[2] = 70;
bytTest[3] = 70;
bytTest[4] = 65;    //A
bytTest[5] = 66;    //B
bytTest[6] = 1;
bytTest[7] = 70;
bytTest[8] = 68;
bytTest[9] = 69;
//bytTest[10] = 0;
//bytes to find 
char bytFindBytes[3];
bytFindBytes[0] = 70;
bytFindBytes[1] = 68;
bytFindBytes[2] = 69;
int lngFindPos = 0;
lngFindPos = FindBytesPosInCharPointer(&bytTest[0], 0, 10, &bytFindBytes[0], sizeof bytFindBytes);
MessageBoxA(NULL, std::to_string(lngFindPos).c_str(), "Caption", 0);

//我知道我可以只传递bytTest和bytFindBytes没有'&'和[0]的函数,但它是向您展示如何使用指针,使用bytTest[0]只会传递数组中第一个字节的值给函数(这无论如何会给出编译错误),其中添加'&'传递数组中第一个字节的指针给函数。为了让你们更清楚。