Hallo, dies ist eine Anfängerfrage. Ich will in einem C-Programm, das auf einem AVR läuft, einen String programmatisch zusammen setzen und stehe etwas auf dem Schlauch (in anderen Sprachen wie z.B. JAVA, VB oder C# für PC-Programmierung ist das einfach). Der String testString soll aus Zeichenfolgen wie z.B. "A = " und aus beliebigen Zahlen, die in uint8_t Variablen vorliegen, zusammen gesetzt werden. Die Zahlen sollen einfach nur als Byte-Wert in den String eingefügt werden. Sie sollen nicht dezimal oder formattiert erscheinen, sondern nur als einzelner character, auch wenn es dafür kein menschen-lesbares ASCII-Zeichen gibt. Danke schon Mal für eure Hilfe. Beispiel: String testString = ""; uint8_t byte1 = 0x4B; //entspricht ASCII-Zeichen für K uint8_t byte2 = 0xC8; //keine lesbare ASCII-Zeichen Entsprechung bzw. Sonderzeichen testString = "A = " + byte1 + ", B = " + byte2 + "." + "\n"; //Abschluss mit Zeilenvorschub So soll der zusammengesetzte String (als Text) aussehen: A = K, B = irgendein Zeichen für byte2. neue Zeile
:
Verschoben durch Moderator
faule Menschen verwenden dafür sprintf(,,). Der kann mit Formatstrings beladen werden.
Es ist nicht wirklich sinnvoll, nicht druckbare Zeichen in einem String zu speichern, der für druckbare Zeichen vorgesehen ist. Natürlich geht das trotzdem.
1 | char myString[] = ("A = x, B = x.\n"); |
2 | myString[4] = byte1; |
3 | myString[11] = byte2; |
:
Bearbeitet durch User
> Gunnar F.
sprintf(,,) ist tatsächlich dafür geeignet ...
aber da es eine Art eierlegende Wollmilchsau ist, braucht es mächtig
viel Platz im Speicher.
Danke schon Mal für die ersten Antworten. Die Zahlen, die in byte1 und byte2 enthalten sind müssen nicht in für Menschen lesbaren Zeichen im testString erscheinen. Der testString soll seriell übertragen werden.Es reicht, wenn er maschinenlesbar ist. sprintf verbraucht zu viel Flash. Ich will nicht mehr als ein paar Code-Bytes spendieren, bzw. soviel wie minimal nötig.
StefanK schrieb: > Die Zahlen, die in byte1 und > byte2 enthalten sind müssen nicht in für Menschen lesbaren Zeichen im > testString erscheinen. Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','.
Benedikt M. schrieb: > Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll > nicht brechen Eben deshalb benutzt man für binäre Daten keine Strings. Es reicht ein Magic-Word als StartWort, dann die beiden Bytes und zum Abschluss noch eine einfache Prüfsumme, z.B. die Addition der beiden Bytes. 5 Bytes in der Übertragung für 2 Nutzbytes sind schon mehr als genug Overhead.
:
Bearbeitet durch User
Benedikt M. schrieb: > Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll > nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','. Interessanter Hinweis, danke. Ich implementiere das in einem Soft-UART. Wenn der String übertragen ist, soll ein Zeilenumbruch erfolgen. Wie erreiche ich den Zeilenumbruch? Bzw womit muss ich den String abschliessen?
Es muss kein String sein, wenn das damit nicht sinnvoll ist. War nur ein erster Gedanke.
Moin, StefanK schrieb: > Wie erreiche ich den Zeilenumbruch?
1 | "Vielleicht mal einen Blick in ein C-Buch werfen?\n" |
Gruss WK
StefanK schrieb: > Der testString soll seriell übertragen werden. Dann kann man die Teile auch nacheinander ausgeben.
1 | puts("A ="); |
2 | putc(byte1); |
3 | puts(", B ="); |
4 | putc(byte2); |
5 | puts(".\n"); |
Dergute W. schrieb: >
1 | "Vielleicht mal einen Blick in ein C-Buch werfen?\n" |
Nee, bin grad zu beschäftigt mit Datenblatt-Lesen ;-)
Ein String in C ist ja nun nichts anderes als ein Array. Dafür gibts memcpy. Falls dir das für Strings und den abschliessenden /0 noch zu kompliziert ist, gibts dafür passende String-Funktionen. Ich werfe mal strcat und strcpy in den Ring. Oliver
Ich würde gern ohne das Einbinden von Funktionen auskommen. Wie gesagt, wenn String ungeeignet ist, nehme ich lieber ein Array.
Benedikt M. schrieb: > Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll > nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','. NUL ist ein Problem, wenn man die für strings vorgesehenen Funktionen à la strlen verwendet. Aber was soll an den anderen Zeichen "schwierig" sein?
Moin, Dann nimm doch den Vorschlag von Klaus (feelfree) von 15:22 Gruss WK
Ist das denn alles zur Compilezeit bekannt, oder sind die Bytes variabel? Oliver
Oliver S. schrieb: > Ist das denn alles zur Compilezeit bekannt, oder sind die Bytes > variabel? Die Bytes bzw. die Zahlenwerte werden im Hauptprogramm berechnet und sind variabel. Der Textanteil wie z.B. "A = " ist konstant und steht zur Compile-Zeit schon fest.
Harald K. schrieb: > NUL ist ein Problem, wenn man die für strings vorgesehenen Funktionen à > la strlen verwendet. Aber was soll an den anderen Zeichen "schwierig" > sein? Je nach Implementierung der entgegennehmenden Software wird zeilenweise eingelesen und verarbeitet. Ein \n (und meistens auch ein \r) würden dann zu zwei ungültigen, partiellen Nachrichten führen. Das Komma stört beim Trennen der Werte in einer Zeile.
:
Bearbeitet durch User
StefanK schrieb: > sprintf verbraucht zu viel Flash. Ich will nicht mehr als ein paar > Code-Bytes spendieren, bzw. soviel wie minimal nötig. Mit ein wenig Pointerakrobatik, geschickt genutzten Unions auf Arrays usw., ist das ja auch kein Problem. Nur muss man den Wald und seine Bäume kennen, sonst herrscht da nur Dunkelheit.
Wie gefällt dir das?
1 | char testString[] = "A = ?, B = ?.\n"; |
2 | testString[4]='x'; // 4 = Position vom ersten Fragezeichen |
3 | testString[11]='y'; // 11 = Position vom zweiten Fragezeichen |
Ausgabe:
1 | A = x, B = y. |
Wenn du die Positionen nicht hart kodieren willst, kannst du sie mit strchr() suchen. Siehe https://cplusplus.com/reference/cstring/strchr/
:
Bearbeitet durch User
Nemopuk schrieb: > Wie gefällt dir das? Gut, schließlich habe ich es ja schon vor 3 Stunden gepostet. Nemopuk schrieb: > mit strchr() suchen Von hinten durch die Brust ins Auge? Dann kann man ja gleich sprintf nutzen.
Klaus schrieb: > Gut, schließlich habe ich es ja schon vor 3 Stunden gepostet. oh, habe ich übersehen 😳
StefanK schrieb: > sprintf verbraucht zu viel Flash. Dann nimmste eben nicht den ATtiny13, sondern den ATtiny85. Die Lib kostet doch nur beim ersten Aufruf mehr Flash.
StefanK schrieb: > Ich würde gern ohne das Einbinden von Funktionen auskommen. Wie gesagt, > wenn String ungeeignet ist, nehme ich lieber ein Array. > Soft-Uart StefanK schrieb: > Wie erreiche ich den Zeilenumbruch? Bzw womit muss ich den String > abschliessen? StefanK schrieb: > ohne das Einbinden von Funktionen In Summe ist m.E. Marios Ansatz der einzig sinnvolle, möglicherweise sogar noch eine Nummer "rudimentärer" für den Anfang. Statt Mario M. schrieb: > Dann kann man die Teile auch nacheinander ausgeben. > puts("A ="); > putc(byte1); > puts(", B ="); > putc(byte2); > puts(".\n"); eher
1 | |
2 | sendUartc('A'); |
3 | sendUartc(' '); |
4 | sendUartc('='); |
5 | sendUartc(byte1); |
6 | sendUartc(','); |
7 | ...
|
Abschließen kannst Du mit "Linefeed" \n
1 | sendUartc('\n'); |
oder auch mit '\r' ("carridge return") davor
Wie oft soll Benedikt das noch sagen? Benedikt M. schrieb: > Je nach Implementierung der entgegennehmenden Software wird zeilenweise > eingelesen und verarbeitet. Ein \n (und meistens auch ein \r) würden > dann zu zwei ungültigen, partiellen Nachrichten führen. Das Komma stört > beim Trennen der Werte in einer Zeile. Ja, es gibt Fälle, wo byte1 und byte2 nie kleiner als 32 werden können. Aber selbst dann geht's dank ',' immer noch schief. Solange man den möglichen Wertebereich nicht kennt, kann man Strings vergessen. Ich würde je 2 Zeichen spendieren und Hex senden. Das kostet nur ein paar Byte Flash.
StefanK schrieb: > Die Zahlen sollen einfach nur als Byte-Wert in den String > eingefügt werden. Sie sollen nicht dezimal oder formattiert erscheinen, > sondern nur als einzelner character, auch wenn es dafür kein > menschen-lesbares ASCII-Zeichen gibt. Also wenn du das wirklich so möchtest, dann hab ich dir da eben mal kurz was zusammengebastelt. Vielleicht kannst es noch auf deine Bedrüfnisse anpassen, aber im großen und ganzen funktioniert es. Getest in der Konsole (Visual Studio).
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | #include <string.h> |
4 | |
5 | /**
|
6 | * str Input String with placeholder '%' for values
|
7 | * values Pointer to buffer with values
|
8 | * cnt Number of values in the buffer
|
9 | *
|
10 | * Return Number of inserted values
|
11 | */
|
12 | uint8_t insert_values(char* str, const uint8_t* values, uint8_t cnt) |
13 | {
|
14 | uint8_t replaced = 0; |
15 | size_t length = strlen(str); |
16 | |
17 | for (size_t i=0; i<length; i++) |
18 | {
|
19 | if ((str[i] == '%') && (replaced < cnt)) |
20 | {
|
21 | str[i] = values[replaced]; |
22 | replaced++; |
23 | }
|
24 | }
|
25 | |
26 | return replaced; |
27 | }
|
28 | |
29 | int main() |
30 | {
|
31 | char string[80]; |
32 | uint8_t values[10]; |
33 | uint8_t ret; |
34 | |
35 | strcpy(string, "My insert test: % % %"); |
36 | |
37 | /**
|
38 | * Um das Ergebnis besser kontrolieren zu können,
|
39 | * werden hier die Buchstaben A, B und C verwendet.
|
40 | */
|
41 | values[0] = 65; // A |
42 | values[1] = 66; // B |
43 | values[2] = 67; // C |
44 | |
45 | ret = insert_values(string, values, 3); |
46 | |
47 | printf("%d values / new string: [%s]\n", ret, string); |
48 | |
49 | return 0; |
50 | }
|
StefanK schrieb: > Die Zahlen sollen einfach nur als Byte-Wert in den String > eingefügt werden. ... > So soll der zusammengesetzte String (als Text) aussehen: > A = K, B = irgendein Zeichen für byte2. > neue Zeile Und wenn du nun 10 / 0xA als Wert von dem Byte hast, dann hast du dort ein newline. Also das erscheint mir nicht sinnvoll. Entweder du machst alles ein Binärprotokoll, oder alles ein Text Protokoll, aber doch nicht gemischt! In diesem Fall, wie wärs mit 2 Bytes Hexadezimal stattdessen?
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | |
4 | static inline char hex(uint8_t x){ |
5 | return x<10 ? x+'0' : x<16 ? x-10+'A' : '#'; |
6 | }
|
7 | #define HEX(X) hex((X) / 16), hex((X) % 16)
|
8 | |
9 | int main(void){ |
10 | int byte1 = 0x4B; |
11 | int byte2 = 0xC8; |
12 | char testString[] = { |
13 | 'A',' ','=',' ', HEX(byte1), ',', ' ', |
14 | 'B',' ','=',' ', HEX(byte2), '.', '\n', |
15 | 0
|
16 | };
|
17 | puts(testString); |
18 | }
|
In solchen Fällen wäre es echt praktisch, wenn man sowas wie "char x[]={"A = ", 'A', 0};" schreiben könnte, aber leider kann das C noch nicht... Oder man nimmt halt snprintf:
1 | char testString[17]; |
2 | snprintf(testString, sizeof(testString), "A = %02X, B = %02X.\n", byte1, byte2); |
:
Bearbeitet durch User
Irgendwie ist das echt so eine Macke von C, oder? Irgendwie scheint C Probleme mit Strings zu haben. Merke das gerade bei Arduino-Sprech, was ich für meinen MPP-Tracker mit ESP32-Webserver "lernen" musste. Die Ausgaben mittels getrennten Funktionsaufrufen aneinander zu basteln ist wirklich die einfachste Methode, ein Char an einen String anhängen geht auch noch, aber den Versuch, mehrere Strings zusammenfügen und in einer Funktion ausgeben, habe ich auch erstmal nach hinten geschoben.
Moin, Ben B. schrieb: > Irgendwie ist das echt so eine Macke von C, oder? Irgendwie scheint C > Probleme mit Strings zu haben. Ja, es ist wie verhext. Um in C mit Strings arbeiten zu koennen, muss man C koennen. Ein Teufelskreis. scnr, WK
Dergute W. schrieb: > Um in C mit Strings arbeiten zu koennen, muss man C koennen. "Arduino" macht einem das Leben mit Strings ja etwas einfacher. Und aber in "Arduino" mit Strings zu arbeiten, muss man "Arduino" können. Ist schon blöd mit der Programmiererei... Oliver
Ben B. schrieb: > aber den Versuch, mehrere Strings zusammenfügen ... > habe ich auch erstmal nach hinten geschoben. Kennst du etwa strncat() und snprintf() nicht? Vielleicht sollte man die Anleitung lesen, bevor man ein Werkzeug benutzt und dabei scheitert.
Ben B. schrieb: > Irgendwie scheint C > Probleme mit Strings zu haben. Nein, das Problem sitzt vor dem Computer, C ist nicht das Problem.
C ist eigentlich sehr durchdacht. Nur die '\0' ist reserviert für Stringfunktionen als Endezeichen. Damit ist es theoretisch möglich, selbst 1TB lange Strings anzulegen.
Beim Arduino kannst du mit "Serial.print" alles wild aneinanderklatschen. Der macht das dann schon irgendwie. Ist dort ja auch egal, wenn man da einen "dicken" oder einen noch dickeren Prozessor auf dem Steckbrett hat.
Axel R. schrieb: > eim Arduino kannst du mit "Serial.print" alles wild > aneinanderklatschen. Der macht das dann schon irgendwie. Es geht hier aber um C, nicht um Arduino und auch nicht um C++. Dass es in anderne Sprachen einfach ist, hat der TO schon in den Eröffnungsbeitrag geschrieben.
Ich kenne die printf-Varianten, aber sie nerven. Man merkt da halt irgendwie eine Schwäche der Programmiersprache, und zwar eine, die ich von einer Hochsprache so nicht erwarte. In Hochsprachen sollten einem solche Probleme eigentlich abgenommen werden bzw. da darf sich der Compiler drum kümmern und in anderen Programmiersprachen funktionierts ja schließlich auch. Nur manche kriegens irgendwie nicht hin, egal wie alt sie sind und wie lange daran schon weiterentwickelt wird. Und übrigens muss ich euch enttäuschen, ich bin nicht gescheitert. In diesem Forum scheinen es ein paar Kranke besonders gerne zu sehen, wenn irgend jemand an irgendwas scheitert - solange sie es nicht selber sind versteht sich. Es tut mir furchtbar leid, aber der MPP-Tracker und der darauf laufende Webserver funktioniert bislang einwandfrei.
Wenn man mit Strings anfängt, bleibt nur strcat oder eigene Pointer-Arithmetik. Letztendlich baut man sich dann auch nur die Funktion von strcat Benutzerdefiniert zusammen. (man spart sich aber printf etc. die Overhead erzeugen.)
:
Bearbeitet durch User
Ben B. schrieb: > In Hochsprachen sollten einem solche Probleme eigentlich abgenommen > werden bzw. da darf sich der Compiler drum kümmern und in anderen > Programmiersprachen funktionierts ja schließlich auch. Das sind aber alles Programmiersprachen für weit größere Computer als ATiny13. Bedenke, daß C von allen "Hochsprachen" mit Absicht eine der niedrigsten ist. Sie stammt aus den frühen 70er Jahren, da hatten die Computer nur wenige kB RAM. Deswegen ist C ja für kleine Mikrocontroller fast alternativlos. Arduinos Stream und String Klassen gehen in die Richtung, die dir vorschwebt, brauchen aber auch etwas größere Mikrocontroller. Der ATtiny85 wurde schon genannt.
:
Bearbeitet durch User
Wenn ich mal unterstelle, dass DU keine eierlegende Wollmilchsau brauchst, solltest DU es mal ins Auge fassen, selber eine Funktion zu schreiben. Ist gar nicht so schwer. Vor allem wenn DU genau weist, wie groß der Wertebereich ist und nicht noch 1000 Sonderfälle abgedeckt werden müssen. Normale Sensoren liefern nicht plötzlich: "keine Ahnung" sondern immer irgendwelche Werte in einem genau vorhersagbaren Bereich.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.