吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1865|回复: 9
上一主题 下一主题
收起左侧

[其他原创] 命令行版本十六进制通配符搜索替换工具 delphi 版本 源码

[复制链接]
跳转到指定楼层
楼主
qqycra 发表于 2025-6-5 23:21 回帖奖励
本帖最后由 qqycra 于 2025-6-8 16:09 编辑

文件说明:


UFileProcessor.pas 文件处理模块;
UCmdParser.pas 命令行解析模块;
UHexUtils.pas 十六进制相关;
HexRep.dpr 项目文件;

替换相关核心代码:
[Delphi] 纯文本查看 复制代码
procedure TFileProcessor.ApplyReplacements(const FileName: string; const Matches: TList;
  const SearchPattern: TPatternInfo; const ReplacePattern: TPatternInfo);
var
  FileMapping: THandle;
  MapView: Pointer;
  FileHandle: THandle;
  FileSize: Int64;
  I: Integer;
  Match: TMatchInfo;
  ActualReplacePattern: TBytes;
  FindBuffer: TBytes;
  ChunkSize: Integer;
  CurrentOffset, ViewSize: Int64;
  MatchesInCurrentView: TList;
  MatchPtr: PMatchInfo;
  HighDWord, LowDWord: DWORD;
  ThreadPool: array of TThread;
  ProcessorCount, TaskCount: Integer;
  MatchesPerTask: Integer;
  GroupedMatches: TArray<TList>;
  MemoryInfo: TMemoryStatusEx;
  AvailableMemoryMB: Int64;
