Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Unicode Ersatz für Format (https://www.delphipraxis.net/90206-unicode-ersatz-fuer-format.html)

Luckie 13. Apr 2007 12:21


Unicode Ersatz für Format
 
Ich benutze folgende Funktion als Ersatz für die Funktion Format:
Delphi-Quellcode:
function FormatW(fmt: WideString; params: array of const): WideString;
var
  pdw1, pdw2        : PDWORD;
  i                : integer;
  pc               : PWIDECHAR;
begin
  pdw1 := nil;
  if length(params) > 0 then
    GetMem(pdw1, length(params) * sizeof(Pointer));
  pdw2 := pdw1;
  for i := 0 to high(params) do
  begin
    pdw2^ := DWORD(PDWORD(@params[i])^);
    inc(pdw2);
  end;
  GetMem(pc, length(params) * sizeof(Pointer));
  try
    ZeroMemory(pc, length(params) * sizeof(Pointer));
    SetString(Result, pc, wvsprintfW(pc, PWIDECHAR(fmt), PCHAR(pdw1)));
  except
    Result := '';
  end;
  if (pdw1 <> nil) then
    FreeMem(pdw1);
  if (pc <> nil) then
    FreeMem(pc);
end;
Allerdings bekomme ich da hin und wieder eine "invalid pointer operation" in der Zeile FreeMem(pdw1);. Ich kann das jetzt auch nicht genau sagen unter welchen Bedingungen sie auftritt.

Sieht da jemand einen Fehler in der Funktion oder kann mit einer alternativen Funktion aufwarten?

Bernhard Geyer 13. Apr 2007 12:31

Re: Unicode Ersatz für Format
 
Eigentlich gibt es WideFormat bei Delphi aber diese kommt bei großen (Format-) Strings durcheinandern.
Ich verwende als Ersatz TNTSysUtils.Tnt_WideFormat

himitsu 13. Apr 2007 12:33

Re: Unicode Ersatz für Format
 
Delphi-Quellcode:
GetMem(pc, length(params) * sizeof(Pointer));
try
  ZeroMemory(pc, length(params) * sizeof(Pointer));
  SetString(Result, pc, wvsprintfW(pc, PWIDECHAR(fmt), PCHAR(pdw1)));
pc soll wohl der Speicher für den (Rückgabe)String sein?

ich denk mal, da stimmt dann was mit der länge nicht :stupid:
1 parameter = 4 Byte ... ich glaub da darf man ruhig sagen "ein Glück daß es nicht öfters kracht"


hab grad das erfolgreich im Einsatz:
Delphi-Quellcode:
Function Format(Msg: LongWord; Const Args: Array of Const): WideString;
  Var StrBuffer, StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of LongWord;
    i: Integer;

  Begin
    LoadStringW(HInstance, Msg, @StrBuffer, Length(StrBuffer));
    For i := High(Args) downto 0 do A[i] := Args[i].VInteger;
    wvsprintfW(@StrBuffer2, @StrBuffer, @A);
    Result := PWideChar(@StrBuffer2);
  End;

//ich hoff grad so müßte es dann mit 'nem String aussehn
Function Format(Const S: WideString Const Args: Array of Const): WideString;
  Var StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of LongWord;
    i: Integer;

  Begin
    For i := High(Args) downto 0 do A[i] := Args[i].VInteger;
    wvsprintfW(@StrBuffer2, PWideChar(S), @A);
    Result := PWideChar(@StrBuffer2);
  End;
ok, die Arraylängen kannst dir ja noch dynamisch umbauen, aber ansonsten reicht es doch?
(und Delphi's Format hat doch auch nur einen statischen Speicher drin)

Zitat:

Zitat von Bernhard Geyer
Eigentlich gibt es WideFormat bei Delphi aber diese kommt bei großen (Format-) Strings durcheinandern.

die Länge ist halt aufgrund des statischen Speichers begrenzt :angel2:

Luckie 13. Apr 2007 12:38

Re: Unicode Ersatz für Format
 
Oh, die kannte ich noch gar nicht. Danke für den Hinweis. Hier in diesem Projekt kann ich das benutzen, allerdings bräuchte ich das auch manchmal in nonVCL Anwendungen, deswegen, wäre es schon gut, wenn ich die obige Funktion stabil zum Laufenbringen könnte.

Da war wieder jemand schneller. Dank dir. Und mehr als 1024 Parameter wird man wohl kaum formatieren wollen. ;)

Robert Marquardt 13. Apr 2007 13:26

Re: Unicode Ersatz für Format
 
GetMem(pc, length(params) * sizeof(Pointer));

pc ist doch der Puffer in den der ganze String per wvsprintfW zusammengebaut wird. Selbiger sollte 1024 Zeichen (also hier WideChar) gross sein. wvsprintf ist dokumentiert mit dieser Puffergroesse. Du alloziierst 2 Zeichen pro Parameter (sizeof(Pointer) = 4 = 2 WideChar). Das ist einfach Unsinn.

Luckie 13. Apr 2007 13:28

Re: Unicode Ersatz für Format
 
Wie müsste es denn lauten? BTW ist die statische Alternative auch in Ordnung.

Robert Marquardt 13. Apr 2007 13:36

Re: Unicode Ersatz für Format
 
Die lokale Puffer-Version von Himitsu ist auch in Ordnung. alles was du brauchst ist schliesslich Puffer geeigneter Groesse.
Ansonsten GetMem(pc, 1024*SizeOf(WideChar));

Luckie 13. Apr 2007 13:39

Re: Unicode Ersatz für Format
 
Ah, gut. Das wollte ich nur wissen.

himitsu 13. Apr 2007 13:44

Re: Unicode Ersatz für Format
 
Was auch ginge, wäre einen String mit genügend Länge zu reservieren und dann direkt da reinzuschreiben ... sowas gefällt mir auch immer (ersparrt einem ebenfall dieses ganze Try-Finally-Zeugs, da sich Delphi drum kümmert)
Code:
Function Format(Const S: WideString Const Args: Array of Const): WideString;
  Var A: Array[0..15] of LongWord;
    i: Integer;

  Begin
    For i := High(Args) downto 0 do A[i] := Args[i].VInteger;
    [color=#ff0000]SetLength(Result, 1024);
    SetLength(Result, wvsprintfW(PWideChar(Result), PWideChar(S), @A));[/color]
  End;
SetLength würde da sozusagen dem GetMem entsprechen, wobei sich ja Delphi um die Freigabe des String kümmert ... auch bei 'ner Exception.

xZise 29. Okt 2007 14:58

Re: Unicode Ersatz für Format
 
Gibt es denn inzwischen eine erfolgreiche Umsetzung ins WideString?
Das scheint ja nur Integer abzubekommen:
Zitat:

Zitat von himitsu
Delphi-Quellcode:
Function Format(Msg: LongWord; Const Args: Array of Const): WideString;
  Var StrBuffer, StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of LongWord;
    i: Integer;

  Begin
    LoadStringW(HInstance, Msg, @StrBuffer, Length(StrBuffer));
    For i := High(Args) downto 0 do A[i] := Args[i].VInteger;
    wvsprintfW(@StrBuffer2, @StrBuffer, @A);
    Result := PWideChar(@StrBuffer2);
  End;

//ich hoff grad so müßte es dann mit 'nem String aussehn
Function Format(Const S: WideString Const Args: Array of Const): WideString;
  Var StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of LongWord;
    i: Integer;

  Begin
    For i := High(Args) downto 0 do A[i] := Args[i].VInteger;
    wvsprintfW(@StrBuffer2, PWideChar(S), @A);
    Result := PWideChar(@StrBuffer2);
  End;


himitsu 29. Okt 2007 22:10

Re: Unicode Ersatz für Format
 
dieses VInteger ist nur etwas irreführend :roll:

da ist einfach nur entweder der Dateninhalt oder ein Zeiger auf den Dateninhalt (z.B. bei String) des entsprechenden Parameters drin.

PS: schau mal nach "Variante offene Array-Parameter" oder einfach mal wie TVarRec definiert ist :zwinker:



was tatsächlich möglich ist, das bestimmt nur MSDN-Library durchsuchenwvsprintf.

xZise 30. Okt 2007 15:57

Re: Unicode Ersatz für Format
 
Also ich bekomme eine Menge Japanischer (oder irgendwas anderes Ostasiatisches) Zeichen.

himitsu 30. Okt 2007 18:48

Re: Unicode Ersatz für Format
 
Kryptische Zeichen?

dann läuft was falsch ... wie sieht denn deine Funktion und der Aufruf aus?
diese Funktionen sind Unicode, also falls bei dir was mit ANSI ist, dann liegt es wohl daran.


wegen dem Int64:
diese Funktion kennt nichts über 32 Byte (bis auf Strings, wo die Zeiger aber auch 32-Bit sind)
konnte jedenfalls keine möglichen Paramerte für solche Datentypen entdecken.

hab hier aber mal den Parameter %q für einen 64-Bit-Integer eingeführt:
Delphi-Quellcode:
Function Format(S: WideString; Const Args: Array of Const): WideString;
  Var StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of Integer;
    i, i2: Integer;
    i8: Int64;

  Begin
    // aufpassen!!! es wird eine unicodefähige StringReplace-Variante benötigt.
    S := StringReplace(S, '%q', '%d%09d', [rfReplaceAll]);
    i2 := 0;
    For i := 0 to High(Args) do
      If Args[i].VType = vtInt64 Then Begin
        i8 := Args[i].VInt64^;
        A[i2] := i8 div 1000000000;
        If i8 < 0 Then i8 := -i8;
        A[i2 + 1] := i8 mod 1000000000;
        Inc(i2, 2);
      End Else Begin
        A[i2] := Args[i].VInteger;
        Inc(i2);
      End;
    wvsprintfW(@StrBuffer2, PWideChar(S), @A);
    Result := PWideChar(@StrBuffer2);
  End;

procedure TForm1.FormCreate(Sender: TObject);
var i: int64;
begin
  i := 2345678901234567890;
  Caption := Format('%q %q', [1234567890123456789, i]);
end;
hier also nochmal die wichtigsten Parameter
%d integer
%q int64
%u cardinal
%s WideString
%S AnsiString/String
%x lower(hex)
%X upper(hex)

und der Ausgabestring darf maximal 1023 Zeichen lang sein.

für genauere Definitionen zu den Standardtypen siehe MSDN.

xZise 30. Okt 2007 21:55

Re: Unicode Ersatz für Format
 
Der Aufruf sieht folgendermaßen aus:
Delphi-Quellcode:
MessageBoxW(hDPM, PWideChar(Format(_('Error while downloading and processing updateinformation.' + #13#10 + 'Message: %s' + #13#10 + 'Errorclass: %s'), [e.Message, e.ClassName])), PWideChar(_('[Update Plugin] Downloading/Processingerror')), MB_OK or MB_ICONERROR);
Selbst nativ (also ohne Übersetzung):
Zitat:

---------------------------
[Update Plugin] Downloading/Processingerror
---------------------------
Error while downloading and processing updateinformation.

Message: Ä
溡Ȇ

Errorclass: 䔉捸灥楴湯
---------------------------
OK
---------------------------

Khabarakh 30. Okt 2007 22:04

Re: Unicode Ersatz für Format
 
e ist doch sicherlich eine stino Exception. Könnte es in diesem Falle vielleicht keine ganz so dumme Idee sein, einer Unicode-Funktion auch Unicode-String zu übergeben... ?
Delphi-Quellcode:
  Writeln(Format('Hello %s', ['World'])); // 'Hello ??d'
  Writeln(Format('Hello %S', ['World'])); // 'Hello World'
  Writeln(Format('Hello %s', [WideString('World')])); // 'Hello World'

himitsu 30. Okt 2007 22:45

Re: Unicode Ersatz für Format
 
%s & %S ... siehe MSDN

in Deutsch:
%s = selber Stringtyp (Ansi/Wide), wie die Aufzurufende Funktion
%S = anderes Stringformat

also bei wvsprintfW %s für PWideChar/WideString und %S für P(Ansi)Char/(Ansi)String
und bei wvsprintf/wvsprintfA %s für P(Ansi)Char/(Ansi)String und %S für PWideChar/WideString

Delphi-Quellcode:
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %S'#13#10'Errorclass: %S', [e.Message, e.ClassName])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

// dieses _( ) macht 'ne Typenumwandlung, oder wie?
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %s'#13#10'Errorclass: %s', [_(e.Message), _(e.ClassName)])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

turboPASCAL 31. Okt 2007 06:27

Re: Unicode Ersatz für Format
 
Ich hätte da noch eine kleine Frage, passt ja zum Thema.
wvsprintf kann nur Integer aber keine Floads oder ?

himitsu 31. Okt 2007 07:43

Re: Unicode Ersatz für Format
 
nicht nur, aber keine reelen Typen

wenn man jetzt nicht auf die API versichtet und alles selbermachen will,
dann muß man entweder mit dem Fehlen einiger Formate leben, oder man muß versuchen den gewünschten Typen in die Grundtypen zu zerlegen (Bsp. siehe 1) oder muß diesen Typen selber z.B in einen String umwandeln (Bsp. siehe 2).
OK und man kann natürlich auch eine der vielen "fertigen" Unicodesammlungen verwenden. :angel:

1:
Delphi-Quellcode:
Function Format(S: WideString; Const Args: Array of Const): WideString;
  Var StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of Integer;
    i, i2: Integer;
    i8: Int64;

  Begin
    // aufpassen!!! es wird eine unicodefähige StringReplace-Variante benötigt.
    S := StringReplace(S, '%q', '%d%09d', [rfReplaceAll]);
    S := StringReplace(S, '%F', '%d%09d,%09d', [rfReplaceAll]);
    i2 := 0;
    For i := 0 to High(Args) do
      Case Args[i].VType of
        vtInt64: Begin
          i8 := Args[i].VInt64^;
          A[i2] := i8 div 1000000000;
          If i8 < 0 Then i8 := -i8;
          A[i2 + 1] := i8 mod 1000000000;
          Inc(i2, 2);
        End;
        vtExtended: Begin
          i8 := Trunc(Args[i].VExtended^);
          A[i2] := i8 div 1000000000;
          If i8 < 0 Then i8 := -i8;
          A[i2 + 1] := i8 mod 1000000000;
          A[i2 + 2] := Trunc(Args[i].VExtended^ * 1000000000);
          Inc(i2, 3);
        End;
        Else Begin
          A[i2] := Args[i].VInteger;
          Inc(i2);
        End;
      End;
    wvsprintfW(@StrBuffer2, PWideChar(S), @A);
    Result := PWideChar(@StrBuffer2);
  End;
2:
Delphi-Quellcode:
Function Format(S: WideString; Const Args: Array of Const): WideString;
  Var StrBuffer2: Array[0..1023] of WideChar;
    A: Array[0..15] of Integer;
    T: Array[0..15] of String;
    i: Integer;

  Begin
    // aufpassen!!! es wird eine unicodefähige StringReplace-Variante benötigt.
    S := StringReplace(S, '%q', '%S', [rfReplaceAll]);
    S := StringReplace(S, '%f', '%S', [rfReplaceAll]);
    For i := High(Args) downto 0 do
      Case Args[i].VType of
        vtInt64: Begin
          T[i] := IntToStr(Args[i].VInt64^);
          A[i] := PChar(T[i]);
        End;
        vtExtended: Begin
          T[i] := FloatToStr(Args[i].VInt64^);
          A[i] := PChar(T[i]);
        End;
        Else A[i] := Args[i].VInteger;
      End;
    wvsprintfW(@StrBuffer2, PWideChar(S), @A);
    Result := PWideChar(@StrBuffer2);
  End;

xZise 1. Nov 2007 16:00

Re: Unicode Ersatz für Format
 
Zitat:

Zitat von himitsu
in Deutsch:
%s = selber Stringtyp (Ansi/Wide), wie die Aufzurufende Funktion
%S = anderes Stringformat

Ah danke :)

Zitat:

Zitat von himitsu
Delphi-Quellcode:
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %S'#13#10'Errorclass: %S', [e.Message, e.ClassName])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

// dieses _( ) macht 'ne Typenumwandlung, oder wie?
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %s'#13#10'Errorclass: %s', [_(e.Message), _(e.ClassName)])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

Ersteres übersetzt den String nicht, und zweiteres ist etwas seltsam ;)
_() macht eine Übersetzung (siehe GnuGettext)

MfG
xZise

himitsu 12. Nov 2007 09:38

Re: Unicode Ersatz für Format
 
asoooo, dacht schon des se wieder irgend so'n neues Delphifeature :lol:

dann natürlich so :angel:
Delphi-Quellcode:
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %S'#13#10'Errorclass: %S', [_(e.Message), _(e.ClassName_)])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %s'#13#10'Errorclass: %s', [WideString(_(e.Message)), WideString(_(e.ClassName_))])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

xZise 13. Nov 2007 09:26

Re: Unicode Ersatz für Format
 
Zitat:

Zitat von himitsu
dann natürlich so :angel:

Du weißt wohl nicht wie das geht :)

Delphi-Quellcode:
Format(_('string %s'), [e.Message]);
Ich muss zuerst einen String mit Platzhaltern übersetzt werden (_())! Dann die Variablen einsetzen (Format) und dann in WideString umwandeln!
Der letzte Schritt ist das Problem.

:)

Dezipaitor 13. Nov 2007 14:30

Re: Unicode Ersatz für Format
 
Zitat:
Delphi-Quellcode:
MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %S'#13#10'Errorclass: %S', [_(e.Message), _(e.ClassName_)])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);

MessageBoxW(hDPM, PWideChar(Format('Error while downloading and processing updateinformation.'
  + #13#10'Message: %s'#13#10'Errorclass: %s', [WideString(_(e.Message)), WideString(_(e.ClassName_))])),
  '[Update Plugin] Downloading/Processingerror', MB_OK or MB_ICONERROR);
Wie wärs, wenn die Formatfunktion auch noch C-Formatierungssymbole erkennen würde? z.b. \r\n für Windows Zeilenumbruch, statt #13#10.
Der obige Code ist meiner Meinung nach nicht wirklich toll.

himitsu 14. Nov 2007 14:55

Re: Unicode Ersatz für Format
 
Zitat:

Zitat von xZise
Du weißt wohl nicht wie das geht :)
Delphi-Quellcode:
Format(_('string %s'), [e.Message]);

oh, hast ja Recht (wörter im Formatstring könnte man och mit übersetzen, :oops:
aber dann wäre beides FormatString und das einzufügende wohl nicht schlecht.

Delphi-Quellcode:
Format(_('string %s'), [_(e.Message)]);
// oder alles zusammen
_(Format('string %s', [e.Message]));
aber mal 'ne Frage, diese _(...) arbeitet doch nur mit Strings/AnsiStrings?
da würde es doch eigentlih alle UnicodeInformationen zerstören,
weshalb man gleich bei delphi's Format-Version (Ansi) bleiben könnte. :gruebel:

xZise 22. Dez 2007 21:47

Re: Unicode Ersatz für Format
 
Zitat:

Zitat von himitsu
Delphi-Quellcode:
Format(_('string %s'), [_(e.Message)]);
// oder alles zusammen
_(Format('string %s', [e.Message]));
aber mal 'ne Frage, diese _(...) arbeitet doch nur mit Strings/AnsiStrings?
da würde es doch eigentlih alle UnicodeInformationen zerstören,
weshalb man gleich bei delphi's Format-Version (Ansi) bleiben könnte. :gruebel:

So :)
Das scheint hier wohl noch ein paar Probleme zu geben!
1. _(<string>) übersetzt einen String (z.B. <string>="Hallo Welt"; Dann guckt er in der Datei nach "Hallo Welt" und würde die jetzt in "Hello World" übersetzten).
2. _() Arbeitet mit WideStrings! (Ein/Ausgabe)

Zu den Ersten:
Das kann er übersetzen:
"Zahl: %d" => "Number %d" => Format(...) Erfolg
Wenn ich aber das so wie dein zweiten Vorschlag machen würde:
"Zahl: %d" => Format(...) => "Zahl: 123456789" => Kein Erfolg
Deshalb muss ich irgendwie Format auf WideStrings anwenden.

MfG
xZise

xZise 30. Mär 2008 16:51

Re: Unicode Ersatz für Format
 
Also irgendwie will das nicht funktionieren!
Gibt es denn keine Möglichkeit dieses Format auch für WideStrings hinzubekommen?

Irgendwie scheinen die Übergabeparameter nicht ganz zu stimmen:
Delphi-Quellcode:
Format(_(<string>), [<string>]);
Er übersetzt das zwar, setzt aber anstelle des Strings irgendwelche komischen (japanisch/chinesisch?) Zeichen ein.
Auch das die Bytereihenfolge falsch ist, stimmt nicht, da ein swapen der Reihenfolge des Strings nichts bringt!

[edit=1]ICH HABE ES :)
Es funktioniert wenn ich statt "<string>" als Variable "WideString(<string>)" einsetze.[/edit]

MfG
xZise


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz