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 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算法一网打尽 楼主也可以试试 ripgrep,它速度比 grep 还快,支持自动忽略隐藏文件夹、二进制文件以及 .gitignore 内指定的文件(当然也可以选择禁用这些自动规则) 请在帖子内插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能介绍【20220924强制执行】
这个不错16进制搜索支持通配符吗 少年持剑 发表于 2024-12-21 17:19
这个不错16进制搜索支持通配符吗
暂时不支持。因为是自用的,目前没有需求就没去思考这个功能。
要支持可能需要对kmp改动更多,目前还没有把握。
欢迎提交MR:lol 苏紫方璇 发表于 2024-12-21 16:06
请在帖子内插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能 ...
功能介绍就有了,关键代码。。。感觉都挺关键的,其他代码不好拆,就贴个大文件映射的类代码吧 感谢老板 yes2 发表于 2024-12-21 20:07
暂时不支持。因为是自用的,目前没有需求就没去思考这个功能。
要支持可能需要对kmp改动更多,目前还没 ...
{:301_998:} 有支持内存特征码匹配的吗? sunday算法在实际场景中是远优于kmp
shift-and算法也能处理流数据单模匹配问题,并且实际中比kmp更加高效。 三滑稽甲苯 发表于 2024-12-22 08:29
楼主也可以试试 ripgrep,它速度比 grep 还快,支持自动忽略隐藏文件夹、二进制文件以及 .gitignore 内指定 ...
感谢推荐,有空试试:lol
页:
[1]
2