You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi7非Unicode应用的TXT编码检测与转ANSI功能需求

实现Delphi 7非Unicode应用的ReturnAsAnsiText函数

以下是满足需求的Delphi 7代码实现,严格遵循指定的检测与转换逻辑:

function ReturnAsAnsiText(const FileName: string): PAnsiChar;
const
  UTF8_BOM: array[0..2] of Byte = ($EF, $BB, $BF);
  UNICODE_LE_BOM: array[0..1] of Byte = ($FF, $FE);
  UNICODE_BE_BOM: array[0..1] of Byte = ($FE, $FF);
var
  FileStream: TFileStream;
  Buffer: array of Byte;
  HasNonAnsi: Boolean;
  I: Integer;
  WideStr: WideString;
  AnsiStr: AnsiString;
  CodePage: UINT;
begin
  Result := nil;
  // 获取系统当前ANSI代码页(对应控制面板区域设置)
  CodePage := GetACP();

  try
    // 读取文件到字节缓冲区
    FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    try
      SetLength(Buffer, FileStream.Size);
      if FileStream.Size > 0 then
        FileStream.ReadBuffer(Buffer[0], FileStream.Size);
    finally
      FileStream.Free;
    end;

    // 步骤1:检测是否为纯ANSI(无大于0x7F的字节)
    HasNonAnsi := False;
    for I := 0 to Length(Buffer) - 1 do
    begin
      if Buffer[I] > $7F then
      begin
        HasNonAnsi := True;
        Break;
      end;
    end;

    if not HasNonAnsi then
    begin
      // 直接返回ANSI内容
      SetString(AnsiStr, PAnsiChar(@Buffer[0]), Length(Buffer));
      Result := StrNew(PAnsiChar(AnsiStr));
      Exit;
    end;

    // 步骤3:先检测BOM(优先处理带BOM的Unicode文件)
    if Length(Buffer) >= 3 then
    begin
      if (Buffer[0] = UTF8_BOM[0]) and (Buffer[1] = UTF8_BOM[1]) and (Buffer[2] = UTF8_BOM[2]) then
      begin
        // UTF-8带BOM,跳过BOM后转换
        if MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[3]), Length(Buffer) - 3, nil, 0) > 0 then
        begin
          SetLength(WideStr, MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[3]), Length(Buffer) - 3, nil, 0));
          MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[3]), Length(Buffer) - 3, PWideChar(WideStr), Length(WideStr));
          // 转换为系统ANSI代码页
          if WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil) > 0 then
          begin
            SetLength(AnsiStr, WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil));
            WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), PAnsiChar(AnsiStr), Length(AnsiStr), nil, nil);
            Result := StrNew(PAnsiChar(AnsiStr));
            Exit;
          end;
        end;
      end;
    end;

    if Length(Buffer) >= 2 then
    begin
      // 检测UTF-16 LE BOM
      if (Buffer[0] = UNICODE_LE_BOM[0]) and (Buffer[1] = UNICODE_LE_BOM[1]) then
      begin
        if Length(Buffer) >= 2 then
        begin
          // 跳过BOM,按UTF-16 LE转换为WideString
          SetLength(WideStr, (Length(Buffer) - 2) div 2);
          Move(Buffer[2], WideStr[1], Length(Buffer) - 2);
          // 转换为系统ANSI代码页
          if WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil) > 0 then
          begin
            SetLength(AnsiStr, WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil));
            WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), PAnsiChar(AnsiStr), Length(AnsiStr), nil, nil);
            Result := StrNew(PAnsiChar(AnsiStr));
            Exit;
          end;
        end;
      end
      // 检测UTF-16 BE BOM
      else if (Buffer[0] = UNICODE_BE_BOM[0]) and (Buffer[1] = UNICODE_BE_BOM[1]) then
      begin
        if Length(Buffer) >= 2 then
        begin
          // 跳过BOM,转换BE到LE后生成WideString
          SetLength(WideStr, (Length(Buffer) - 2) div 2);
          for I := 0 to Length(WideStr) - 1 do
            WideStr[I+1] := WideChar(Buffer[2 + I*2 + 1] shl 8 or Buffer[2 + I*2]);
          // 转换为系统ANSI代码页
          if WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil) > 0 then
          begin
            SetLength(AnsiStr, WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil));
            WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), PAnsiChar(AnsiStr), Length(AnsiStr), nil, nil);
            Result := StrNew(PAnsiChar(AnsiStr));
            Exit;
          end;
        end;
      end;
    end;

    // 步骤2:尝试无BOM的UTF-8转换
    if MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[0]), Length(Buffer), nil, 0) > 0 then
    begin
      SetLength(WideStr, MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[0]), Length(Buffer), nil, 0));
      if MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(@Buffer[0]), Length(Buffer), PWideChar(WideStr), Length(WideStr)) > 0 then
      begin
        // 转换为系统ANSI代码页
        if WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil) > 0 then
        begin
          SetLength(AnsiStr, WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), nil, 0, nil, nil));
          WideCharToMultiByte(CodePage, 0, PWideChar(WideStr), Length(WideStr), PAnsiChar(AnsiStr), Length(AnsiStr), nil, nil);
          Result := StrNew(PAnsiChar(AnsiStr));
          Exit;
        end;
      end;
    end;

    // 所有转换失败,返回NULL
    Result := nil;
  except
    // 捕获文件读取等异常,返回NULL
    Result := nil;
  end;
end;

// 使用示例
procedure TForm1.Button1Click(Sender: TObject);
var
  AnsiText: PAnsiChar;
begin
  AnsiText := ReturnAsAnsiText('test.txt');
  if Assigned(AnsiText) then
  begin
    try
      Memo1.Text := AnsiText;
    finally
      StrDispose(AnsiText);
    end;
  end
  else
    ShowMessage('无法打开或转换文件,文件内容不符合当前系统代码页');
end;

代码说明

  • 步骤1实现:遍历文件字节流,判断是否存在大于0x7F的字节,无则直接返回原ANSI内容。
  • 步骤3实现:优先检测BOM标识,分别处理UTF-8 BOM、UTF-16 LE BOM和UTF-16 BE BOM,跳过BOM后转换为WideString,再转成系统当前ANSI代码页。
  • 步骤2实现:对无BOM但包含非ANSI字节的文件,尝试按UTF-8转换,再转成系统ANSI代码页。
  • 错误处理:任何转换失败或异常情况均返回nil,确保仅返回符合当前区域代码页的内容。

注意事项

  • 调用函数后需使用StrDispose释放返回的PAnsiChar内存,避免内存泄漏。
  • 系统代码页通过GetACP()获取,对应控制面板的「区域设置」中「非Unicode程序的语言」选项。

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:25:15