yes2 发表于 2024-12-21 13:57

hex_search 多文件搜索文本和二进制,开源命令行工具

本帖最后由 yes2 于 2024-12-21 20:05 编辑

https://gitee.com/yes2/hex_search
可能我需求比较怪,一直没找到趁手的工具,所以自己写了一个。这个是在windows下使用的,因为linux下有grep功能更强大速度更快。
用法:
hex_search target_folder "text"
hex_search target_folder "binary_data" -b
功能一:搜索文本,不区分大小写,命令行末尾可以手动加上代码页参数。
比如 hex_search "c:\some_app" "验证失败" 65001 或者 hex_search "c:\some_app" "验证失败" 1200
如果是 hex_search "c:\some_app" "验证失败" 不带代码页参数的话就使用系统默认代码页
功能二:搜索二进制数据,末尾加-b参数表示搜索二进制而不是文本。
比如 hex_search "c:\some_app" "4b 6d" -b 二进制数据中间可以有空格也可以不加空格。


特性:
尽量追求性能。
1.使用多线程任务队列方式,排队取任务进行搜索;
2.使用内存映射技术,避免频繁read;
3.使用kmp搜索技术,kmp是抄的开源代码,做了一点小修改支持忽略大小写;


todo:
1.大文件可以考虑分块给任务队列领取任务,避免其他线程都干完活了只剩下一个线程在搜索最后一个大文件;
2.文本搜索的时候也许可以提供一个区分大小写的选项,理论上应该能提高一点性能;
3.单次访问的页面范围可以考虑更大,减少内存映射和kmp搜索的次数;
4.搜索结果目前只输出匹配结果的文件名和第一个匹配到的偏移,后面可能会考虑输出更多信息,以及单个文件内多个匹配到的结果


欢迎review代码和fork,欢迎批评和建议,互相探讨,共同进步。
附上大文件操作的类代码:
LargeFile.h
#pragma once

#define ALIGN_DOWN_BY(length, alignment) \
    ((ULONG_PTR)(length) & ~(alignment - 1))

#define ALIGN_UP_BY(length, alignment) \
    (ALIGN_DOWN_BY(((ULONG_PTR)(length) + alignment - 1), alignment))

#define ALIGN_DOWN_POINTER_BY(address, alignment) \
    ((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)alignment - 1)))

#define ALIGN_UP_POINTER_BY(address, alignment) \
    (ALIGN_DOWN_POINTER_BY(((ULONG_PTR)(address) + alignment - 1), alignment))

#define ALIGN_DOWN(length, type) \
    ALIGN_DOWN_BY(length, sizeof(type))

#define ALIGN_UP(length, type) \
    ALIGN_UP_BY(length, sizeof(type))

#define ALIGN_DOWN_POINTER(address, type) \
    ALIGN_DOWN_POINTER_BY(address, sizeof(type))

#define ALIGN_UP_POINTER(address, type) \
    ALIGN_UP_POINTER_BY(address, sizeof(type))

class CLargeFile
{
public:
        CLargeFile();
        ~CLargeFile();

        /************************************************************************/
        /* open file, return TRUE if success; if FALSE, use GetLastError to get error code
        /* error may occur at CreateFile or CreateFileMapping
        /************************************************************************/
        BOOL OpenFile(LPCTSTR pFilePathName, UINT nPageCount = 3);

        /************************************************************************/
        /* check if i have opened a file.
        /************************************************************************/
        BOOL IsOpenFile();

        /************************************************************************/
        /* get file path name.
        /************************************************************************/
        LPCTSTR GetFilePathName();

        /************************************************************************/
        /* just close file, NOTHING saved.
        /* if you want to save, call SaveFile() before CloseFile().
        /************************************************************************/
        void CloseFile();

        /************************************************************************/
        /* get file size.
        /************************************************************************/
        DWORD GetFileSizeLow();
        DWORD GetFileSizeHigh();
        void GetFileSizeEx(LARGE_INTEGER* puFileSize);

        /************************************************************************/
        /* visit file position. return a pointer point to the data at the position.
        /* dwAvalibleSize received the avalible size of the data that you can use,
        /* if out of the size, you should call VisitFilePosition()
        /* with the new position to get a new pointer and new avalible size.
        /************************************************************************/
        void* VisitFilePosition(DWORD nVisitLow, DWORD nVisitHigh = 0, DWORD* pdwAvalibleSize = 0);
        void* VisitFilePosition(LARGE_INTEGER nVisit, DWORD* pdwAvalibleSize = 0);

protected:
        virtual void OnUnmapViewOfFile();
        virtual BYTE* OnMapViewOfFile(LARGE_INTEGER nViewStart, DWORD dwMapSize);
        LARGE_INTEGER m_nViewStart;
        DWORD m_dwMapSize;
        DWORD m_dwPageSize;
        DWORD m_dwPageCount;
        BYTE* m_pView;
private:
        void init();
        TCHAR m_szFilePathName;
        HANDLE m_hFile;
        HANDLE m_hMap;

