windows - software - tag editor download
¿Writeln es capaz de soportar Unicode? (3)
Considera este programa:
{$APPTYPE CONSOLE}
begin
Writeln(''АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ'');
end.
La salida en mi consola que usa la fuente Consolas es:
????????Z??????????????????????????????????????
La consola de Windows es bastante capaz de soportar Unicode como lo demuestra este programa:
{$APPTYPE CONSOLE}
uses
Winapi.Windows;
const
Text = ''АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ'';
var
NumWritten: DWORD;
begin
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(Text), Length(Text), NumWritten, nil);
end.
para lo cual el resultado es:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
¿ Writeln
puede persuadir a Writeln
para que respete Unicode, o está Writeln
inutilizado?
La unidad del System
declara una variable llamada AlternateWriteUnicodeStringProc
que permite la personalización de cómo Writeln
realiza la salida. Este programa:
{$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.
produce esta salida:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
Soy escéptico sobre cómo he implementado MyAlternateWriteUnicodeStringProc
y cómo interactuaría con Pascal I / O clásico. Sin embargo, parece comportarse como se desea para la salida a la consola.
La documentación de AlternateWriteUnicodeStringProc
dice actualmente, espere, ...
Actualmente Embarcadero Technologies no tiene información adicional. ¡Por favor ayúdenos a documentar este tema usando la página de discusión!
Simplemente configure la página de códigos de salida de la consola a través de la rutina SetConsoleOutputCP()
con la página de códigos 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.
Productos:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
WriteLn()
traduce cadenas Unicode UTF16 a la página de códigos de salida seleccionada (cp_UTF8) internamente.
Actualizar:
Lo anterior funciona en Delphi-XE2 y superior. En Delphi-XE necesita una conversión explícita a UTF-8 para que funcione correctamente.
WriteLn(UTF8String(''АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ''));
Apéndice:
Si se realiza una salida a la consola en otra página de códigos antes de llamar a SetConsoleOutputCP(cp_UTF8)
, el sistema operativo no enviará correctamente el texto en utf-8
. Esto puede solucionarse cerrando / volviendo a abrir el controlador de stdout.
Otra opción es declarar un nuevo controlador de salida de texto para utf-8
.
var
toutUTF8: TextFile;
...
SetConsoleOutputCP(CP_UTF8);
AssignFile(toutUTF8,'''',cp_UTF8); // Works in XE2 and above
Rewrite(toutUTF8);
WriteLn(toutUTF8,''АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ'');
WriteConsoleW
parece ser una función bastante mágica.
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.
Entonces en resumen:
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...)
compatible con UTF-16.
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), ...)
no es compatible con UTF-16.
Mi suposición sería que para soportar diferentes codificaciones ANSI, la clásica Pascal I / O usa la llamada WriteFile
.
También tenga en cuenta que cuando se usa en un archivo en lugar de en la consola, también debe funcionar:
salida de archivo de texto unicode difiere entre XE2 y Delphi 2009?
Eso significa que el uso ciego de WriteConsole
rompe la redirección de salida. Si usa WriteConsole
, debe recurrir a WriteFile
siguiente manera:
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;
Tenga en cuenta que la redirección de salida con cualquier codificación funciona bien en cmd.exe
. Simplemente escribe la secuencia de salida al archivo sin cambios.
Sin embargo, PowerShell espera que la salida ANSI o el preámbulo correcto (/ BOM) se tengan que incluir al comienzo de la salida (¡o el archivo se malencinará!). Además, PowerShell siempre convertirá la salida en UTF-16 con preámbulo.
MSDN recomienda utilizar GetConsoleMode
para averiguar si el identificador estándar es un identificador de consola, también se menciona el BOM:
WriteConsole falla si se utiliza con un identificador estándar que se redirige a un archivo. Si una aplicación procesa resultados multilingües que pueden redirigirse, determine si el manejador de salida es un manejador de consola (un método es llamar a la función GetConsoleMode y verificar si tiene éxito). Si el asa es un asa de consola, llame a WriteConsole. Si el identificador no es un controlador de consola, la salida se redirige y debe llamar a WriteFile para realizar la E / S. Asegúrese de anteponer un archivo de texto sin formato Unicode con una marca de orden de bytes. Para obtener más información, vea Usar marcas de orden de bytes.