Forum: PC-Programmierung Wie baut man in C Strings zusammen?


von StefanK (stefanka)


Lesenswert?

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
von Gunnar F. (gufi36)


Lesenswert?

faule Menschen verwenden dafür sprintf(,,). Der kann mit Formatstrings 
beladen werden.

von Matthias S. (dachs)


Lesenswert?


von Klaus (feelfree)


Lesenswert?

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
von Sebastian S. (amateur)


Lesenswert?

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

von StefanK (stefanka)


Lesenswert?

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.

von Benedikt M. (bmuessig)


Lesenswert?

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

von Klaus (feelfree)


Lesenswert?

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
von StefanK (stefanka)


Lesenswert?

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?

von StefanK (stefanka)


Lesenswert?

Es muss kein String sein, wenn das damit nicht sinnvoll ist. War nur ein 
erster Gedanke.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

StefanK schrieb:
> Wie erreiche ich den Zeilenumbruch?
1
"Vielleicht mal einen Blick in ein C-Buch werfen?\n"

Gruss
WK

von Mario M. (thelonging)


Lesenswert?

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");

von StefanK (stefanka)


Lesenswert?

Dergute W. schrieb:
>
1
"Vielleicht mal einen Blick in ein C-Buch werfen?\n"

Nee, bin grad zu beschäftigt mit Datenblatt-Lesen ;-)

von Nemopuk (nemopuk)


Lesenswert?

Mario M. schrieb:
> puts

Puts hängt Zeilenumbrüche an.

von Mario M. (thelonging)


Lesenswert?

Ja. Das Beispiel war symbolisch gemeint.

von Oliver S. (oliverso)


Lesenswert?

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

von StefanK (stefanka)


Lesenswert?

Ich würde gern ohne das Einbinden von Funktionen auskommen. Wie gesagt, 
wenn String ungeeignet ist, nehme ich lieber ein Array.

von Harald K. (kirnbichler)


Lesenswert?

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?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Dann nimm doch den Vorschlag von Klaus (feelfree) von 15:22

Gruss
WK

von Oliver S. (oliverso)


Lesenswert?

Ist das denn alles zur Compilezeit bekannt, oder sind die Bytes 
variabel?

Oliver

von StefanK (stefanka)


Lesenswert?

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.

von Benedikt M. (bmuessig)


Lesenswert?

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
von Cartman E. (cartmaneric)


Lesenswert?

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.

von Nemopuk (nemopuk)


Lesenswert?

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
von Klaus (feelfree)


Lesenswert?

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.

von Nemopuk (nemopuk)


Lesenswert?

Klaus schrieb:
> Gut, schließlich habe ich es ja schon vor 3 Stunden gepostet.

oh, habe ich übersehen 😳

von Peter D. (peda)


Lesenswert?

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.

von Bruno V. (bruno_v)


Lesenswert?

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

von Bauform B. (bauformb)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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
}

von Daniel A. (daniel-a)


Lesenswert?

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
von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

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

von Nemopuk (nemopuk)


Lesenswert?

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.

von Frank D. (Firma: LAPD) (frank_s634)


Lesenswert?

Ben B. schrieb:
> Irgendwie scheint C
> Probleme mit Strings zu haben.

Nein, das Problem sitzt vor dem Computer, C ist nicht das Problem.

von Peter D. (peda)


Lesenswert?

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.

von Axel R. (axlr)


Lesenswert?

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.

von Nemopuk (nemopuk)


Lesenswert?

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.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von Philipp K. (philipp_k59)


Lesenswert?

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
von Nemopuk (nemopuk)


Lesenswert?

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
von Sebastian S. (amateur)


Lesenswert?

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
Noch kein Account? Hier anmelden.