Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem mit Chars in String

22 views
Skip to first unread message

stefan

unread,
Jul 21, 2020, 12:01:50 PM7/21/20
to
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

unread,
Jul 21, 2020, 4:13:30 PM7/21/20
to
Hallo stefan,

Du schriebst am Tue, 21 Jul 2020 18:01:49 +0200:

> Ich wollte einen String über die serielle Schnittstelle zu einem
> Etikettendrucker schicken. Dazu muss eine Kommandozeile zusammengesetzt

Sowas kann interessant werden...
...
> 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.

> Ich hab den String dann umcodiert:
...
> 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

> 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.

> 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...

...
> 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
...
> 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.

> Wenn wir die Character einzeln senden, funktioniert es:
>
> writecom(#27);

"writecom" nimmt als Parameter "char"? Offensichtlich...

> 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...

> 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.

> 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

unread,
Jul 22, 2020, 1:13:57 AM7/22/20
to
Am 21.07.2020 um 21:52 schrieb Sieghard Schicktanz:

> Delphi 7 -> ASCII, ISO8859-* o.ä., Lazarus -> UTF-8
>
>> 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.
>
>> 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.

>> 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
> ...
>> 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...

>> Wenn wir die Character einzeln senden, funktioniert es:
>>
>> 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.

>> 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.

>> 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.
>
>> 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:

;-)

Michael Landenberger

unread,
Jul 22, 2020, 3:40:31 AM7/22/20
to
"stefan" schrieb am 21.07.2020 um 18:01:49:

> Auf die einzelnen Zeilen im Memo greife ich mit:
>
> 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

unread,
Jul 22, 2020, 1:05:24 PM7/22/20
to
danke für den Tipp. Werd ich morgen mal probieren

Sieghard Schicktanz

unread,
Jul 22, 2020, 6:13:29 PM7/22/20
to
Hallo stefan,

Du schriebst am Wed, 22 Jul 2020 07:13:57 +0200:

> > Delphi 7 -> ASCII, ISO8859-* o.ä., Lazarus -> UTF-8
...
> 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.

> 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.

...
> > (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.

> > "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?

...
> > 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.).

> 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.
0 new messages