        LARGE_INTEGER m_nFileSize;
};



LargeFile.cpp
#include "stdafx.h"
#include <windows.h>
#include "LargeFile.h"


CLargeFile::CLargeFile()
{
        SYSTEM_INFO si;
        GetSystemInfo(&si);
        m_dwPageSize = si.dwAllocationGranularity;
        m_dwPageCount = 3;
        init();
}


CLargeFile::~CLargeFile()
{
    CloseFile();
}

BOOL CLargeFile::OpenFile(LPCTSTR pFilePathName, UINT nPageCount /*= 3*/)
{
        CloseFile();

        m_hFile = CreateFile(pFilePathName,
                GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, 0);
        if (m_hFile == INVALID_HANDLE_VALUE)
        {
                return FALSE;
        }
        ::GetFileSizeEx(m_hFile, &m_nFileSize);
       
        if (!m_nFileSize.QuadPart)
        {
                CloseHandle(m_hFile);
                m_hFile = INVALID_HANDLE_VALUE;
                SetLastError(ERROR_EMPTY);
                return FALSE;
        }
        m_hMap = CreateFileMapping(m_hFile, 0, PAGE_WRITECOPY, 0, 0, 0);
        if (!m_hMap)
        {
                CloseHandle(m_hFile);
                m_hFile = INVALID_HANDLE_VALUE;
                return FALSE;
        }
        lstrcpy(m_szFilePathName, pFilePathName);
        m_pView = 0;
        m_nViewStart.QuadPart = 0;
        m_dwPageCount = nPageCount;
        return TRUE;
}

BOOL CLargeFile::IsOpenFile()
{
        return (m_hFile != INVALID_HANDLE_VALUE) && (m_hMap != NULL);
}

LPCTSTR CLargeFile::GetFilePathName()
{
        return m_szFilePathName;
}

void CLargeFile::CloseFile()
{
        if (m_pView)
        {
      OnUnmapViewOfFile();
        }
        if (m_hMap)
        {
                CloseHandle(m_hMap);
        }
        if (m_hFile != INVALID_HANDLE_VALUE)
        {
                CloseHandle(m_hFile);
        }
        init();
}

DWORD CLargeFile::GetFileSizeLow()
{
        return m_nFileSize.LowPart;
}

DWORD CLargeFile::GetFileSizeHigh()
{
        return m_nFileSize.HighPart;
}

void CLargeFile::GetFileSizeEx(LARGE_INTEGER* puFileSize)
{
        if (puFileSize)
                puFileSize->QuadPart = m_nFileSize.QuadPart;
}

void* CLargeFile::VisitFilePosition(LARGE_INTEGER nVisit, DWORD* pdwAvalibleSize /*= 0*/)
{
        if (m_hFile == INVALID_HANDLE_VALUE)
        {
                return 0;
        }
        if (nVisit.QuadPart >= m_nFileSize.QuadPart)
        {
                return 0;
        }
        DWORD dwAvalibleSize = 0;
        DWORD dwVisitOffset = 0;

        if (m_pView)
        {
                if (nVisit.QuadPart >= m_nViewStart.QuadPart + m_dwPageSize &&
                        nVisit.QuadPart <= m_nViewStart.QuadPart + m_dwPageSize * 2)
                {
            dwVisitOffset = (DWORD)(nVisit.QuadPart - m_nViewStart.QuadPart);
                        dwAvalibleSize = m_dwPageSize * 3 - dwVisitOffset;
                }

                if (nVisit.QuadPart >= m_nViewStart.QuadPart && // m_nViewStart.QuadPart means 0
                        nVisit.QuadPart < m_dwPageSize)
                {
            dwVisitOffset = (DWORD)nVisit.QuadPart;
                        dwAvalibleSize = m_dwPageSize * 3 - dwVisitOffset;
                }

                if (dwAvalibleSize)
                {
                        if (dwAvalibleSize > m_nFileSize.QuadPart - nVisit.QuadPart)
                        {
                dwAvalibleSize = (DWORD)(m_nFileSize.QuadPart - nVisit.QuadPart);
                        }
                        if (pdwAvalibleSize)
                        {
                                *pdwAvalibleSize = dwAvalibleSize;
                        }
                        return m_pView + dwVisitOffset;
                }
        }

        if (m_pView)
        {
                OnUnmapViewOfFile();
        }

        if (nVisit.QuadPart < m_dwPageSize)
        {
                m_nViewStart.QuadPart = 0;
        }
        else
        {
                m_nViewStart.QuadPart = ALIGN_DOWN_BY(nVisit.QuadPart, m_dwPageSize) - m_dwPageSize;
        }

        m_dwMapSize = m_dwPageSize * m_dwPageCount;
        if (m_nViewStart.QuadPart + m_dwMapSize > m_nFileSize.QuadPart)
        {
      m_dwMapSize = (DWORD)(m_nFileSize.QuadPart - m_nViewStart.QuadPart);
        }

        m_pView = OnMapViewOfFile(m_nViewStart, m_dwMapSize);
        if (!m_pView)
        {
                return 0;
        }
    dwVisitOffset = (DWORD)(nVisit.QuadPart - m_nViewStart.QuadPart);
        dwAvalibleSize = m_dwMapSize - dwVisitOffset;
        if (pdwAvalibleSize)
        {
                *pdwAvalibleSize = dwAvalibleSize;
        }

        return m_pView + dwVisitOffset;
}

