Hallo,
folgender Sachverhalt: Ich habe eine recht hübsche Shell auf einem
Controller, die über RS232 angesprochen werden kann. Hier kann man
einfache Befehle ausführen lassen oder sich Inhalte von bestimmten
Variablen printen lassen. Hierbei ist auch etwas Ausgabeflexibilität
gefragt. Ein Beispiel:
1
bytes=[256]0xAA// Erzeugt einen Byte-Array mit 256 Elementen und Wert 0xAA
2
bytes?format4"%u "// Gibt den Array in 4-bytige unsigned integer Blöcke zerlegt aus, getrennt durch ein Leerzeichen
3
bytes?format1"0x%X,"// Zeigt die Bytes im Hex-Format an und trennt sie mit einem Komma
Oben genannte Beispiele funktionieren prima, da im Hintergrund die
Funktion vsnprintf() arbeitet, die den Formatstring (z.B. "0x%X")
entgegennimmt und auf die Daten der Shell-Variable "bytes" in args
anwendet und das Ergebnis im screenbuffer ablegt:
1
vsnprintf(screenbuffer,100,format,args);
Wenn ich jetzt aber möchte, dass mir immer nur ein Wert pro Ausgabezeile
ausgegeben wird, erscheint folgende Eingabe logisch, funktioniert aber
nicht, wie von mir erwartet:
1
bytes?format4"%u\r\n"
Jetzt wird keine neue Zeile begonnen, sondern der String "\r\n" genau so
mit ausgegeben und alle Ergebnisse aneinander gehängt, was nicht im
Sinne des Erfinders liegt.
Warum reagiert vsnprintf hier so und interpretiert die eingegeben
Escape-Sonderzeichen nicht? Oder liegt's vielleicht am Terminalprogramm?
Wenn ich im Source-Code
1
vsnprintf(screenbuffer,100,format,args);
ersetze durch
1
vsnprintf(screenbuffer,100,"%u\r\n",args);
kommen die Zeilenumbrüche wie erwartet.
Wie bringe ich vsnprintf dazu, meine eingegebenen Escape-Zeichen zu
interpretieren anstatt sie nur zur Ausgabe durchzureichen?
um welche Prorgrammiersprache geht es denn hier?
> bytes = [256] 0xAA> bytes? format 4 "%u "
also C ist das schon mal nicht
> vsnprintf(screenbuffer, 100, format, args);
das sieht zwar wie C aus, aber was steht nun in format drin?
Achso, sorry.
Die Shell hat ihre eigene Syntax und sollte hier keine Rolle spielen,
die Argumente werden geparset und zerlegt und an die C-Funktion
vsnprintf weitergegeben.
1
vsnprintf(screenbuffer,100,format,args);
In der C-Variable "format" steht das in der Shell eingegebene Argument
"%u\r\n" drin, in C-Variable "args" ist der Zeiger auf den Inhalt der
Shell-Variable "bytes" enthalten. Die Variableninhalte von format und
args habe ich mit dem Debugger überprüft, die Argument-Zerlegung durch
die Shell hat korrekt funktioniert.
Alexander I. schrieb:> Warum reagiert vsnprintf hier so und interpretiert die eingegeben> Escape-Sonderzeichen nicht?
Du hast etwas noch nicht verinnerlicht:
Der String
"\r\n"
enthält NICHT die Zeichen '\' dann 'r', dann '\' und 'n'.
\n ist lediglich in der Programmiersprache C die Schreibweise für ein
ganz bestimmtes Zeichen, welche eben in der Programmiersprache durch
hintereinanderfügen der beiden Zeichen \ und n geschrieben wird. Aber es
ist im String lediglich ein einziges Zeichen (für das es auf der
Tastatur keine Taste gibt, daher der Ausweg über die \ Schreibweise. Der
C-Compiler fischt dann das \ mit dem nachfelgenden n heraus und ersetzt
diese Sequenz durch den ASCII Code des Zeichens Carriage Return)
Wenn du aber einen Text per RS232 (oder sonstwo) her bekommst, dann
enthält der String tatsächlich einfach nur die beiden Zeichen \ und n,
und zwar im String.
Auf ASCII Code Ebene sieht man das noch besser.
Der String, den du in C schreibst,
"\r\n"
wird vom Compiler im Speicher so abgelegt
0x0D 0x0A 0x00
gibts du aber über deine Command Line den Text "\r\n" ein, dann wird der
so im Speicher abgelegt
0x5C 0x72 0x5C 0x6E 0x00
etwas völlig anderes. (In der Programmiersprache würdest du das als
"\\r\\n" schreiben)
Wenn du haben willst, dass in einem String, den dir der Benutzer
vorgegben hat, ein "\r" zum Zeichen '\r' umgewandelt wird (bzw "\n" zu
'\n') dann musst du das selber entsprechend umformen. Also genau das
tun, was auch der C-Compiler tut, wenn er dein Programm übersetzt und in
einem String auf die Sequenz "\r" (oder sonst irgendeine andere Escape
Sequenz) stösst.
> Warum reagiert vsnprintf hier so und interpretiert die> eingegeben Escape-Sonderzeichen nicht?
Weil vsnprintf in der Beziehung da überhaupt nichts mehr interpretiert.
In einem C-Programm wird die entsprechende Substituierung bereits vom
C-Compiler erledigt. Und das macht auch Sinn, denn schliesslich soll die
strlen("\r\n") ja 2 sein und nicht 4.
Karl Heinz Buchegger schrieb:> Der> C-Compiler fischt dann das \ mit dem nachfelgenden n heraus und ersetzt> diese Sequenz durch den ASCII Code des Zeichens Carriage Return)
LF, nicht CR :-)
Hallo KHB,
ich hatte es fast befürchtet, dass der Precompiler hier seine Finger im
Spiel hat. Dann werde ich meine Argumenterfassung der Shell entsprechend
anpassen müssen, damit diese ein CR oder LF-Zeichen erzeugt. :-/ Hatte
gehofft da gäbe es vielleicht was Hübsches in stdio was diese Aufgabe
zur Laufzeit übernehmen kann...
Alexander I. schrieb:> Hallo KHB,>> ich hatte es fast befürchtet, dass der Precompiler hier seine Finger im> Spiel hat.
Nix Precompiler. Das ist schon der echte Compiler.
> Dann werde ich meine Argumenterfassung der Shell entsprechend> anpassen müssen, damit diese ein CR oder LF-Zeichen erzeugt. :-/ Hatte> gehofft da gäbe es vielleicht was Hübsches in stdio was diese Aufgabe> zur Laufzeit übernehmen kann...
Ah, geh. Ist doch trivial. Einfache Zeichenersetzung, in einer Schleife
den String durchgehen und nach einem '\' ja nachdem nächsten Zeichen
einen anderen ASCII Code einsetzen. Da der String dabei nur kürzer
werden kann, kann man das auch inplace machen.
Aber Achtung: Diese Substituierung darfst du nur bei Dingen machen, die
du in deiner Shell als String klassifiziert hast!
\a Bell (alert)
\b Backspace
\f Formfeed
\n New line
\r Carriage return
\t Horizontal tab
\v Vertical tab
\' Single quotation mark
\" Double quotation mark
\\ Backslash
\? Literal question mark
\ooo ASCII character in octal notation
\xhh ASCII character in hexadecimal notation
Die letzten 3 kannst du dir höchst wahrscheinlich sparen und den
'vertical tab' wüsste ich jetzt auch nicht, wozu der gut sein soll.
Eine eigene Frage?
Dieser Thread ist über 14 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.