Writeln est-il capable de supporter Unicode?
estime que ce programme est:
{$APPTYPE CONSOLE}
begin
Writeln('АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ');
end.
la sortie sur ma console qui utilise la police Consolas est:
????????Z??????????????????????????????????????
la console Windows est tout à fait capable de supporter Unicode comme en témoigne ce programme:
{$APPTYPE CONSOLE}
uses
Winapi.Windows;
const
Text = 'АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ';
var
NumWritten: DWORD;
begin
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(Text), Length(Text), NumWritten, nil);
end.
pour lequel la sortie est:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
peut-on convaincre Writeln
de respecter Unicode, ou est-il intrinsèquement infirme?
3 réponses
vient de définir le codepage de sortie de la console par le SetConsoleOutputCP()
routine avec codepage 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.
sorties:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
WriteLn()
traduit en interne les chaînes Unicode UTF16 en codepage de sortie sélectionné (cp_UTF8).
mise à Jour:
Les travaux ci-dessus dans Delphi XE2, et au-dessus. Dans Delphi-XE vous avez besoin d'une conversion explicite en UTF-8 pour le faire fonctionner correctement.
WriteLn(UTF8String('АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ'));
Addendum:
si une sortie vers la console est effectuée dans une autre page de code avant d'appeler SetConsoleOutputCP(cp_UTF8)
,
le système d'exploitation ne produira pas correctement le texte de utf-8
.
Ceci peut être corrigé en fermant/rouvrant le handler stdout.
une autre option consiste à déclarer un nouveau gestionnaire de sortie de texte pour utf-8
.
var
toutUTF8: TextFile;
...
SetConsoleOutputCP(CP_UTF8);
AssignFile(toutUTF8,'',cp_UTF8); // Works in XE2 and above
Rewrite(toutUTF8);
WriteLn(toutUTF8,'АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ');
l'unité System
déclare une variable nommée AlternateWriteUnicodeStringProc
qui permet de personnaliser la façon dont Writeln
exécute la sortie. Ce programme:
{$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.
produit ce résultat:
АБВГДЕЖЅZЗИІКЛМНОПҀРСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ
je suis sceptique quant à la façon dont j'ai implémenté MyAlternateWriteUnicodeStringProc
et comment il interagirait avec Pascal I/O. cependant, il semble se comporter comme souhaité pour la sortie vers la console.
la documentation de AlternateWriteUnicodeStringProc
actuellement dit, Attendez-le, ...
Amarcadero Technologies ne dispose actuellement d'aucune information supplémentaire. Aidez-nous à documenter ce sujet en utilisant la page de Discussion!
WriteConsoleW
semble être une fonction tout à fait magique.
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.
donc en résumé:
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...)
supporte UTF-16.
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), ...)
ne supporte pas L'UTF-16.
je pense que pour supporter différents encodages ANSI le classique Pascal I/O utilise l'appel WriteFile
.
gardez également à l'esprit que lorsqu'il est utilisé sur un fichier au lieu de la console il doit fonctionner aussi bien:
la sortie du fichier texte unicode diffère-t-elle entre XE2 et Delphi 2009?
cela signifie que l'utilisation aveugle de WriteConsole
interrompt la redirection de sortie. Si vous utilisez WriteConsole
vous devez revenir à WriteFile
comme ceci:
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;
notez que la redirection de sortie avec n'importe quel encodage fonctionne très bien dans cmd.exe
. Il a juste écrit la flux de sortie vers le fichier inchangé.
PowerShell s'attend toutefois à ce que soit sortie ANSI ou le préambule correct (/BOM) doit être inclus au début de la sortie (ou le fichier sera malencodé!). Aussi PowerShell convertira toujours la sortie en UTF-16 avec préambule.
MSDN recommande en utilisant GetConsoleMode
pour savoir si la poignée standard est une poignée de console, aussi le BOM est mentionné:
WriteConsole échoue si elle est utilisée avec une poignée standard qui est redirigée vers un fichier. Si une application traite une sortie multilingue qui peut être redirigé, déterminer si la poignée de sortie est un manette de la console (une méthode consiste à appeler la fonction GetConsoleMode et vérifiez si il y arrive). Si la poignée est une poignée de console, appeler WriteConsole. Si la poignée n'est pas une console de poignée, la sortie est redirigé et vous devrait appeler WriteFile pour effectuer l'entrée / sortie. préfixe Unicode fichier texte avec une marque d'ordre d'octet. Pour plus d' information, voir utilisation des marques D'ordre de Octet.