delphi 文件 转16进制的方法

发布网友 发布时间:2022-04-20 09:51

我来回答

2个回答

热心网友 时间:2022-07-10 21:30

好久以前,自己写过一个这样的小工具,今天看到你看重执行效率,遂捡出来仔细优化,力争最快,在优化过程中,个人也对32位汇编有了更深的体会。
试验了三种方法,实测50M的文件转换,耗时都不到一秒,分别是:565ms、468ms、374ms,编译成.exe后,执行bin2hex不带参数,就显示用法。
用法举例:
bin2hex bigFile.dat 1.txt //缺省采用最快的转换方法
bin2hex bigFile.dat 1.txt word //采用最快的转换方法
bin2hex bigFile.dat 2.txt byte //采用方法#2
bin2hex bigFile.dat 3.txt pascal //采用纯pascal实现的转换方法

程序如下,其中有几个函数也自感不错,可能会时常用到。
program bin2hex;

{$APPTYPE CONSOLE}

uses Classes, SysUtils, DateUtils;

procere ShowUsageAndExit;
const
C_sUsageFormat : string =
'功能:文件转换——字节数据→十六进制字符串'#$0D#$0A +
'用法: %s 原始文件 目标文件[ 转换方法]'#$0D#$0A +
'说明: 【原始文件】必须存在,一般是二进制文件(.dat/.dll/.exe等)'#$0D#$0A +
' 对于本来不是二进制文件的,也按照二进制的字节流来理解'#$0D#$0A +
' 【目标文件】如果存在,将被覆盖,没有提示,必要时自行备份'#$0D#$0A +
' 【转换方法】为“字节转成十六进制字符”的方法,有如下3种'#$0D#$0A +
' Pas:纯PASCAL,一次转换1个字节'#$0D#$0A +
' Byte:汇编实现,一次转换1个字节'#$0D#$0A +
' Word:汇编实现,一次转换2个字节'#$0D#$0A +
' 最后一种转换方法最快,故缺省采用';
var
s : string;
begin
s := ChangeFileExt( ExtractFileName( ParamStr(0) ), '' );
s := Format( C_sUsageFormat, [ s ] );
Writeln( ErrOutput, s );
Halt;
end;

function GetCmdArguments(
var sFN_Source : string;
var sFN_Target : string;
var nMethod : Integer
) : Boolean;
var
n : Integer;
s : string;
begin
Result := False;
n := ParamCount;
if n < 2 then // 参数不足
Exit;
sFN_Source := ParamStr(1);
if not FileExists( sFN_Source ) then // 原始文件必须存在
Exit;
sFN_Target := ParamStr(2);
nMethod := 3; // 缺省采用WORD转换方法
if n > 2 then // 如果有第三个参数...
begin
s := ParamStr(3);
case UpCase( s[1] ) of
'P': nMethod := 1; // PASCAL
'B': nMethod := 2; // BYTE
end;
end;
Result := True;
end;

// 转换方法#3:汇编,以Word为单位
function WordToHex_ASM( const AWord : Cardinal ) : Cardinal; register;
asm
PUSH ECX
MOV ECX, EAX
SHR ECX, 8
AND EAX, $FF
MOV EDX, EAX
SHR EAX, 4
AND EDX, $0F
CMP EDX, 9
JBE @@1
ADD EDX, 7
@@1:
CMP EAX, 9
JBE @@2
ADD EAX, 7
@@2:
SHL EDX, 8
OR EDX, EAX
XCHG EDX, ECX
MOV EAX, EDX
SHR EAX, 4
AND EDX, $0F
CMP EDX, 9
JBE @@3
ADD EDX, 7
@@3:
CMP EAX, 9
JBE @@4
ADD EAX, 7
@@4:
SHL EDX, 8
OR EAX, EDX
SHL EAX, 16
OR EAX, ECX
ADD EAX, $30303030
POP ECX
end;

// 转换方法#2:汇编,以Byte为单位
function ByteToHex_ASM( const AByte : Cardinal ) : Cardinal; register;
asm
MOV EDX, EAX
SHR EAX, 4
AND EDX, $0F
CMP EDX, 9
JBE @@1
ADD EDX, 7
@@1:
CMP EAX, 9
JBE @@2
ADD EAX, 7
@@2:
SHL EDX, 8
OR EAX, EDX
ADD EAX, $3030
end;

// 转换方法#1:PASCAL,以Byte为单位
function ByteToHex( const AByte : Byte ) : Cardinal;
begin
Result := $3030 + ( AByte and $0F ) shl 8 + AByte shr 4;
if Hi( Result ) > $39 then
Inc( Result, $0700 );
if Lo( Result ) > $39 then
Inc( Result, $0007 );
end;

