admin

[系统应用] delphi 转换二进制文件为txt文本格式十六进制

https://zhidao.baidu.com/question/198949958.html


program bin2hex;

{$APPTYPE CONSOLE}

uses Classes, SysUtils, DateUtils;

procedure ShowUsageExit;
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
             EAX, $FF
        MOV     EDX, EAX
        SHR     EAX, 4
             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;

// 输出一个任务的耗时
procedure 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;

procedure 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.


#1楼
发帖时间:5月前   |   查看数:0   |   回复数:0
游客组