Hi allerseits,
als gestandener Pascal-Programmierer bin ich ja allerlei
Komfort-Funktionen für diese Aufgabe gewohnt. Jetzt bei meinen
Arduino-Spielereien habe ich mir bald einen abgebrochen:
1
Stringst=Serial.readString();
2
inti,j;
3
intwert=0;
4
inthoch=1;
5
st.trim();
6
j=st.length()-1;
7
for(i=j;i>=0;i--){// in einzelne Bytes zerlegen
8
wert+=(int(st[i])-48)*hoch;// aus ACII num. Wert erzeugen
9
hoch*=10;// ins 10er System bringen
10
Serial.println(wert);// Kontrollausdruck
11
}
Und da sind jetzt noch keine Plaus-Tests oder Range-Checks dabei.
Geht das irgendwie auch einfacher? Habe ich irgendeine Standard-Lib
nicht gefunden?
MfG
OdlUri
Peter B. schrieb:> Geht das irgendwie auch einfacher?
Die Komfortfunktion nennt sich sscanf. Sie liefert die Anzahl der
fehlerfrei konvertierten Argumente zurück und gestattet verschiedene
Formate (hex, dez, int, long, float).
Näheres dazu im Wiki oder jedem anderen C-Tutorial.
atoi und Konsorten geben leider keinen Fehlerstatus zurück. Es kann eine
0 gelesen worden sein oder Mumpitz.
Peter B. schrieb:> Geht das irgendwie auch einfacher?
Dummer weise sagst du nicht, was du tun willst!
Zeigst auch keine Quelldaten.
Ansonsten haben dir meine Vorredner schon die üblichen Möglichkeiten
vorgebetet.
Nutze die Methoden der Serial Klasse, welche von Stream einige Parser
erbt.
Auch String bringt Parser/Konverter mit.
Die C-Lib, die ist auch voll damit.
Hi,
danke für die Antworten. toint, und atoi habe ich getestet und irgendwie
nicht hinbekommen. Oder hat nicht funktioniert. sscanf werde ich noch
testen.
Grüße, OldUri
Nachtrag - so funktionierts:
1
voidloop(){
2
if(Serial.available()){
3
Stringstr=Serial.readString();
4
intwert=str.toInt();
5
Serial.println(wert);
6
}
7
}
Ich hatte nur "toint" mit kleinem "i" für kleine Integer getestet, und
das scheint nicht implementiert zu sein. - Danke für die Antworten.
Joachim B. schrieb:> um String dein str in c-str() zu wandeln gibt es doch str.c_str()
String::c_str() liefert eine Zeiger auf den internen Buffer von String.
Es wandelt nicht.
Eigentlich ist die Methode recht kritisch, da man der Klasse so einen
unterjubeln kann. z.B. strtok() usw. verträgt sich nicht unbedingt damit
Auch kann die Instanz verworfen werden und du behältst einen Zeiger auf
freigegebene Speicherbereiche. Zudem weiß man die wahre Größe des
Buffers nicht.
Zum Export ist eigentlich String::toCharArray() gedacht.
Joachim B. schrieb:> wert = atoi(str.c_str()); sollte doch klappen
Ja, tut es auch. Habe soeben getestet: es ist mit 56 Byte weniger
Programmspeicherplatz sogar sparsamer gegenüber "wert = str.toInt();".
Funktionelle Unterschiede habe ich keine gefunden, beide schnippeln
Blanks vorne und hinten ab, beide hören beim 1. nichtnum. Zeichen auf.
Wieder was gelernt!
MfG, OldUri
Arduino Fanboy D. schrieb:> Zudem weiß man die wahre Größe des> Buffers nicht.
deswegen mag ich die Stringklasse nicht, aber keiner hindert mich Buffer
passend anzulegen nach C-Manier, vor dem Überlauf abzubrechen den Puffer
zu leeren und nur sinnvolle Kommandos die erkannt wurden zu nutzen.
Damit ist zumindest mein Ramverbrauch definiert!
In einem Beispiel wird mal eben der Serial In buffer STRING auf 200 Byte
angelegt mit reserve, kein Prüfen auf Überlauf oder fand ich nur
schlechte Beispiele?
Keine Reservierung, keine Prüfung auf Überlauf
https://www.best-microcontroller-projects.com/arduino-string.html
Joachim B. schrieb:> Keine Reservierung,> https://www.best-microcontroller-projects.com/arduino-string.html
Wenn du dich an schlechten Beispielen orientieren möchtest, dann men
los.
Dass da jemand keinen Speicher reserviert, und so der Fragmentierung auf
kleinen µC Tür und Tor öffnet, kannst/darfst du weder Arduino noch mir
ans Hemd kleben.
Merke:
https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/reserve/> keine Prüfung auf Überlauf
Leider gibt es keine Execptions unter AVR GCC, daher ist es
Problematisch ein Versagen der Speicherverwaltung an das aufrufende
Programm/Funktion/Methode zu melden. Allerdings wird ein jeder
Überlauf/Speichermangel in sofern abgehandelt, dass eben die Erweiterung
des internen Buffers nicht stattfindet.
Das macht leider den String unvollständig. Aber immerhin läuft der Rest
des Programms unbeeinflusst weiter.
Peter B. schrieb:> Und da sind jetzt noch keine Plaus-Tests oder Range-Checks dabei.> Geht das irgendwie auch einfacher? Habe ich irgendeine Standard-Lib> nicht gefunden?
Kommt drauf an, was du tatsächlich haben willst. In deinem Beispiel hast
du dir ja einen abgebrochen... s.u.
Ich für meinen Teil brauche im Grunde immer wieder eine einfache
Kommandozeilen-Auswertung und da stehen eben nicht nur Zahlen auf der
Kommandozeile, sondern es kann alles mögliche sein je nach Fall. Also
muß es ein möglichst einfach zu benutzender Parser sein und den hab ich
mir selber geschrieben, weil die Standard-Funktionen von C zuviel Vor-
oder Nacharbeit verlangen und nicht wirklich flexibel genug sind. Da
kann ich dir nur empfehlen, dir sowas auch zuzulegen, wenn man's da hat,
gebraucht man es allenthalben und es erleichtert die Arbeit immens.
Ansonsten ist dein Ansatz etwas seltsam:
1
for(i=j;i>=0;i--){// in einzelne Bytes zerlegen
2
wert+=(int(st[i])-48)*hoch;// aus ACII num. Wert erzeugen
3
hoch*=10;// ins 10er System bringen
Es reicht doch aus, etwa so zu verfahren:
1
char*P;
2
3
wert=0;
4
unddieSchleifedazu:
5
P=SkipBlanks(meineZeile)
6
do
7
{ch=*P++;
8
if((ch>='0')&&(ch<='9')
9
{ch=ch-'0';
10
wert=wert*10+ch;
11
// C kennt keinen Unterschied zwischen char und byte
12
}
13
elsefertigundraushier
14
}...
Eine Multiplikation mit 10 mußt du dir in jedem Falle gönnen, allenfalls
bei Platformen ohne Mul dann eben die *10 auflösen in shl 2,add,shl 1
Und eine Zahl im nur möglichen Bereich 0..9 zu einem int oder longint zu
addieren, geht immer. Dafür brauchst du keinen cast.
Und wenn du es so machst wie ich gezeigt habe, dann brauchst du auch
nicht erst das Ende zu finden, um von da aus rückwärts die Ziffern zu
verarbeiten, sondern kannst von vorn bis zum Trennzeichen bzw. null am
Ende vorwärts arbeiten. Eben bei jeder Ziffer Wert=Wert*10+Ziffer
rechnen.
W.S.
W.S. schrieb:> kannst von vorn bis zum Trennzeichen bzw. null am> Ende vorwärts arbeiten.
Ja, deine Version ist zweifellos eleganter. Ich war ja froh, dass ich
mit dem mir völlig ungewohnten "C" überhaupt ein korrektes Ergebnis
hinbekam, nach längerem Herumprobieren. (Und ich habe durchaus gesucht!)
Mein Ziel war / ist, dem blöden Arduino irgendwelche Kommandos,
bestehend aus jeweils einem Zeichen, zu schicken. In der Ardoino-IDE
geht das per "Seriellem Monitor" mit Zeichen + Return, ohne diese, z.B.
mit microcom-Terminal, einfach das Zeichen eingeben, Return nicht nötig.
Ist logisch, weil der Serial.read() auf der anderen Seite auch einzelne
Zeichen einliest. Dann kam der Wunsch auf, auch num. Werte zu
übermitteln. Naheliegend, aber zu kurz gedacht: Buchstabe u.
Ziffernfolge als String eingeben.
Aufgrund der bisherigen Diskussion werde ich es jetzt umdrehen: Wenn
Ziffer ankommt, dann in num. Wert umwandeln bis der abschließende
Bef.Char. bestimmt, was damit geschieht. Wird deutlich einfacher!
Dann brauche ich nicht mal einen String und auch keinen
readString-Befehl
Danke für Eure Tipps!
Nachtrag: besonders Chic finde ich: "ch = ch - '0';"
Peter B. schrieb:> Nachtrag: besonders Chic finde ich: "ch = ch - '0';"
Ist besser als "magische" Zahlen im Quelltext, die man nur erkennt, wenn
man diverse Tabellen im Kopf hat.
Stefan ⛄ F. schrieb:> isnum() subtrahiert nicht.
Trotzdem gut, um "kaputte" Daten zu erkennen und zu melden.
Peter B. schrieb:> besonders Chic finde ich: "ch = ch - '0';"
Ich nicht.
Denn da wird nicht nur der Wert, sondern auch die Bedeutung einer
Variablen geändert.
Das ist unanständig, da es dem "Prinzip der geringsten Verwunderung"
widerspricht.
Es ist ein (folgenloser?) Verstoß gegen die Typisierungsprinzipien.
Besser:
sehe keinen Unterschied schrieb:> uint8_t ist auch nur ein char.
char ist dafür bestimmt, Buchstaben/Zeichen aufzunehmen.
uint8_t ist für binäre numerische Values.
Das ist ein Prinzip.
Von mir aus auch ein Glaubensbekenntnis.
Dass beide dabei den gleichen Raum einnehmen ist irrelevant.
Es ist eine Frage der Semantik, nix anderes.
Wiederholung:
> besonders Chic finde ich: "ch = ch - '0';"
Ich nicht.
Denn da wird nicht nur der Wert, sondern auch die Semantik einer
Variablen geändert.
Zusatz:
Das kann zu schwer findbaren Fehlern führen.
Gerade wenn man Fremdcode wartet, ist das u.U. sehr hinterhältig.
W.S. schrieb:> Also> muß es ein möglichst einfach zu benutzender Parser sein und den hab ich> mir selber geschrieben, weil die Standard-Funktionen von C zuviel Vor-> oder Nacharbeit verlangen
Ok, atoi() liefer dafür aber für char* str="-123" zumindest richtige
Ergebnisse ;-)
Arduino Fanboy D. schrieb:> char ist dafür bestimmt, Buchstaben/Zeichen aufzunehmen.> uint8_t ist für binäre numerische Values.
Vor der Einführung von uint8_t war char mit Sicherheit auch für Zahlen
vorgesehen.
Stefan ⛄ F. schrieb:> Vor der Einführung von uint8_t war char mit Sicherheit auch für Zahlen> vorgesehen.
Und?
Ist das ein gutes Argument um das "Prinzip der geringsten Verwunderung"
auszuhebeln?
Merke:
Wir sprechen hier nicht über einen uralt C Compiler, sondern über
relativ modernes C++.
Tipp:
Früher war alles besser, sogar die Zukunft.
Stefan ⛄ F. schrieb:> isnum() subtrahiert nicht.
will ich auch nicht und deswegen gilt dein Einwand nicht!
Man muss schon wissen was man will, wer will denn von allen möglichen
Char stets 48 abziehen?
Peter B. schrieb:> dem blöden Arduino
der Arduino ist nicht blöde, also wirklich.......
weiter schreibe ich nicht!
Joachim B. schrieb:>>>>> besonders Chic finde ich: "ch = ch - '0';">>> die man immer noch behandeln muss, da nehme ich lieber isnum()>> isnum() subtrahiert nicht.> will ich auch nicht und deswegen gilt dein Einwand nicht!
Du hast isnum() als Alternative für die Subtraktion empfohlen.
> Man muss schon wissen was man will, wer will denn von allen möglichen> Char stets 48 abziehen?
Ich nehme an, dass du so wie ich die avr-libc gerne verwendest, weil du
der Meinung bist, dass sie gut und verlässlich ist, richtig?
Die atoi() Funktion avr-libc ruft intern strtol() aus. Schau mal was ich
da gefunden habe:
Peter B. schrieb:> Nachtrag: besonders Chic finde ich: "ch = ch - '0';"
Naja, kann man so oder so sehen. Im Grunde finde ich es absolut
unzeitgemäß, char und byte/word/long/int64 und Konsorten in einen
einzigen Topf zu werfen. Daß C damals eben derart schlampig entworfen
wurde, mag man ja verstehen, die Erfinder hatten als Vorlage lediglich
BCPL vor der Nase und offenbar keine Ahnung davon was das damals
durchaus übliche Algol war. Vielleicht hatten sie auch bloß ihren Stolz,
etwas zu schaffen, das möglichst in allen Dingen anders ist als Algol
und Fortran war und ist eigentlich zu mathematisch orientiert.
Aber so ist das, ich benutze C eben, weil es für µC derzeit nichts
besseres gibt - aber ohne es zu mögen. Falls es vom Free Pascal mal eine
echte Version für Arm bzw. Cortex bare metal gibt, würde ich
umschwenken.
Und du hast mittlerweile die Kommandoweise für dich entdeckt, die ich
für diverse PC-Peripherie verwende: zuerst numerisches Argument und dann
als Abschluß ein Buchstabe. Für die Kommandogabe von Programm-->µC geht
das ausgezeichnet, aber wenn man mal etwas für menschliche Kommunikation
schreibt, dann ist das nicht so gut. Da läßt man lieber eine ganze
Kommandozeile in einem Puffer auflaufen und wertet sie anschließend aus.
Das macht es möglich, auch mal sowas wie BackSpace zu verwenden, wenn
man sich mal vertippt hat und man kann als Kommandos beliebige Worte
verwenden.
Ich geb dir mal ne Kostprobe in Form von "match":
1
charmatch(char*item,char**Zptr)
2
{char*P;
3
IgnoreSpace(Zptr);
4
P=*Zptr;
5
if(UpCase(*P)!=*item)return0;
6
while(*item){if(UpCase(*P++)!=(*item++))return0;}
7
*Zptr=P;
8
IgnoreSpace(Zptr);
9
return1;
10
}
Das liest aus der Kommandozeile und zwar von der gerade aktuellen Stelle
die Zeichen (Zptr zeigt auf den Zeiger, der auf die aktuelle Stelle
zeigt), vergleicht sie mit einem Referenztext (item zeigt dort drauf)
und liefert 0 oder 1 je nachdem ob die Zeichenfolge übereinstimmt oder
nicht. Wenn übereinstimmt, dann wird der Zeiger für die aktuelle Stelle
hinter das Kommando gesetzt, sonst nicht. Ach ja, Referenz in
Großbuchstaben, weil ich hier keine Case-Sensivität haben wollte. Und
die Funktion ist steinalt, heute würde ich nicht char sondern bool
schreiben.
Und die Verwendung wäre:
1
if(match("BLOCKERASE",&Cpt))
2
{L=Hex_In(&Cpt);
3
Adresse=L;
4
BlockLoeschen(wo);
5
return;
6
}
7
8
if(match("ERASE",&Cpt))
9
{if(match("ALL",&Cpt))
10
{String_Out("Lösche Flash 0..",wo);
11
ChipErase(0,wo);
12
String_Out("Lösche Flash 1..",wo);
13
ChipErase(FlashSize,wo);
14
return;
15
}
16
if(match("FL0",&Cpt))
17
{String_Out("Lösche Flash 0..",wo);
18
ChipErase(0,wo);
19
return;
20
}
21
if(match("FL1",&Cpt))
22
{String_Out("Lösche Flash 1..",wo);
23
ChipErase(FlashSize,wo);
24
return;
25
}
26
String_Out(" Args: FL0 oder FL1\r\n",wo);
27
return;
28
}
29
30
if(match("PROG",&Cpt))
31
{L=Hex_In(&Cpt);
32
while(*Cpt)
33
{B=Hex_In(&Cpt);
34
erg=ProgFlash(L,B,wo);
35
if(!erg)
36
{String_Out("..Fehler!\r\n",wo);
37
return;
38
}
39
++L;
40
}
Das ist nur zum Erkennen, wie match verwendet wird. Man kann es je nach
Bedarf auch für mehrere Textargumente nehmen. Und die Funktionen Hex_In,
Long_In usw. benutzen dieselbe 'Mechanik': sie kriegen einen Zeiger auf
den Zeiger in die Kommandozeile ab, konvertieren ab dieser Stelle in der
Kommandozeile und aktualisieren dann den Zeiger in die Kommandozeile.
W.S.
Mir wird ganz schwindelig!
Seit ihr alle studierte und C spezialisierte Informatiker?
Oder Aliens🤓?
Oder macht Ihr seit Jahrzehnten nix anderes?
Hut ab.
Stefan ⛄ F. schrieb:> Du hast isnum() als Alternative für die Subtraktion empfohlen.
ne!
nur für den Einzelzeichentest!
abgesehen davon das ich mich irrte, alle meine Codes durchsucht und nur
Zeichen eingesammelt die isprint() waren somit fallen alle anderen raus!
Mit isnum irrte ich!
und du hast meinen Text nicht verstanden
Joachim B. schrieb:> Peter B. schrieb:>> Nachtrag: besonders Chic finde ich: "ch = ch - '0';">> die man immer noch behandeln muss, da neheme ich lieber isnum()
wenn ich alle eintrudelnen Zeichen mit '0' subtrahiere kommt Müll raus!
wenn ich auf Ziffer testen will isnum! aber nur für das einzelne Zeichen
und isnum subtrahiert nicht, war falsch und ich meinte isdigit
Joachim B. schrieb:> und du hast meinen Text nicht verstanden> wenn ich alle eintrudelnen Zeichen mit '0' subtrahiere kommt Müll raus!> wenn ich auf Ziffer testen will isnum! aber nur für das einzelne Zeichen> und isnum subtrahiert nicht, war falsch und ich meinte isdigit
Ach so, jetzt ergibt es auch für mich Sinn.