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
相关产品推荐
相关产品推荐

