Forum: Compiler & IDEs Strings zu einem zusammenfügen


von T.M. (Gast)


Lesenswert?

Hallo,

Ich möchte immoment mehrere Strings zu einem zusammenfügen also ungefähr 
so:
1
unsigned char Enddaten[40];
2
for (n=0;n<4;n++)
3
  {
4
  dtostrf(Daten[n],2,4,data);
5
/*hier soll mit jedem durchlauf der Schleife "data" den "Enddaten" angehangen werden so das am ende alle 4 Werte darin gespeichert sind*/
6
   }

leider hab ich nich die geringste ahnung wie ich das anstellen soll!
Hab nach etwas googlen zwar einige Funktionen hierfür gefunden, 
allerdings nie wirklich gut erklärt.

Hoffe ihr könnt mir hier etwas weiterhelfen.

Gruß T.M.

von Oliver (Gast)


Lesenswert?


von Gast (Gast)


Lesenswert?

Hallo,

die Funktionen strcat bzw. strncat sollten das für Dich erledigen 
können. Um sie nutzen zu können, musst Du string.h einbinden.

Kuckst Du hier:
http://www.opengroup.org/onlinepubs/009695399/functions/strcat.html
http://www.opengroup.org/onlinepubs/009695399/functions/strncat.html

Gruß,
Thomas

von T.M. (Gast)


Lesenswert?

Danke für die schnelle Hilfe werd ich sofort einmal ausprobieren!

von T.M. (Gast)


Lesenswert?

Habs jetzt ausprobiert leider bekomm ich einen Zahlensalat!
1
        unsigned char Enddata[40];
2
  unsigned char data[6];
3
for (n=0;n<4;n++)
4
  {
5
  dtostrf(Daten[n],2,4,data);
6
  strcat(Enddata,data);
7
  }

Fällt euch eventuell auf was ich falsch gemacht haben könnte ?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Zahlensalat? Wie sieht der aus?

Wie sehen die Werte in Daten[] aus?

Was steht vor dem Aufruf der Schleife in Enddata[]?

von Oliver (Gast)


Lesenswert?

Eingentlich nichts - in den vorhandenen Zeilen. Warum du die 
Zeichenfelder als unsigned anlegst, verstehe ich zwar nicht, aber das 
ist hier jetzt egal.

Hast du mal überprüft, ob die Einzelstrings in data in jedem 
Schleifendurchlauf richtig sind?

Oliver

von Johannes M. (johnny-m)


Lesenswert?

data hat nur 6 Elemente. Bist Du ganz sicher, dass Dein mit dtostrf 
erzeugter String da überhaupt reinpasst? Du gibst zwei Nachkommastellen 
an. Mit dem Dezimaltrennzeichen und einem eventuellen Vorzeichen sind 
das schon vier Stellen, da bleibt nur noch eine einzige für den 
Vorkommaanteil...

von T.M. (Gast)


Lesenswert?

Einen Fehler hab ich schon gefunden die und zwar liegt der Programmcode 
in einer while Schleife und dadurch wurden der Endwertstring immer 
wieder neu beschrieben. das hab ich allerdings mit gets(Enddata); 
gelöst!
seh ich doch richtig das dies den String auf "\0"setzt oder ?

Ich habe das Problem des Teilweisen Zahlensalates immernoch.