begin
  if (Matches.Count = 0) or (Length(ReplacePattern.Bytes) = 0) then
    Exit;

  // 确保搜索和替换的长度相同
  if Length(SearchPattern.Bytes) <> Length(ReplacePattern.Bytes) then
    Exit;

  // 打开文件准备映射
  FileHandle := CreateFile(
    PChar(FileName),
    GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ, // 允许其他进程读取
    nil,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);

  if FileHandle = INVALID_HANDLE_VALUE then
    raise Exception.Create('Cannot open file for replacement: ' + FileName);

  try
    FileSize := GetFileSize(FileHandle, nil);
    
    // 获取系统可用内存信息,用于优化内存映射策略
    MemoryInfo.dwLength := SizeOf(MemoryInfo);
    if GlobalMemoryStatusEx(MemoryInfo) then
      AvailableMemoryMB := MemoryInfo.ullAvailPhys div (1024 * 1024)
    else
      AvailableMemoryMB := 1024; // 默认假设1GB可用内存
    
    // 对于大文件,采用更高效的分块替换策略
    if (FileSize > 1024 * 1024 * 1024) or (AvailableMemoryMB < FileSize div (1024 * 1024) div 2) then // 1GB以上的文件或可用内存不足的情况
    begin
      // 计算最佳任务数
      ProcessorCount := TThread.ProcessorCount;
      TaskCount := Min(ProcessorCount * 2, Matches.Count);
      if TaskCount < 1 then TaskCount := 1;
      
      // 每个任务处理的匹配数
      MatchesPerTask := Matches.Count div TaskCount;
      if Matches.Count mod TaskCount > 0 then
        Inc(MatchesPerTask);
        
      SetLength(ThreadPool, TaskCount);
      SetLength(GroupedMatches, TaskCount);
      
      // 为每个任务分配匹配
      for I := 0 to TaskCount - 1 do
      begin
        GroupedMatches[I] := TList.Create;
      end;
      
      // 将匹配按偏移分组,相邻的放在一起,这样可以更有效地利用内存映射
      for I := 0 to Matches.Count - 1 do
      begin
        var TaskIndex := I div MatchesPerTask;
        if TaskIndex >= TaskCount then TaskIndex := TaskCount - 1;
        
        var MatchCopy := New(PMatchInfo);
        MatchCopy^ := GetMatchInfo(Matches, I);
        GroupedMatches[TaskIndex].Add(MatchCopy);
      end;
      
      // 并行处理每组匹配 - 使用TThread而不是TTask
      for I := 0 to TaskCount - 1 do
      begin
        var LocalI := I;
        ThreadPool[I] := TThread.CreateAnonymousThread(
          procedure
          var
            LocalMatches: TList;
            J: Integer;
            LocalFileMapping: THandle;
            LocalMapView: Pointer;
            LocalHighDWord, LocalLowDWord: DWORD;
            LocalOffset, LocalViewSize: Int64;
            LocalMatch: TMatchInfo;
            LocalFindBuffer, LocalActualReplacePattern: TBytes;
            LastMappedOffset: Int64;
            LastMapView: Pointer;
            LastMapSize: Int64;
          begin
            LocalMatches := GroupedMatches[LocalI];
            if LocalMatches.Count = 0 then Exit;
            
            // 创建文件映射对象
            LocalFileMapping := CreateFileMapping(
              FileHandle,
              nil,
              PAGE_READWRITE,
              0, 0,
              nil);
              
            if LocalFileMapping = 0 then Exit;
            
            try
              // 初始化映射变量
              LastMappedOffset := -1;
              LastMapView := nil;
              LastMapSize := 0;
              
              // 对匹配进行排序,这样可以优化映射
              LocalMatches.Sort(StaticCompareMatchOffsets);
              
              for J := 0 to LocalMatches.Count - 1 do
              begin
                LocalMatch := PMatchInfo(LocalMatches[J])^;
                
                if LocalMatch.IsReplace then
                begin
                  // 计算映射位置
                  LocalOffset := LocalMatch.Offset;
                  LocalViewSize := Length(SearchPattern.Bytes);
                  
                  // 检查是否可以重用上一次的映射
                  if (LastMappedOffset <> -1) and 
                     (LocalOffset >= LastMappedOffset) and 
                     (LocalOffset + LocalViewSize <= LastMappedOffset + LastMapSize) then
                  begin
                    // 可以重用上一次的映射
                    var RelativeOffset := LocalOffset - LastMappedOffset;
                    
                    // 获取实际找到的内容
                    SetLength(LocalFindBuffer, Length(SearchPattern.Bytes));
                    Move(PByte(LastMapView)[RelativeOffset], LocalFindBuffer[0], Length(SearchPattern.Bytes));
                    
                    // 应用替换模式,处理通配符
                    LocalActualReplacePattern := THexUtils.ApplyReplacePattern(LocalFindBuffer, ReplacePattern);
                    
                    // 执行替换
                    Move(LocalActualReplacePattern[0], PByte(LastMapView)[RelativeOffset], Length(LocalActualReplacePattern));
                  end
                  else
                  begin
                    // 需要创建新的映射
                    if LastMapView <> nil then
                      UnmapViewOfFile(LastMapView);
                      
                    // 计算高32位和低32位
                    LocalHighDWord := DWORD(LocalOffset shr 32);
                    LocalLowDWord := DWORD(LocalOffset and $FFFFFFFF);
                    
                    // 映射当前位置,使用更大的映射区域以便重用
                    var ExtendedViewSize := Min(Int64(4 * 1024 * 1024), FileSize - LocalOffset); // 最多映射4MB
                    
                    LocalMapView := MapViewOfFile(
                      LocalFileMapping,
                      FILE_MAP_WRITE,
                      LocalHighDWord,
                      LocalLowDWord,
                      ExtendedViewSize);
                      
                    if LocalMapView = nil then Continue;
                    
                    // 更新映射信息
                    LastMappedOffset := LocalOffset;
                    LastMapView := LocalMapView;
                    LastMapSize := ExtendedViewSize;
                    
                    // 获取实际找到的内容
                    SetLength(LocalFindBuffer, Length(SearchPattern.Bytes));
                    Move(PByte(LocalMapView)[0], LocalFindBuffer[0], Length(SearchPattern.Bytes));
                    
                    // 应用替换模式,处理通配符
                    LocalActualReplacePattern := THexUtils.ApplyReplacePattern(LocalFindBuffer, ReplacePattern);
                    
                    // 执行替换
                    Move(LocalActualReplacePattern[0], PByte(LocalMapView)[0], Length(LocalActualReplacePattern));
                  end;
                end;
              end;
              
              // 清理最后一次映射
              if LastMapView <> nil then
                UnmapViewOfFile(LastMapView);
            finally
              CloseHandle(LocalFileMapping);
            end;
          end);
          
        ThreadPool[I].FreeOnTerminate := True;
        ThreadPool[I].Start;
      end;
      
      // 等待所有线程完成
      for I := 0 to TaskCount - 1 do
      begin
        if ThreadPool[I] <> nil then
          ThreadPool[I].WaitFor;
      end;
      
      // 清理资源
      for I := 0 to TaskCount - 1 do
      begin
        for var J := 0 to GroupedMatches[I].Count - 1 do
        begin
          Dispose(PMatchInfo(GroupedMatches[I][J]));
        end;
        GroupedMatches[I].Free;
      end;
    end
    else // 对于较小的文件,使用原来的分块映射方法
    begin
      // 对于大文件,分块映射处理
      ChunkSize := 512 * 1024 * 1024; // 512MB的块大小用于替换操作
      CurrentOffset := 0;
      
      while CurrentOffset < FileSize do
      begin
        // 计算当前视图大小
        ViewSize := Min(ChunkSize, FileSize - CurrentOffset);
        
        // 创建文件映射对象
        FileMapping := CreateFileMapping(
          FileHandle,
          nil,
          PAGE_READWRITE,
          0, 0, // 使用整个文件大小
          nil);
          
        if FileMapping = 0 then
          raise Exception.Create('Cannot create file mapping: ' + FileName);
          
        try
          // 计算高32位和低32位
          HighDWord := DWORD(CurrentOffset shr 32);
          LowDWord := DWORD(CurrentOffset and $FFFFFFFF);
          
          // 映射当前块
          MapView := MapViewOfFile(
            FileMapping,
            FILE_MAP_WRITE,
            HighDWord, // 高32位
            LowDWord, // 低32位
            ViewSize);
            
          if MapView = nil then
            raise Exception.Create('Cannot map file view: ' + FileName);
            
          try
            // 收集当前视图中的匹配
            MatchesInCurrentView := TList.Create;
            try
              for I := 0 to Matches.Count - 1 do
              begin
                Match := GetMatchInfo(Matches, I);
                
                // 检查匹配是否在当前视图范围内
                if (Match.Offset >= CurrentOffset) and (Match.Offset < CurrentOffset + ViewSize) and
                   (Match.Offset + Length(SearchPattern.Bytes) <= CurrentOffset + ViewSize) then
                begin
                  New(MatchPtr);
                  MatchPtr^ := Match;
                  MatchesInCurrentView.Add(MatchPtr);
                end;
              end;
              
              // 对当前视图中的匹配进行替换
              for I := 0 to MatchesInCurrentView.Count - 1 do
              begin
                MatchPtr := MatchesInCurrentView[I];
                Match := MatchPtr^;
                
                if Match.IsReplace then
                begin
                  // 计算在当前视图中的相对偏移
                  var RelativeOffset := Match.Offset - CurrentOffset;
                  
                  // 获取实际找到的内容
                  SetLength(FindBuffer, Length(SearchPattern.Bytes));
                  Move(PByte(MapView)[RelativeOffset], FindBuffer[0], Length(SearchPattern.Bytes));
                  
                  // 应用替换模式,处理通配符
                  ActualReplacePattern := THexUtils.ApplyReplacePattern(FindBuffer, ReplacePattern);
                  
                  // 执行替换
                  Move(ActualReplacePattern[0], PByte(MapView)[RelativeOffset], Length(ActualReplacePattern));
                end;
              end;
            finally
              // 清理当前视图中的匹配列表
              for I := 0 to MatchesInCurrentView.Count - 1 do
              begin
                MatchPtr := MatchesInCurrentView[I];
                if Assigned(MatchPtr) then
                  Dispose(MatchPtr);
              end;
              MatchesInCurrentView.Free;
            end;
          finally
            UnmapViewOfFile(MapView);
          end;
        finally
          CloseHandle(FileMapping);
        end;
        
        // 移动到下一个块
        Inc(CurrentOffset, ViewSize);
      end;
    end;
  finally
    if FileHandle <> INVALID_HANDLE_VALUE then
      CloseHandle(FileHandle);
  end;
end;


该版本没有高级功能,我尝试增加高级功能想提高性能,但多次改写失败,后来改为搜索替换等长,
再后来发现应该用go写。go版本800MB文件在我电脑上通配符替换实测不到900ms。

这是自己随便写的,源码可以用来学习用,相关文件使用人责任自负。


hexrep.7z (9.05 KB, 下载次数: 42)

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| qqycra 发表于 2025-6-5 23:22 |楼主
我还有个go版本的,那么功能多些
3#
zixuan203344 发表于 2025-6-6 09:30
4#
alongzhenggang 发表于 2025-6-6 12:21
5#
10830 发表于 2025-6-6 17:06
qqycra 发表于 2025-6-5 23:22
我还有个go版本的,那么功能多些

蹲蹲大神安排
6#
 楼主| qqycra 发表于 2025-6-7 13:14 |楼主
越界 丢项 格式不好看,这几天一直处理中
7#
苏紫方璇 发表于 2025-6-8 15:43
请在帖子中插入部分核心代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能介绍
8#
 楼主| qqycra 发表于 2025-6-8 16:04 |楼主
苏紫方璇 发表于 2025-6-8 15:43
请在帖子中插入部分核心代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能 ...

好的,过会去改
9#
asong 发表于 2025-7-11 00:05
感谢分享,难得有Delphi的大佬分享技术
10#
wangjbcc888 发表于 2025-8-3 15:02
附件是个啥啊/?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-14 15:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表