Delphi7、TFileStream打不开特殊字符文件
Delphi 7, TFileStream cant open files with special characters
这一行:
TFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
如果文件名包含类似 ñ
的内容则抛出异常
您最终调用 CreateFileA
,ANSI API,并且您使用的字符没有 ANSI 编码。 唯一 方法是使用 CreateFileW
、Unicode API 打开文件。
您可能没有意识到您调用了 CreateFileA
,但这就是 Delphi 7 文件流的实现方式。
解决问题的一个简单方法是升级到最新的 Delphi,它对本机 Windows Unicode API 有很好的支持。
如果您受困于 ANSI Delphi,那么您仍然需要调用 CreateFileW
。您可以这样做来创建文件句柄。您需要将 UTF-16 字符串传递给 API。使用WideString
来存储它。您还需要以 UTF-16 格式从用户处获取文件名。这意味着调用 GetOpenFileNameW
或 IFileDialog
。通过将文件句柄传递给 THandleStream
创建一个流。
要使这一切成为可能,您需要使用 TNT Unicode 库。他们工作得很好,但会给你强加一个很大的端口。
坦率地说,正确的前进方向是使用支持 Unicode 的现代工具。
您可以使用 TntUnicode 单元在 Delphi 7 下获得 UTF8 支持。
将 TntClasses 添加到您的 Use 中并像这样进行调用:
TTntFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
确保文件名是宽字符串。
在这里您可以获得一份 TntUnicode:
https://github.com/rofl0r/TntUnicode
UTF16 可以被认为是一个代码页,就像所有可能的 ANSI 代码页一样。
正如雷米在他的评论中提到的那样,假设您的 ANSI 代码页支持 Unicode 字符串中所需的字符,您只需将该字符串的 Unicode 版本转换为等效的 ANSI 代码页版本。
Delphi 编译器可以自动为您进行简单的转换,您只需将 WIDEString (UTF16) 转换为 (ANSI)字符串:
const
WIDE_FILENAME : WIDEString = 'fuññy.txt';
var
sFilename: String;
strm: TFileStream;
begin
sFilename := String(WIDE_FILENAME);
strm := TFileStream.Create(sFilename, fmOpenRead);
// etc
end;
即使在(例如)Delphi 7 上也能很好地工作。唯一需要注意的是,涉及的代码页(系统默认)必须支持 Unicode 字符串中的扩展字符。
注意: 上面的代码使用了 String 类型而不是显式的 ANSIString。在 String 为 ANSIString 的 Delphi 版本上,这具有所需的效果,但也可移植到 String 的版本 是 UnicodeString (以后要不要升级版本)。
如果在这种情况下显式使用 ANSIString,结果将是 double 转换 if/when 您升级:
// Unicode compiler using ANSIString type....
var
sFilename: ANSIString;
begin
sFilename := ANSIString(WIDE_FILENAME); // Codepage conversion from UTF16 to ANSI
strm := TFileStream.Create(sFilename, fmOpenRead); // Will implicitly convert *back* from ANSI to WIDE
对比
// Unicode compiler using String type....
var
sFilename: String;
begin
sFilename := String(WIDE_FILENAME); // String type conversion from WideString to UnicodeString
strm := TFileStream.Create(sFilename, fmOpenRead); // No further conversion necessary
最好的解决方案是使用 Unicode,但如果这不是一个选项,您仍然可以解决问题。
在 Windows 中,您可以设置非 Unicode 程序使用的代码页。只需更改它以支持正确的语言(西班牙语?)。然后代码应该可以工作。
Windows 7
:控制面板 > 区域和语言 > 管理 > 非 Unicode 程序的语言
Windows XP
:控制面板 > 区域和语言 > 高级 > 非 Unicode 程序的语言
这一行:
TFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
如果文件名包含类似 ñ
的内容则抛出异常您最终调用 CreateFileA
,ANSI API,并且您使用的字符没有 ANSI 编码。 唯一 方法是使用 CreateFileW
、Unicode API 打开文件。
您可能没有意识到您调用了 CreateFileA
,但这就是 Delphi 7 文件流的实现方式。
解决问题的一个简单方法是升级到最新的 Delphi,它对本机 Windows Unicode API 有很好的支持。
如果您受困于 ANSI Delphi,那么您仍然需要调用 CreateFileW
。您可以这样做来创建文件句柄。您需要将 UTF-16 字符串传递给 API。使用WideString
来存储它。您还需要以 UTF-16 格式从用户处获取文件名。这意味着调用 GetOpenFileNameW
或 IFileDialog
。通过将文件句柄传递给 THandleStream
创建一个流。
要使这一切成为可能,您需要使用 TNT Unicode 库。他们工作得很好,但会给你强加一个很大的端口。
坦率地说,正确的前进方向是使用支持 Unicode 的现代工具。
您可以使用 TntUnicode 单元在 Delphi 7 下获得 UTF8 支持。 将 TntClasses 添加到您的 Use 中并像这样进行调用:
TTntFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
确保文件名是宽字符串。
在这里您可以获得一份 TntUnicode: https://github.com/rofl0r/TntUnicode
UTF16 可以被认为是一个代码页,就像所有可能的 ANSI 代码页一样。
正如雷米在他的评论中提到的那样,假设您的 ANSI 代码页支持 Unicode 字符串中所需的字符,您只需将该字符串的 Unicode 版本转换为等效的 ANSI 代码页版本。
Delphi 编译器可以自动为您进行简单的转换,您只需将 WIDEString (UTF16) 转换为 (ANSI)字符串:
const
WIDE_FILENAME : WIDEString = 'fuññy.txt';
var
sFilename: String;
strm: TFileStream;
begin
sFilename := String(WIDE_FILENAME);
strm := TFileStream.Create(sFilename, fmOpenRead);
// etc
end;
即使在(例如)Delphi 7 上也能很好地工作。唯一需要注意的是,涉及的代码页(系统默认)必须支持 Unicode 字符串中的扩展字符。
注意: 上面的代码使用了 String 类型而不是显式的 ANSIString。在 String 为 ANSIString 的 Delphi 版本上,这具有所需的效果,但也可移植到 String 的版本 是 UnicodeString (以后要不要升级版本)。
如果在这种情况下显式使用 ANSIString,结果将是 double 转换 if/when 您升级:
// Unicode compiler using ANSIString type....
var
sFilename: ANSIString;
begin
sFilename := ANSIString(WIDE_FILENAME); // Codepage conversion from UTF16 to ANSI
strm := TFileStream.Create(sFilename, fmOpenRead); // Will implicitly convert *back* from ANSI to WIDE
对比
// Unicode compiler using String type....
var
sFilename: String;
begin
sFilename := String(WIDE_FILENAME); // String type conversion from WideString to UnicodeString
strm := TFileStream.Create(sFilename, fmOpenRead); // No further conversion necessary
最好的解决方案是使用 Unicode,但如果这不是一个选项,您仍然可以解决问题。
在 Windows 中,您可以设置非 Unicode 程序使用的代码页。只需更改它以支持正确的语言(西班牙语?)。然后代码应该可以工作。
Windows 7
:控制面板 > 区域和语言 > 管理 > 非 Unicode 程序的语言Windows XP
:控制面板 > 区域和语言 > 高级 > 非 Unicode 程序的语言