Hier nochmal der ganze Programmcode:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <avr/rf12.h>
4
#include <inttypes.h>
5
#include <stdlib.h>
6
//------------------------------------------------------------------------------
7
// ADC auslesen und umrechnen in Spannungswert...
8
double readADC(uint8_t channel)
9
{
10
  uint8_t i;
11
  uint16_t adc_value;
12
  double voltage_sensor;
13
  //
14
   ADMUX =channel;
15
  ADMUX |= (1<<REFS0);
16
    adc_value = 0;
17
  for(i=0;i<4;i++)
18
  {
19
  ADCSRA |=(1<<ADSC);
20
  while (ADCSRA & (1<<ADSC));
21
  adc_value +=ADCW;
22
  }
23
  adc_value /=4;
24
  voltage_sensor = adc_value * 2.048 / 1024.0;
25
  //dtostrf(voltage_sensor,2,4,data);
26
  return voltage_sensor;
27
}
28
int main (void) 
29
{
30
  unsigned char Enddata[40];
31
  unsigned char data[6];
32
  double Daten[4];
33
  int n;
34
  DDRD = (1<<6)|(1<<4);
35
36
  PORTD |= (1<<6);
37
  PORTD |= (1<<4);
38
  
39
  DDRB = (1<<3);
40
  PORTB = (1<<3);
41
  
42
43
  rf12_init();          // ein paar Register setzen (z.B. CLK auf 10MHz)
44
  rf12_setfreq(RF12FREQ(868.5));  // Sende/Empfangsfrequenz auf 433,92MHz einstellen
45
  rf12_setbandwidth(5, 1, 4);    // 200kHz Bandbreite, -6dB Verstärkung, DRSSI threshold: -79dBm 
46
  rf12_setbaud(9600);        // 19200 baud
47
  rf12_setpower(0, 6);      // 1mW Ausgangangsleistung, 120k
48
  ADCSRA =(1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
49
    
50
  while(1)
51
  { 
52
// 4 ADC's auslesen
53
  for (n=0;n<4;n++)
54
  {
55
  Daten[n] = readADC(n); 
56
  }
57
// Packen der Daten in einen String
58
  for (n=0;n<4;n++)
59
  {
60
  dtostrf(Daten[n],2,4,data);
61
  strcat(Enddata,data);
62
  }
63
// senden des Endstrings
64
  rf12_txdata(Enddata,40);
65
  for(long a = 0;a<100000;a++){asm("nop");};
66
  gets(Enddata);
67
  }
68
}

Würd mich auf Kritiken zum Programmcode und oder Verbesserungsvorschläge 
freuen.

von Karl H. (kbuchegg)


Lesenswert?

T.M. wrote:

> Ich habe das Problem des Teilweisen Zahlensalates immernoch.

Du hast die Doku zu dtostrf nicht gelesen oder nicht verstanden.
dotostrf will 2 Zahlen haben, die festlegen wie es den String 
formatieren soll.
Die erste der beiden Zahlen legt die Feldbreite fest. Feldbreite 
bezeichnet die komplette Anzahl an zu generierenden Zeichen.
Die zweite der beiden Zahlen legt fest wieviele Nachkommastellen erzeugt 
werden sollen.


Du hast
>   dtostrf(Daten[n],2,4,data);

D.h.
du willst deine Ausgabe in einem Feld der Breite 2 haben.
Und in diesem Feld sollen 4 Stellen nach dem Komma aufscheinen.

Das wird schon mal nicht gehen!

Beachte: Die Feldbreite beinhaltet auch den Dezimalpunkt und ein ev. 
Vorzeichen


Und mach data ein wenig größer.
In einem char Array der Länge 6 bringst du maximal einen String mit 5 
nutzbaren Zeichen unter!
Mach das ruhig mal 10 oder 20 groß!


> // senden des Endstrings
>   rf12_txdata(Enddata,40);

Ach so? Dein String hat also eine Länge von 40. Das glaub ich nicht!
Dein Array ist mit 40 Einträgen dimensioniert. Das heisst aber nicht, 
dass der String der drinnen steht auch 40 Zeichen lang ist.

Du solltest auch Enddata mit einem leeren String initialisieren, ehe du 
anfängst an den leeren String Teile anzuhängen

  Enddata[0] = '\0';
  while(1)
  {
// 4 ADC's auslesen
  for (n=0;n<4;n++)
  {

http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F



Und gewöhn dir von Anfang an an, deinen Code korrekt einzurücken!

von Johannes M. (johnny-m)


Lesenswert?

Hupps, hatte gar nicht gesehen, dass der OP auch noch width und 
precision vertauscht hat. Danke, Karl heinz...

von holger (Gast)


Lesenswert?

>Du solltest auch Enddata mit einem leeren String initialisieren, ehe du
>anfängst an den leeren String Teile anzuhängen

Und das dann IN die Schleife legen könnte helfen ;)

  while(1)
  {
   Enddata[0] = '\0';
// 4 ADC's auslesen
  for (n=0;n<4;n++)
  {

von Karl H. (kbuchegg)


Lesenswert?

holger wrote:
>>Du solltest auch Enddata mit einem leeren String initialisieren, ehe du
>>anfängst an den leeren String Teile anzuhängen
>
> Und das dann IN die Schleife legen könnte helfen ;)
>
>   while(1)
>   {
>    Enddata[0] = '\0';
> // 4 ADC's auslesen
>   for (n=0;n<4;n++)
>   {

Ah ja.
Durch die nicht vorhendene Einrückung ist mir völlig entgangen, dass er 
ja tatsächlich eine Hauptschleife hat.
Warum ichs allerdings nicht gemerkt habe, als ich den Einfügepunkt für 
das Leersetzen des Strings gesucht habe, ist mir rätselhaft. Hab wohl 
wieder mal mit dem Weißen geschaut.

von T.M. (Gast)


Lesenswert?

Danke muss mir wohl noch einiges zum Thema Strings durchlesen...
Naja aus Fehlern lernt man.
Danke für die konstruktiven Antworten ;) ist ja leider nicht immer 
Selbstverständlich das sich Leute Zeit für Anfänger nehmen.
Werde meine Programm morgen oder am Samstag fertigstellen!

Wie gesagt jede Kritik am Programm sind willkommen sollte gegen Ende ja 
möglichst Perfekt sein!

von T.M. (Gast)


Lesenswert?

Hallo,

Habe nun wieder rumprobiert bekomme allerdings immer noch Zahlensalad 
auf dem Empfänger.
Ich benutze folgenden Code:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <avr/rf12.h>
4
#include <inttypes.h>
5
#include <stdlib.h>
6
#include <string.h>
7
//------------------------------------------------------------------------------
8
9
double readADC(uint8_t channel)
10
{
11
  uint8_t i;
12
  uint16_t adc_value;
13
  double voltage_sensor;
14
  //
15
   ADMUX =channel;
16
  ADMUX |= (1<<REFS0);
17
    adc_value = 0;
18
  for(i=0;i<4;i++)
19
  {
20
  ADCSRA |=(1<<ADSC);
21
  while (ADCSRA & (1<<ADSC));
22
  adc_value +=ADCW;
23
  }
24
  adc_value /=4;
25
  voltage_sensor = adc_value * 2.048 / 1024.0;
26
  return voltage_sensor;
27
}
28
int main (void) 
29
{
30
  char data[7];
31
  char Enddata[25];
32
  double Daten[4];
33
  int n;
34
  DDRD = (1<<6)|(1<<4);
35
36
  PORTD |= (1<<6);
37
  PORTD |= (1<<4);
38
  
39
  DDRB = (1<<3);
40
  PORTB = (1<<3);
41
  
42
43
  rf12_init();          // ein paar Register setzen (z.B. CLK auf 10MHz)
44
  rf12_setfreq(RF12FREQ(868.5));  // Sende/Empfangsfrequenz auf 433,92MHz einstellen
45
  rf12_setbandwidth(5, 1, 4);    // 200kHz Bandbreite, -6dB Verstärkung, DRSSI threshold: -79dBm 
46
  rf12_setbaud(9600);        // 19200 baud
47
  rf12_setpower(0, 6);      // 1mW Ausgangangsleistung, 120k
48
  
49
  
50
  ADCSRA =(1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
51
    
52
  while(1)
53
  { 
54
    Enddata[0] ="\0";
55
  for (n=0;n<2;n++)
56
  {
57
  Daten[n] = readADC(n);
58
  }
59
60
  for (n=0;n<2;n++)
61
  {
62
  dtostrf(Daten[n],6,4,data);
63
  strncat(Enddata,data,7);
64
  }
65
  rf12_txdata(Enddata,13);
66
  for(long a = 0;a<100000;a++){asm("nop");};
67
  }
68
}

Hab nach langen ausprobieren gemerkt das es wohl an der strncat funktion 
liegt habe versucht dort auch andere Stringsanzuhängen dies funktioniert 
allerdings nicht.
Hab ich an der Funktion irgendetwas falsch verstanden?
oder einen anderen Fehler gemacht ?

von Jörg G. (joergderxte)


Lesenswert?

>Hab nach langen ausprobieren gemerkt das es wohl an der strncat funktion
>liegt
Heißt das, du kannst:
 - ein einzelnes Byte
 - einen konstanten string
 - einen einzelnen dtostrf()-gewandelten Wert
senden (und empfangen)?
Vielleicht fügst du einfach mal ein Trennzeichen in den String ein:
1
int main (void) 
2
{
3
  char tmp_string[7];     //war: data
4
  char final_string[25];  //war: Enddata
5
  double sensor_value[4]; //war: Daten
6
  int i;                  //war: n (i -> Zaehlvariable aus Mathe)
7
  /* 
8
  init... 
9
  */
10
  while(1)
11
  { 
12
    final_string[0] = '\0'; // ' statt "
13
    for(i=0; i<2; i++)
14
    {
15
      sensor_value[i] = readADC(i);
16
      //"-1.234" bis "9.9999"?
17
      dtostrf(sensor_value[i], 6, 4, tmp_string);
18
      strncat(final_string, tmp_string, 7);
19
      strncat(final_string, "; ", 2);
20
    }
21
    rf12_txdata(final_string, 14);
22
    // "#include <util/delay.h>" nicht vergessen
23
    // Kann man den Sender nicht pollen?
24
    uint16_t pause;
25
    for(pause=1000; pause; pause--)
26
      _delay_ms(1);
27
  }
28
}
hth, Jörg

von T.M. (Gast)


Lesenswert?

>Heißt das, du kannst:
> - ein einzelnes Byte
> - einen konstanten string
> - einen einzelnen dtostrf()-gewandelten Wert
>senden (und empfangen)?


Kann eine Dtostrf()-gewandelten Wert empfangen werde deine 
Verbesserungsvorschläge mal einbringen und dann nocheinmal melden!

von Karl H. (kbuchegg)


Lesenswert?

T.M. wrote:

> Hab nach langen ausprobieren gemerkt das es wohl an der strncat funktion
> liegt

    strncat(Enddata,data,7);

In der Tat hast du da was missverstanden.

Was wird wohl die 7 da hinten angeben?
Die Länge von data? Wozu soll das gut sein? Wie lang der String in data 
ist, interessiert nun wirklich niemanden. Am allerwenigsten strcat oder 
strncat. Beide Funktionen nehmen einfach was da ist, solange bis sie im 
Quellstring auf ein '\0' Byte treffer.

Also wozu wird diese Längenangabe gut sein?
Um zu bezeichnen wieviel Platz im Zielarray zur Verfügung steht!
Genau das ist doch das grosse Problem von strcat, strcpy und wie sie 
alle heissen! Keine dieser Funktionen weiss wie gross das Zielarray ist 
und kann sich daher dagegen schützen, dass es versucht mehr Daten in das 
Ziel zu stopfen als dort überhaupt Platz hat.

Also wird dieses Argument mit Sicherheit NICHT die Größe des 
Quellstrings sein. Wenn die Funktion diesen Wert brauchen würde, könnte 
sie ihn ganz leicht selbst mit strlen ermitteln. Das ist die Größe des 
Zielarrays! In deinem Fall also:

    strncat( Enddata, data, sizeof(Enddata) );

Und warum kriegst du dann Zeichensalat?
Ganz einfach: Was macht strncat, wenn der Platz im Zielarray nicht 
reicht? Simpel: es hört auf umzukopieren. Aber gemeinerweise schliesst 
strncat in Falle, dass der Platz nicht ausreicht, den Zielstring dann 
nicht mehr mit einem '\0' Byte ab! Falls also der Zielplatz nicht 
ausreicht, dann hast du einen ungültigen String in der Zielvariablen! Du 
hast also jetzt einen ArrayOverflow durch einen ungültigen String 
eingewechselt. Sorry, aber String Verarbeitung in C ist nicht so 
einfach. Da ist Sorgfalt angesagt!

Das andere, du kannst bei der Ausgabe nicht eine fixe ANzahl an Bytes 
angeben. DU weisst ja nicht wie lang der String wirklich ist! ALso musst 
du das ermitteln. Oder aber du baust dir endlich eine Ausgabefunktion, 
die diese Anzahl nicht braucht (weil sie ausgibt, bis das abschliessende 
'\0' Zeichen auftaucht)
1
  while(1)
2
  { 
3
    final_string[0] = '\0'; // ' statt "
4
    for(i=0; i<2; i++)
5
    {
6
      sensor_value[i] = readADC(i);
7
      //"-1.234" bis "9.9999"?
8
      dtostrf(sensor_value[i], 6, 4, tmp_string);
9
      strncat(final_string, tmp_string, sizeof(final_string);
10
      strncat(final_string, "; ", sizeof(final_string));
11
    }
12
    rf12_txdata(final_string, strlen(final_string));
13
    for(pause=1000; pause; pause--)
14
      _delay_ms(1);
15
  }

Und kauf die ein Buch über C.
So schwer ist Stringverarbeitung dann auch wieder nicht.

von Jörg G. (joergderxte)


Lesenswert?

@ Karl heinz
Die AVR-Libc-docu liest sich aber so:
>Concatenate two strings.
>
>The strncat() function is similar to strcat(), except that only the first
> n characters of src are appended to dest.
(http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#gfa4a8701698b766f40180c735726cfe7)
>>dt.:" Verbind zwei strings.
>> die Funktion strncat() ähnelt strcat(), bis auf dass nur die ersten n
>> char's von src an dest gehängt werden
(Übersetzung von mir ;))
Das klingt auf jeden Fall so, als ob die Angabe "strlen(final_string)" 
ziemlich in die Hose gehen kann.

hth, Jörg

von Karl H. (kbuchegg)


Lesenswert?

Jörg G. wrote:
> @ Karl heinz
> Die AVR-Libc-docu liest sich aber so:
>>Concatenate two strings.
>>
>>The strncat() function is similar to strcat(), except that only the first
>> n characters of src are appended to dest.
> 
(http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#gfa4a8701698b766f40180c735726cfe7)
>>>dt.:" Verbind zwei strings.
>>> die Funktion strncat() ähnelt strcat(), bis auf dass nur die ersten n
>>> char's von src an dest gehängt werden
> (Übersetzung von mir ;))
> Das klingt auf jeden Fall so, als ob die Angabe "strlen(final_string)"
> ziemlich in die Hose gehen kann.

Du hast recht.
(Und ich hab mich vertan. strncpy kann man so benutzen, dass man sich 
gegenüber Array Overflow absichern kann. Bei strncat geht das so einfach 
nicht)

Allerdings: Damit gibt es in obigem Code überhaupt keinen Grund mehr 
strncat zu benutzen.
1
  while(1)
2
  { 
3
    final_string[0] = '\0'; // ' statt "
4
    for(i=0; i<2; i++)
5
    {
6
      sensor_value[i] = readADC(i);
7
      //"-1.234" bis "9.9999"?
8
      dtostrf(sensor_value[i], 6, 4, tmp_string);
9
      strcat(final_string, tmp_string);
10
      strcat(final_string, "; ");
11
    }
12
    rf12_txdata(final_string, strlen(final_string));
13
    for(pause=1000; pause; pause--)
14
      _delay_ms(1);
15
  }

von T.M. (Gast)


Lesenswert?

Nur noch mal ne frage am Rand warum soll die delay funktion unbedingt 
besser sein als wenn ich
1
 asm("nop")
 nutze ?

Bin leider noch nicht zum Ausprobieren gekommen werd dies allerding bis 
morgen abend schaffen.

von der mechatroniker (Gast)


Lesenswert?

Weil sich bei der Delay-Funktion

1. ein schlauer Mensch unter Kenntnis des Compilers mal Gedanken gemacht 
hat, wie man möglichst exakt die angegebene Verzögerung hinbekommt

2. weil sich so dein Code auch noch in einem anderen Projekt verwenden 
läßt, was z.B. mit ner anderen Taktfrequenz läuft

von T.M. (Gast)


Lesenswert?

ok hab das Problem gelöst!

Nochmals danke für eure Hilfe !

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.