Dav*_*nan 25 windows delphi delphi-xe7
考虑这个程序:
{$APPTYPE CONSOLE}
begin
Writeln('????????Z??????????????????????????????????????');
end.
Run Code Online (Sandbox Code Playgroud)
我的控制台上使用Consolas字体的输出是:
????????Z??????????????????????????????????????
Windows控制台非常能够支持Unicode,如此程序所示:
{$APPTYPE CONSOLE}
uses
Winapi.Windows;
const
Text = '????????Z??????????????????????????????????????';
var
NumWritten: DWORD;
begin
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(Text), Length(Text), NumWritten, nil);
end.
Run Code Online (Sandbox Code Playgroud)
输出为:
????????Z??????????????????????????????????????
可以Writeln
说服尊重Unicode,还是它固有的残缺?
LU *_* RD 27
只需通过代码页设置控制台输出代码SetConsoleOutputCP()
页cp_UTF8
.
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils,Windows;
Const
Text = '????????Z??????????????????????????????????????';
VAR
NumWritten: DWORD;
begin
ReadLn; // Make sure Consolas font is selected
try
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(Text), Length(Text), NumWritten, nil);
SetConsoleOutputCP(CP_UTF8);
WriteLn;
WriteLn('????????Z??????????????????????????????????????');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
Run Code Online (Sandbox Code Playgroud)
输出:
????????Z??????????????????????????????????????
????????Z??????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
WriteLn()
在内部将Unicode UTF16字符串转换为选定的输出代码页(cp_UTF8).
更新:
以上工作在Delphi-XE2及以上版本.在Delphi-XE中,您需要显式转换为UTF-8才能使其正常工作.
WriteLn(UTF8String('????????Z??????????????????????????????????????'));
Run Code Online (Sandbox Code Playgroud)
附录:
如果在调用之前在另一个代码页中完成了对控制台的输出SetConsoleOutputCP(cp_UTF8)
,则操作系统将无法正确输出文本utf-8
.这可以通过关闭/重新打开stdout处理程序来解决.
另一种选择是声明一个新的文本输出处理程序utf-8
.
var
toutUTF8: TextFile;
...
SetConsoleOutputCP(CP_UTF8);
AssignFile(toutUTF8,'',cp_UTF8); // Works in XE2 and above
Rewrite(toutUTF8);
WriteLn(toutUTF8,'????????Z??????????????????????????????????????');
Run Code Online (Sandbox Code Playgroud)
Dav*_*nan 11
该System
单元声明一个名为的变量AlternateWriteUnicodeStringProc
,允许自定义如何Writeln
执行输出.这个程序:
{$APPTYPE CONSOLE}
uses
Winapi.Windows;
function MyAlternateWriteUnicodeStringProc(var t: TTextRec; s: UnicodeString): Pointer;
var
NumberOfCharsWritten, NumOfBytesWritten: DWORD;
begin
Result := @t;
if t.Handle = GetStdHandle(STD_OUTPUT_HANDLE) then
WriteConsole(t.Handle, Pointer(s), Length(s), NumberOfCharsWritten, nil)
else
WriteFile(t.Handle, Pointer(s)^, Length(s)*SizeOf(WideChar), NumOfBytesWritten, nil);
end;
var
UserFile: Text;
begin
AlternateWriteUnicodeStringProc := MyAlternateWriteUnicodeStringProc;
Writeln('????????Z??????????????????????????????????????');
Readln;
end.
Run Code Online (Sandbox Code Playgroud)
产生这个输出:
????????Z??????????????????????????????????????
我对我的实现MyAlternateWriteUnicodeStringProc
以及如何与经典的Pascal I/O进行交互持怀疑态度.但是,它似乎表现为输出到控制台所需的行为.
AlternateWriteUnicodeStringProc
目前的文档说,等待它,...
Embarcadero Technologies目前没有任何其他信息.请使用"讨论"页面帮助我们记录此主题!
WriteConsoleW
似乎是一个非常神奇的功能.
procedure WriteLnToConsoleUsingWriteFile(CP: Cardinal; AEncoding: TEncoding; const S: string);
var
Buffer: TBytes;
NumWritten: Cardinal;
begin
Buffer := AEncoding.GetBytes(S);
// This is a side effect and should be avoided ...
SetConsoleOutputCP(CP);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), Buffer[0], Length(Buffer), NumWritten, nil);
WriteLn;
end;
procedure WriteLnToConsoleUsingWriteConsole(const S: string);
var
NumWritten: Cardinal;
begin
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(S), Length(S), NumWritten, nil);
WriteLn;
end;
const
Text = '????????Z??????????????????????????????????????';
begin
ReadLn; // Make sure Consolas font is selected
// Works, but changing the console CP is neccessary
WriteLnToConsoleUsingWriteFile(CP_UTF8, TEncoding.UTF8, Text);
// Doesn't work
WriteLnToConsoleUsingWriteFile(1200, TEncoding.Unicode, Text);
// This does and doesn't need the CP anymore
WriteLnToConsoleUsingWriteConsole(Text);
ReadLn;
end.
Run Code Online (Sandbox Code Playgroud)
总结如下:
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...)
支持UTF-16.
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), ...)
不支持UTF-16.
我的猜测是,为了支持不同的ANSI编码,经典的Pascal I/O使用该WriteFile
调用.
还要记住,当在文件而不是控制台上使用它时,它也必须工作:
unicode文本文件输出在XE2和Delphi 2009之间有所不同?
这意味着盲目地使用WriteConsole
break输出重定向.如果你使用WriteConsole
你应该回到WriteFile
这样:
var
NumWritten: Cardinal;
Bytes: TBytes;
begin
if not WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(S), Length(S),
NumWritten, nil) then
begin
Bytes := TEncoding.UTF8.GetBytes(S);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), Bytes[0], Length(Bytes),
NumWritten, nil);
end;
WriteLn;
end;
Run Code Online (Sandbox Code Playgroud)
请注意,任何编码的输出重定向都可正常工作cmd.exe
.它只是将输出流写入文件不变.
但PowerShell 要求在输出开始时包含ANSI输出或正确的前导码(/ BOM)(或者文件将是malencoded!).此外,PowerShell将始终使用前导码将输出转换为UTF-16.
MSDN建议使用GetConsoleMode
以查明标准句柄是否为控制台句柄,还提到了BOM:
如果WriteConsole与重定向到文件的标准句柄一起使用,则会失败.如果应用程序处理可以重定向的多语言输出,请确定输出句柄是否为控制台句柄(一种方法是调用GetConsoleMode函数并检查它是否成功).如果句柄是控制台句柄,则调用WriteConsole.如果句柄不是控制台句柄,则输出被重定向,您应该调用WriteFile来执行I/O. 请务必在Unicode纯文本文件前加一个字节顺序标记.有关更多信息,请参阅使用字节顺序标记.
归档时间: |
|
查看次数: |
3098 次 |
最近记录: |