// 输出一个任务的耗时
procere PrintEllapsed( sJobName : string; ANow, AThen : TDateTime );
var
X : Double;
begin
X := MillisecondSpan( ANow, AThen );
if X < 10000 then // 小于10000毫秒,即小于10秒,以毫秒为单位输出
Writeln( Format( '%s'#$09'%d 毫秒', [ sJobName, Trunc( X ) ] ) )
else // 大于10秒,则以秒为单位输出
Writeln( Format( '%s'#$09'%.2n 秒', [ sJobName, X ] ) );
end;

type
PJobEllapsed = ^TJobEllapsed;
TJobEllapsed = packed record
sJobName : string;
dtBegin : TDateTime;
dtEnd : TDateTime;
end;

procere PrintAllEllapsed( P : PJobEllapsed );
begin
if Assigned( P ) then
while P^.dtBegin <> 0 do
begin
PrintEllapsed( P^.sJobName, P^.dtBegin, P^.dtEnd );
Inc( P );
end;
end;

// 字节流→十六进制字符,返回字节数,即原始文件大小
function BytesToHexString(
sFN_Source : string;
sFN_Target : string;
nMethod : Integer = 3; // 缺省采用最快的转换方法
prEllapsed : PJobEllapsed = nil // 如需返回环节耗时,则给定缓冲区,缺省不必
) : Integer;
var
dtJobBegin : TDateTime; // 任务开始时间,每项任务开始前赋值
ms0 : TMemoryStream; // 用于装载原始文件
ms1 : TMemoryStream; // 用于暂存转换结果和保存到目标文件
P : PByte; // 原始数据字节流指针
Q : PCardinal; // 目标数据双字指针
i : Integer;
bComputeEllapsed : Boolean; // 是否需要返回分任务耗时
begin
bComputeEllapsed := Assigned( prEllapsed );
ms0 := TMemoryStream.Create;
ms1 := TMemoryStream.Create;
try
dtJobBegin := Now; // 本可为if bComputeEllapsed then ...,消除WARNing故
{ 装载... }
ms0.LoadFromFile( sFN_Source );
if bComputeEllapsed then
begin
prEllapsed^.sJobName := '装载';
prEllapsed^.dtBegin := dtJobBegin;
prEllapsed^.dtEnd := Now;
Inc( prEllapsed );
end;

Result := ms0.Size;
ms1.SetSize( Result * 2 );

if bComputeEllapsed then
dtJobBegin := Now;

{ 转换... }
P := PByte( ms0.Memory );
Q := PCardinal( ms1.Memory );
case nMethod of
1: // PASCAL
begin
for i := 1 to Result - 1 do
begin
Q^ := ByteToHex( P^ );
Inc( P );
Inc( PByte( Q ), 2 ); // 字节转为十六进制串占两个字节,故指针+2
end;
{ 特殊技巧说明:
下面这句若为Q^:=...,则内存越界,PWord(Q)^:=...刚好用足通过ms1申请的内存
上面循环终值是Result-1,而不是Result,理由亦同
}
PWord( Q )^ := ByteToHex( P^ );
end;
2: // ASM_BYTE
begin
for i := 1 to Result - 1 do
begin
Q^ := ByteToHex_ASM( P^ );
Inc( P );
Inc( PByte( Q ), 2 );
end;
PWord( Q )^ := ByteToHex_ASM( P^ );
end;
3: // ASM_WORD
begin
for i := 1 to Result shr 1 do
begin
Q^ := WordToHex_ASM( PWord( P )^ );
Inc( PWord( P ) ); // 等同于 Inc( P, 2 )
Inc( Q ); // 每次处理2个字节,十六进制字符占4个字节
end;
// 如果原始字节数是奇数,则处理最后一个
if Result and 1 <> 0 then
PWord( Q )^ := WordToHex_ASM( P^ );
end;
end;

if bComputeEllapsed then
begin
prEllapsed^.sJobName := '转换';
prEllapsed^.dtBegin := dtJobBegin;
prEllapsed^.dtEnd := Now;
Inc( prEllapsed );
end;

if bComputeEllapsed then
dtJobBegin := Now;

{ 保存... }
ms1.SaveToFile( sFN_Target );

if bComputeEllapsed then
begin
prEllapsed^.sJobName := '保存';
prEllapsed^.dtBegin := dtJobBegin;
prEllapsed^.dtEnd := Now;
end;

finally
ms1.Free;
ms0.Free;
end;
end;

var
n : Integer;
sSourceFile : string;
sTargetFile : string;
arJobEllapsed : array[ 0..15 ] of TJobEllapsed; // 足够大的缓冲区

begin
if not GetCmdArguments( sSourceFile, sTargetFile, n ) then
ShowUsageAndExit;

Writeln( '原始文件:', ExpandFileName( sSourceFile ) );
Writeln( '目标文件:', ExpandFileName( sTargetFile ) );
case n of
1: Writeln( '转换方法:PASCAL,以字节为单位处理' );
2: Writeln( '转换方法:汇编,以字节为单位处理' );
3: Writeln( '转换方法:汇编,以双字节为单位处理' );
end;

{ 以下这句予以注释屏蔽,乃因为操作系统加载进程时,自然会给它初始化
如果只用一次,就不必主动赋值,如欲使用多次,就需要自行初始化 }
// FillChar( arJobEllapsed, SizeOf( arJobEllapsed ), 0 );

n := BytesToHexString( sSourceFile, sTargetFile, n, @arJobEllapsed[0] );
Writeln( Format( '文件长度:%d', [ n ] ) );
PrintAllEllapsed( @arJobEllapsed[0] );
end.

热心网友 时间:2022-07-10 21:31

保存成文本类型的十六进制(用记事本打开后1J55A4JM6FJ5D24.......)? 先把文件装入TMemoryStream然后存入动态数组b 在用IntToHex()和循环语句转换后存入字符串数组s 再存入一个文本文件。注意!!循环中尽量用指针
声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。
E-MAIL:11247931@qq.com