void* CLargeFile::VisitFilePosition(DWORD nVisitLow, DWORD nVisitHigh /*= 0*/, DWORD* pdwAvalibleSize /*= 0*/)
{
        LARGE_INTEGER nVisit;
        nVisit.LowPart = nVisitLow;
        nVisit.HighPart = nVisitHigh;
        return VisitFilePosition(nVisit, pdwAvalibleSize);
}

void CLargeFile::OnUnmapViewOfFile()
{
        UnmapViewOfFile(m_pView);
}

BYTE* CLargeFile::OnMapViewOfFile(LARGE_INTEGER nViewStart, DWORD dwMapSize)
{
        return (BYTE*)MapViewOfFile(m_hMap, FILE_MAP_COPY, nViewStart.HighPart,
                nViewStart.LowPart, dwMapSize);
}

void CLargeFile::init()
{
        m_hFile = INVALID_HANDLE_VALUE;
        m_hMap = 0;
        m_pView = 0;

        m_nFileSize.QuadPart = 0;
        m_nViewStart.QuadPart = 0;
        m_szFilePathName = 0;
}

Elaineliu 发表于 2024-12-22 12:41

本帖最后由 Elaineliu 于 2024-12-22 13:00 编辑

yes2 发表于 2024-12-22 12:40
感谢指导,我研究一下。我在算法这块比较渣
https://www.52pojie.cn/thread-1962446-1-1.html
https://www.52pojie.cn/thread-1982500-1-1.html

这个也行


你也可以在百度搜字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽

三滑稽甲苯 发表于 2024-12-22 08:29

楼主也可以试试 ripgrep,它速度比 grep 还快,支持自动忽略隐藏文件夹、二进制文件以及 .gitignore 内指定的文件(当然也可以选择禁用这些自动规则)

苏紫方璇 发表于 2024-12-21 16:06

请在帖子内插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能介绍【20220924强制执行】

少年持剑 发表于 2024-12-21 17:19

这个不错16进制搜索支持通配符吗

yes2 发表于 2024-12-21 20:07

少年持剑 发表于 2024-12-21 17:19
这个不错16进制搜索支持通配符吗

暂时不支持。因为是自用的,目前没有需求就没去思考这个功能。
要支持可能需要对kmp改动更多,目前还没有把握。
欢迎提交MR:lol

yes2 发表于 2024-12-21 20:08

苏紫方璇 发表于 2024-12-21 16:06
请在帖子内插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能 ...

功能介绍就有了,关键代码。。。感觉都挺关键的,其他代码不好拆,就贴个大文件映射的类代码吧

xiaoxion999 发表于 2024-12-21 22:43

感谢老板

董督秀 发表于 2024-12-22 06:37

yes2 发表于 2024-12-21 20:07
暂时不支持。因为是自用的,目前没有需求就没去思考这个功能。
要支持可能需要对kmp改动更多,目前还没 ...

{:301_998:} 有支持内存特征码匹配的吗?

Elaineliu 发表于 2024-12-22 12:17

sunday算法在实际场景中是远优于kmp
shift-and算法也能处理流数据单模匹配问题,并且实际中比kmp更加高效。

yes2 发表于 2024-12-22 12:39

三滑稽甲苯 发表于 2024-12-22 08:29
楼主也可以试试 ripgrep,它速度比 grep 还快,支持自动忽略隐藏文件夹、二进制文件以及 .gitignore 内指定 ...

感谢推荐,有空试试:lol
页: [1] 2
查看完整版本: hex_search 多文件搜索文本和二进制,开源命令行工具