Discussion:
Problem mit Chars in String
(zu alt für eine Antwort)
stefan
2020-07-21 16:01:49 UTC
Permalink
Hallo,

ich hab jetzt kurz hintereinander bisher unbekannte Probleme mit Strings
gehabt. Das erste war allerdings nicht Delphi sondern Lazarus.

Ich wollte einen String über die serielle Schnittstelle zu einem
Etikettendrucker schicken. Dazu muss eine Kommandozeile zusammengesetzt
werden, ungefähr so:

TEXT 100,100,1,1,90,0,"TestText"

Dieser Text steht in einer Textdatei die in ein Memo gelesen wird. Die
Datei besteht aus mehreren Zeilen.

Auf die einzelnen Zeilen im Memo greife ich mit:

s := memo1.lines[i]

zu.

Dann übergebe ich den string s an eine Procedure, die das ganze dann an
den Drucker sendet.

Soweit funktioniert das alles. Jetzt hat der Drucker aber keine Umlaute,
jedenfalls nicht da wo sie sein sollten.

Ich hab den String dann umcodiert:

For i := 1 to length(s) do
begin
if s[i] = 'ä' then s[i] := #123;
if s[i] = 'ö' then s[i] := #124;
if s[i] = 'ü' then s[i] := #125;
... // ungefähr so,
end;

Hat unter Delphi 7 funktioniert.

Unter Lazarus nicht. Da kommt vor jedem Umlaut ein Zeichen #195 und
danach 186 o.ä.

Also jeder Umlaut wird im String durch 2 Bytes bzw. 2 Chars repräsentiert.

Also irgendwas mit Ansistring. Unter Turbo-Pascal war das alles noch
viel schöner, da wusste man noch, wie ein String im Speicher abgelegt
wird...

Also die Umkodierung entsprechend angepasst und die Character einzeln
zum Drucker geschickt und es funktioniert.

{$H+} war eingestellt, {H-} hat nicht groß was geändert.

Jedenfalls konnte ich meine Umlaute drucken. Soweit ok, schien ein
Problem von Lazarus zu sein.

Heute hatte ein Kollege ein ähnliches Problem, diesmal mit Delphi XE.

Zu einem Gerät sollte eine ESC-Sequenz geschickt werden, also:
<esc> 'OFF' + #13 + #10

also: s := #27 + 'OFF' + #13 + #10

Dann mit writecom(s) den String an die Schnittstellenroutine übergeben.

=> Ergebnis: Scheiße... Es kommt #27 + 'O' + #0 + 'F' + #0

Das zweite 'F' und #13+#10 werden verschluckt, statt dessen stehen da #0
zwischen den einzelnen Bytes.

Wenn wir die Character einzeln senden, funktioniert es:

writecom(#27);
writecom('O');
writecom('F');
writecom('F');
writecom(#13);
writecom(#10);

Eine Programmversion, die vor 2 Wochen mit demselben Rechner compiliert
wurde zeigt das Verhalten nicht. Der Kollege sagt, er habe nichts
verändert, keine Compilerschalter gesetzt o.ä.

Die Komponente hab ich selbst vor 20 Jahren geschrieben, seitdem tausend
mal eingesetzt und keine Probleme. Jetzt so ein curioses Verhalten.

Was mich wundert ist, dass die vor 2 Wochen compilierte Version das tut,
was sie soll.

==> hat irgendjemand eine Erklärung?
Sieghard Schicktanz
2020-07-21 19:52:38 UTC
Permalink
Hallo stefan,
Post by stefan
Ich wollte einen String über die serielle Schnittstelle zu einem
Etikettendrucker schicken. Dazu muss eine Kommandozeile zusammengesetzt
Sowas kann interessant werden...
...
Post by stefan
Soweit funktioniert das alles. Jetzt hat der Drucker aber keine Umlaute,
jedenfalls nicht da wo sie sein sollten.
Das ist dafür der übliche Ausgangspunkt.
...
Post by stefan
Hat unter Delphi 7 funktioniert.
Unter Lazarus nicht. Da kommt vor jedem Umlaut ein Zeichen #195 und
danach 186 o.ä.
Delphi 7 -> ASCII, ISO8859-* o.ä., Lazarus -> UTF-8
Post by stefan
Also jeder Umlaut wird im String durch 2 Bytes bzw. 2 Chars repräsentiert.
Ja, Unicode hat viel Probleme der alten "Code Pages" zu neuen gemacht.
Post by stefan
Also irgendwas mit Ansistring. Unter Turbo-Pascal war das alles noch
viel schöner, da wusste man noch, wie ein String im Speicher abgelegt
wird...
Das "weiß" man mit UTF-8 auch, aber wenn man die Kodierungen wild
durcheinanderwürfelt, geht da halt auch mal was durcheinander...

...
Post by stefan
Heute hatte ein Kollege ein ähnliches Problem, diesmal mit Delphi XE.
<esc> 'OFF' + #13 + #10
...
Post by stefan
Das zweite 'F' und #13+#10 werden verschluckt, statt dessen stehen da #0
zwischen den einzelnen Bytes.
(Neueres) Windows -> UTF16, d.h. "wide characters", also jedes Zeichen _2_
(_zwei_) Bytes, für einfache ASCII-Zeichen 1 Byte ASCII-Code + 1 Byte #0.
Post by stefan
writecom(#27);
"writecom" nimmt als Parameter "char"? Offensichtlich...
Post by stefan
Eine Programmversion, die vor 2 Wochen mit demselben Rechner compiliert
wurde zeigt das Verhalten nicht. Der Kollege sagt, er habe nichts
verändert, keine Compilerschalter gesetzt o.ä.
Nee, klar. Nur andere Ländereinstellung, Zeichensatzkodierung oder sowas?
Hat ja mit dem Compiler nix zu tun, ist ja nur das Betriebssystem...
Post by stefan
Die Komponente hab ich selbst vor 20 Jahren geschrieben, seitdem tausend
mal eingesetzt und keine Probleme. Jetzt so ein curioses Verhalten.
Damals war der ganze Verhau mit Unicode und "Code Pages" auch noch keiner,
man hat ASCII benutzt, Sonderzeichen nach eigener (Windows-) Manier kodiert
und pro Buchstaben genau 1 Byte benutzt. Heute...
Heute kann man sich nichtmal sicher sein, daß zwei Programm auf der
gelichen Maschine die gleiche Zeichenkodierung benutzen.
Post by stefan
Was mich wundert ist, dass die vor 2 Wochen compilierte Version das tut,
was sie soll.
==> hat irgendjemand eine Erklärung?
Nicht dafür, und auch nicht für die vielen Leerzeilen am Ende:
--
--
(Weitergabe von Adressdaten, Telefonnummern u.ä. ohne Zustimmung
nicht gestattet, ebenso Zusendung von Werbung oder ähnlichem)
-----------------------------------------------------------
Mit freundlichen Grüßen, S. Schicktanz
-----------------------------------------------------------
stefan
2020-07-22 05:13:57 UTC
Permalink
Post by Sieghard Schicktanz
Delphi 7 -> ASCII, ISO8859-* o.ä., Lazarus -> UTF-8
Post by stefan
Also jeder Umlaut wird im String durch 2 Bytes bzw. 2 Chars repräsentiert.
Ja, Unicode hat viel Probleme der alten "Code Pages" zu neuen gemacht.
Post by stefan
Also irgendwas mit Ansistring. Unter Turbo-Pascal war das alles noch
viel schöner, da wusste man noch, wie ein String im Speicher abgelegt
wird...
Das "weiß" man mit UTF-8 auch, aber wenn man die Kodierungen wild
durcheinanderwürfelt, geht da halt auch mal was durcheinander...
Problem ist, ich verwende die Strings um Telegramme zusammen zu bauen.
Nicht jetzt in dem aktuellen Projekt, aber bei anderen.
Da werden dann binäre Daten mit einem Header versehen, also

Startzeichen + Blocklänge + Daten + checksumme

Das wird Byte für Byte in einen string geschrieben und dann an die
Senderoutine übergeben. Hat bisher immer funktioniert. Nachdem, was ich
gestern gesehen habe, könnten wir da Probleme bekommen.
Post by Sieghard Schicktanz
Post by stefan
Heute hatte ein Kollege ein ähnliches Problem, diesmal mit Delphi XE.
<esc> 'OFF' + #13 + #10
...
Post by stefan
Das zweite 'F' und #13+#10 werden verschluckt, statt dessen stehen da #0
zwischen den einzelnen Bytes.
(Neueres) Windows -> UTF16, d.h. "wide characters", also jedes Zeichen _2_
(_zwei_) Bytes, für einfache ASCII-Zeichen 1 Byte ASCII-Code + 1 Byte #0.
Ja, so sah das aus. Wenn man sich darauf verlassen kann, dass da immer 2
Bytes stehen, könnte man damit ja leben...
Post by Sieghard Schicktanz
Post by stefan
writecom(#27);
"writecom" nimmt als Parameter "char"? Offensichtlich...
Nein, der Parameter ist ein String. Es wird aber auch ein Char
akzeptiert. Delphi wandelt den wohl in einen String mit der Länge 1 um.
Post by Sieghard Schicktanz
Post by stefan
Eine Programmversion, die vor 2 Wochen mit demselben Rechner compiliert
wurde zeigt das Verhalten nicht. Der Kollege sagt, er habe nichts
verändert, keine Compilerschalter gesetzt o.ä.
Nee, klar. Nur andere Ländereinstellung, Zeichensatzkodierung oder sowas?
Hat ja mit dem Compiler nix zu tun, ist ja nur das Betriebssystem...
Das ist eine meiner Fragen. Kann es sein, dass durch ein Windows-Update
solche Sachen passieren?
Wie Delphi mit Strings umgeht solle doch Sache des Compilers sein.
Post by Sieghard Schicktanz
Post by stefan
Die Komponente hab ich selbst vor 20 Jahren geschrieben, seitdem tausend
mal eingesetzt und keine Probleme. Jetzt so ein curioses Verhalten.
Damals war der ganze Verhau mit Unicode und "Code Pages" auch noch keiner,
man hat ASCII benutzt, Sonderzeichen nach eigener (Windows-) Manier kodiert
und pro Buchstaben genau 1 Byte benutzt. Heute...
Heute kann man sich nichtmal sicher sein, daß zwei Programm auf der
gelichen Maschine die gleiche Zeichenkodierung benutzen.
Post by stefan
Was mich wundert ist, dass die vor 2 Wochen compilierte Version das tut,
was sie soll.
==> hat irgendjemand eine Erklärung?
;-)
Sieghard Schicktanz
2020-07-22 21:06:34 UTC
Permalink
Hallo stefan,
Post by stefan
Post by Sieghard Schicktanz
Delphi 7 -> ASCII, ISO8859-* o.ä., Lazarus -> UTF-8
...
Post by stefan
Problem ist, ich verwende die Strings um Telegramme zusammen zu bauen.
Solange das ASCII- (ANSI-) _Byte_-Zeichen-Codes sind, sollte das auch
gehen. Umlaute u.ä. sind aber keine, müssen also "geeignet" kodiert werden,
und das ist von verschiednenen Umständen abhängig. Deshalb wurden ja mit
den neuen Delphi-Version zeichensatzkodierte Strings eingeführt, und genau
die kannst Du in Deiner Anwendung nicht brauchen.
Post by stefan
Nicht jetzt in dem aktuellen Projekt, aber bei anderen.
Da werden dann binäre Daten mit einem Header versehen, also
Startzeichen + Blocklänge + Daten + checksumme
Kenne ich, aber binäre Daten in einem String waren schon immer sozusagen
ein "Husarenstück", weil Strings dafür eben nie vorgesehen waren. Dein
aktuelles Delphi müßte aber dafür einen "Bytestring" als Typ bieten. Falls
Deine Sendefunktion einen solchen akzeptiert, wäre das wohl am geeignetsten.

...
Post by stefan
Post by Sieghard Schicktanz
(Neueres) Windows -> UTF16, d.h. "wide characters", also jedes Zeichen
_2_ (_zwei_) Bytes, für einfache ASCII-Zeichen 1 Byte ASCII-Code + 1
Byte #0.
Ja, so sah das aus. Wenn man sich darauf verlassen kann, dass da immer 2
Bytes stehen, könnte man damit ja leben...
Kann man bei UTF-16, aber sicherstellen, daß immer "wide characters" und
"wide strings" benutzt werden, ist trotzdem ratsam. Oder man kodiert halt
um auf ASCII/ANSI, wenn man sicher weiß, daß nur Ein-Byte-Zeichen vorkommen.
Post by stefan
Post by Sieghard Schicktanz
"writecom" nimmt als Parameter "char"? Offensichtlich...
Nein, der Parameter ist ein String. Es wird aber auch ein Char
akzeptiert. Delphi wandelt den wohl in einen String mit der Länge 1 um.
Ja. Wobei hier wohl "traditionell" Ein-Byte-Strings benutzt werden.
Daß da keine Meldung (mindestens eine Warnung) beim Kompilieren kam, ist
aber verwunderlich - oder wurde die unterdrückt bzw. ignoriert?

...
Post by stefan
Post by Sieghard Schicktanz
Nee, klar. Nur andere Ländereinstellung, Zeichensatzkodierung oder
sowas? Hat ja mit dem Compiler nix zu tun, ist ja nur das
Betriebssystem...
Das ist eine meiner Fragen. Kann es sein, dass durch ein Windows-Update
solche Sachen passieren?
Um's mal so zu sagen: es ist nicht grundsätzlich auszuschließen.
Unterschiedliche Zeichenkodierungen können evtl. sogar schon dadurch
zustande kommen, daß jemand anders mit anderer Spracheinstellung das
Programm benutzt (Deutsch (de) vs. Deutsch (ch) z.B.).
Post by stefan
Wie Delphi mit Strings umgeht solle doch Sache des Compilers sein.
Delphi geht mit den Strings halt so um, daß die Möglichkeiten des
Betriebssystems möglichst vollständig ausgenutzt werden können. Der
Compiler ist daran nur soweit beteiligt, wie er die Programmanweisungen in
Aufrufe des Run-Time-Systems umsetzt. Wie die Daten, die da als Parameter
übergeben werden, letztendlich verarbeitet werden, ist dann durch das
letztere definiert, da hat der Compiler nix mehr damit zu tun.
--
--
(Weitergabe von Adressdaten, Telefonnummern u.ä. ohne Zustimmung
nicht gestattet, ebenso Zusendung von Werbung oder ähnlichem)
-----------------------------------------------------------
Mit freundlichen Grüßen, S. Schicktanz
-----------------------------------------------------------
Michael Landenberger
2020-07-22 07:40:26 UTC
Permalink
Post by stefan
s := memo1.lines[i]
zu.
Deklariere s nicht als string, sondern als AnsiString. Dann sollte es
funktionieren. Es gibt nur eine Compiler-Warnung, dass durch die Umwandlung
Daten verloren gehen könnten. Da du aber ohnehin nur mit Ansi-(8bit-)Chars
arbeitest, ist diese Warnung für dich irrelevant.

Gruß

Michael
stefan
2020-07-22 17:05:24 UTC
Permalink
Post by Michael Landenberger
Post by stefan
s := memo1.lines[i]
zu.
Deklariere s nicht als string, sondern als AnsiString. Dann sollte es
funktionieren. Es gibt nur eine Compiler-Warnung, dass durch die Umwandlung
Daten verloren gehen könnten. Da du aber ohnehin nur mit Ansi-(8bit-)Chars
arbeitest, ist diese Warnung für dich irrelevant.
Gruß
Michael
danke für den Tipp. Werd ich morgen mal probieren
Lesen Sie weiter auf narkive:
Loading...