[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;