Zahl im String finden

Gast #549726
Lesenswert?

Hallo an alle!

Hab da ein Problem und ich komm nicht drauf, wieso das nicht 
funktioniert:

Und zwar will ich aus einem String eine Zahl herausfiltern.

Der String sieht z.B. so aus:

> char string[]="PARAM#123";

nun will ich die Zahl 123 herausfiltern. Ich habe es bisher so probiert:

> volatile int stringPosition=0;

> while(string[stringPosition]!='#')
> {
>   stringPosition++;
> }
> for(i=stringPosition, j=0; i<(stringPosition+3); i++, j++)
> {
>   zeitString[j]=string[i+1];
> }
> zeit1=atoi(zeitString);

wenn der String jetzt z.B. "PARAM#001" ist, ist zeit1=1, aber wenn im 
String jetzt "PARAM#010" steht, dann ist zeit1=0

Wieso liest er das nicht alle drei Stellen ein???

Bitte um Hilfe!

lg Peter
Gast #549764
Lesenswert?

@ peter

danke, d.h ich brauch die ganzen schleifen gar nicht und brauch nur

> zeit1 = atoi( strchr(string, '#') + 1);

zu schreiben?

Wenn ja, was ist, wenn der String so aussieht: "PARAM#123#456"

werden in zeit1 dann nur die zeichen bis zum nächsten # gespeichert, 
oder muss ich das dann anders machen?
Gast #549815
Lesenswert?

> Wenn ja, was ist, wenn der String so aussieht: "PARAM#123#456"

Peters Ansatz für eine beliebige Anzahl (auch 0) von Nummern:
1
#include <stdio.h>
2
#include <string.h>
3

4
char str[] = "PARAM#1#22#333#4444 blabla #55555 ende";
5

6
int main() {
7
  char *p;
8
  p = str;
9
  while(p=strchr(p, '#'))
10
    printf("%d\n", atoi(++p));
11
  return 0;
12
}

Die Zeile mit dem printf kannst du natürlich durch etwas anderes 
ersetzen, bspw.
1
array[n++] = atoi(++p);

wenn du die gefundenen Zahlen in ein Array schreiben möchtest.
Gast #554139
Lesenswert?

Hallo noch einmal an alle!

Hab jetzt mein Programm mit folgender Variante die letzte Zeit laufen 
gehabt:

Also der String sieht so aus:

>"PARAM#123*456+789:"

Und so lese ich das ein:

>zahl1=atoi(strchr(string,'#')+1);
>zahl2=atoi(strchr(string,'*')+1);
>zahl3=(atoi(strchr(string,'+')+1));

Nach längerem Testen stellte sich nun heraus, dass diese Art nicht immer 
zuverlässig ist. D.h. manchmal steht in den Variablen eine andere Zahl 
als im String und manchmal gar nichts.

Vielleicht sollte ich noch erwähnen, dass ich den String über die 
serielle Schnittstelle einlese, aber das Einlesen funktioniert.

Hat vielleicht einer eine Idee oder eine andere sichere Variante?

Bin für jeden Tip dankbar!

lg Peter
#554142
Lesenswert?

Das ist ja alles ziemlich vage. Ich würde drauf tippen, daß da 
irgendwelcher Schrott eingelesen wird, der Deinen Parser aus dem Gleis 
haut.

Nur ohne gesichertes Wissen über die Eingangsdaten kommst Du nicht 
weiter... interessant ist nicht, was die Gegenseite der seriellen 
Leitung abgeschickt hat, sondern was tatsächlich angekommen ist.
#554145
Lesenswert?

Peter H. wrote:
> Hallo noch einmal an alle!
>
> Hab jetzt mein Programm mit folgender Variante die letzte Zeit laufen
> gehabt:
>
> Also der String sieht so aus:
>
>>"PARAM#123*456+789:"
>
> Und so lese ich das ein:
>
>>zahl1=atoi(strchr(string,'#')+1);
>>zahl2=atoi(strchr(string,'*')+1);
>>zahl3=(atoi(strchr(string,'+')+1));
>
> Nach längerem Testen stellte sich nun heraus, dass diese Art nicht immer
> zuverlässig ist. D.h. manchmal steht in den Variablen eine andere Zahl
> als im String

Beispiel?

> und manchmal gar nichts.
Gar nichts kann nicht sein. Ein int hat immer einen Wert

>
> Hat vielleicht einer eine Idee

Übernimm nie das Ergebnis eines strchr ungeprüft.
wenn der NULL zurückliefert weiss niemand was der
atoi für ein Ergebnis liefern wird.

Dann bleieben noch die üblichen Verdächtigen:
Fehler sonstwo, Array Überlauf, String zu klein dimensioniert, ...
Dinge in der Art

> oder eine andere sichere Variante?

Das grundsätzliche Vorgehen ist ok.
Gast #554166
Lesenswert?

@Karl heinz Buchegger

Hallo! Danke erstmal für die schnelle Antwort.

Also wie schon gesagt schicke ich den String über die serielle 
Schnittstelle und der passt so, dass ist sicher.

Woran ich merke, dass da was ab und zu nicht stimmt?

Wenn ich bestimmte Werte per serieller Schnittstelle schicke, dann setze 
ich Leds um zu überprüfen, ob sie richtig eingelesen wurden. Es 
funktioniert ja auch an und ab, aber wie gesagt nicht immer. Und dass 
ist das, was ich überhaupt nicht verstehe, wieso das manchmal geht und 
wieso nicht.

Der Einlese-String ist auch groß genug, von da her dürfte auch nichts 
sein. Wo könnte dann der Fehler sein?

Wo ist ein Fehler, wenn es manchmal geht und dann wieder nicht?

Hoffe auf weitere Antworten wie auch auf etwaige andere 
Lösungsvorschläge.

lg Peter
#554177
Lesenswert?

Peter H. wrote:
> @Karl heinz Buchegger
>
> Hallo! Danke erstmal für die schnelle Antwort.
>
> Also wie schon gesagt schicke ich den String über die serielle
> Schnittstelle und der passt so, dass ist sicher.
>
> Woran ich merke, dass da was ab und zu nicht stimmt?
>
> Wenn ich bestimmte Werte per serieller Schnittstelle schicke, dann setze
> ich Leds um zu überprüfen, ob sie richtig eingelesen wurden. Es
> funktioniert ja auch an und ab, aber wie gesagt nicht immer. Und dass
> ist das, was ich überhaupt nicht verstehe, wieso das manchmal geht und
> wieso nicht.
>
> Der Einlese-String ist auch groß genug, von da her dürfte auch nichts
> sein. Wo könnte dann der Fehler sein?
>
> Wo ist ein Fehler, wenn es manchmal geht und dann wieder nicht?

Das ist sehr, sehr schwer zu sagen ohne Source Code.
Selbst mit Source Code ist es schwer :-)

Normalerweise sind solch sporadische Dinge auf
Array Overflows, Stack Overflows oder sonstige Schweinereien
zurückzuführen.

Was ich tun würde:
Ich würde auf jeden Fall mal die Return Werte der strchr
anschauen. Wahrscheinlich würde ich mir auch an dieser
Stelle eine Debug-Strecke über die Serielle zurück aufbauen
um zu sehen:
* Welcher String kommt den da (du bist nicht der erste der
  schwört, dass die Eingangswerte in Ordnung sind und wenn man
  dann genauer hinschaut, stellt man fest, dass dem nicht so ist :-)
* Welche Teilstrings wurden mit dem strchr identifiziert
* Was ist das Ergebnis vom atoi()
Ohne solche Debug-Mittel ist das ziemlich schwer zu finden.

Ich weiss schon, du willst wahrscheinlich hören, dass strchr oder
atoi manchmal Fehler machen. Schmink dir das gleich wieder ab.
Der Fehler liegt mit 99.9% Sicherheit in deinem Code.
Gast #554189
Lesenswert?

@Karl heinz Buchegger

>Ich weiss schon, du willst wahrscheinlich hören, dass strchr oder
>atoi manchmal Fehler machen. Schmink dir das gleich wieder ab.
>Der Fehler liegt mit 99.9% Sicherheit in deinem Code.

Du kennst mich ja gut. ;-)

Ja, du hast wahrscheinlich recht, also dann werde ich mal überprüfen, 
was strch() genau findet und was atoi() liefert.

Eins wäre vielleicht noch zu sagen:

Die Daten von der seriellen Schnittstelle kommen von einem Java 
Programm.
Wenn ich aber nun das Java-Programm mittels Hyperterminal simuliere. 
Also per Hand eintippe was kommen soll dann funktioniert es immer.

Vielleicht fällt dir dazu noch was ein?

lg Peter


#554238
Lesenswert?

Peter H. wrote:
> @Karl heinz Buchegger
>
> Eins wäre vielleicht noch zu sagen:
>
> Die Daten von der seriellen Schnittstelle kommen von einem Java
> Programm.
> Wenn ich aber nun das Java-Programm mittels Hyperterminal simuliere.
> Also per Hand eintippe was kommen soll dann funktioniert es immer.

Da klingeln bei mir alle Alarmglocken, dass du dir
auf jeden Fall das was von der Schnittstelle kommt in
irgendeiner Form anschauen sollst.

>
> Vielleicht fällt dir dazu noch was ein?

Da fällt mir zum Beispiel ein, dass das Java Programm
die Daten wesentlich schneller senden kann als du tippen
kannst -> hast du ein Handshake implementiert?
Gast #554265
Lesenswert?

So, hab jetzt mal ausgiegig getestet.

Ich hab mir den ganzen String gespeichert und dann wieder ausgeben.

Bei den werten Zahlen 2, 50 und 100 sollen leds leuchten.
Jetzt hat z.B. nur die led für 2 geleuchtet, aber der String sah 
folgendermaßen aus:

>PARAM#002*050+0100:

D.h. es müsste funktionieren. Also weiter Fehlersuche.

Gast #554271
Lesenswert?

@Uhu Uhuhu

Ich hab ihn wieder zum hyperterminal geschickt!

@Peter Dannegger

Heißt das, ich muss schauen, dass vor der Zahl keine Nullen kommen? Aber 
im Hyperterminal hat es auch mit Nullen davor geklappt.
Gast #554274
Lesenswert?

Ach ja, noch was:

Also im String steht:

>PARAM#002*050+0100:

Die leds für zahl1 und zahl2 leuchten, für die zahl3 nicht, obwohl es im 
String steht.
Dann hab ich mir die zahlen noch einmal geschickt
zahl1=2
zahle2=50
zahl3=0

???

also kann es doch am Einlesen liegen, oder?
#554282
Lesenswert?

Wenn Du einen Debugger an Deinen µC hast, dann schreib doch einfach 
hinter den Befehl, der die letzte Zahl einliest, sowas:

   if (zahl3 == 0)
      zahl3 = zahl3;

Auf die bedingt ausgeführte Anweisung - die nichts bewirkt - setzt Du 
einen Debugger-Haltepunkt.

Damit Du die Strings ansehen kannst, machst Du was in der Art:

   char *S1, *S2, S3;

   S1 = strchr(string,'#') + 1;
   zahl1=atoi(S1);
   ...

Dann kannst Du Dir die Strings im Debugger ansehen und mußt nicht 
rätselraten... Achte auch darauf, daß die S*-Pointer auch wirklich in 
Deinen Puffer zeigen!
Gast #554289
Lesenswert?

@Uhu Uhuhu

Ich muss auch nicht rätselraten, ich hab es zwar nicht mit dem Debugger 
gemacht, aber ich weiß, dass im String das Richtige steht.

Das hab ich jetzt ausgiebig getestet.

Und ich weiß jetzt auch immer, was in den Zahlen steht.

Wenn die entsprechende Led nicht leuchtet, dann steht in der Zahl auch 
nicht das Richtige.

Ich habe die Zahlen öfter überprüft, nicht nur mit den Leds. Ich hab sie 
auch so anggesehen.

Somit muss wohl der Fehler beim Einlesen sein, oder?

Also bei:

>zahl1=atoi(strchr(string,'#')+1);
>zahl2=atoi(strchr(string,'*')+1);
>zahl3=(atoi(strchr(string,'+')+1));
#554292
Lesenswert?

Glaubst Du wirklich, daß Du auf Anhieb in den Routinen, die vor Dir 
tausende von Programmierern erfolgreich benutzt haben und deren korrekte 
Implementierung seit 30 Jahren bekannt ist, einen Fehler findest?

Eher fällt der erste Mai auf einen Freitag den 13...

Checks nochmal so nach, wie ich es vorhin beschrieben habe.
Gast #554664
Lesenswert?

@Lukas D.

Hallo!

Ich habe das Programm sozusagen angehalten, den String abgespeichtert 
und dann wieder über die serielle Schnittstelle zum Hyperterminal 
gesendet. D.h. ich habe das Java-Programm geschlossen und dann den 
String zum Hyperterminal gesendet. Und der String passt, nur in den 
Zahlen steht manchmal das Richtige drinnen und manchmal nicht.

Ich bin immer noch nicht dahinter gekommen wieso.

@all

Mir ist schon klar, dass ich da wo einen Fehler habe. Is ja klar, wer 
sonst? Und dass die Funktionen funktionieren will ich auch nicht 
anzweifeln, sondern ich denke mir nur, dass ich sie eventuell falsch 
verwende.

lg Peter
#554677
Lesenswert?

Nochmal: Machs, wie ich am 04.05.2007 20:07 geschrieben habe. Du mußt 
wirklich in die Variablen im µC gucken, alles andere ist 
Kaffeesatzleserei.

Es ist z.B. denkbar, daß es sich um ein dynamisches Problem handelt, das 
erst dann entsteht, wenn Du den empfangenen String schon zurückgeschickt 
hast - z.B. daß während der Verarbeitung des Pufferinhaltes von der 
Gegenstelle schon wieder was neues in den Puffer geschrieben wird - 
Fachausdruck Racecondition.

Solche Dinger kriegt man am besten mit der Haltepunktfalle, die ich oben 
skizziert habe.
Gast #554685
Lesenswert?

@Uhu Uhuhu

Hallo!

Es könnte tatsächlich sein, dass sich da auch noch andere Zeichen in den 
String schmuggeln. Weil es werden kurz danach, auch noch andere Zeichen 
vom Java aus gesendet (vl ja zeitgleich???). Ich verstehe nur nicht, 
wieso dann der String im Hyperterminal passt. Aber ich glaube schon, 
dass du recht hast, dass ich nur im µC schauen kann, was wirklich 
drinnen steht.

Das Problem ist leider nur, dass ich vom debuggen nicht viel Ahnung habe 
und es deswegen damit noch nicht probiert habe.

Also kann es sein, dass obwohl der String im Hyperterminal richtig 
steht, er im µC anders gespeichert ist?

#554690
Lesenswert?

> Also kann es sein, dass obwohl der String im Hyperterminal richtig
>steht, er im µC anders gespeichert ist?

Ja: Wenn er kurz nach dem Absenden an das HT im µC zerdroschen wurde - 
z.B. von der Interrupt-Routine, die für das Datenempfangen auf der 
Leitung verantwortlich ist.

Benutzt Du nur einen einzigen Empfangspuffer?

> Das Problem ist leider nur, dass ich vom debuggen nicht viel Ahnung habe
> und es deswegen damit noch nicht probiert habe.

Dann ist das die Gelegenheit, es zu lernen...
Gast #554694
Lesenswert?

Ja, ich habe nur einen Empfangsbuffer.

Aber den String schicke ich erst, nachdem ich die Zahlen eingelesen 
habe. Und siehe da, in den Zahlen steht nicht das Richtige, aber dann im 
Hyperterminal steht der String richtig.
Gast #554703
Lesenswert?

So, das sind jetzt nur ein paar Auszüge:

die Variablen sind global und volatile
1
void main (void)
2
{
3
  init();
4
  led1=0;
5
    
6
  while(1)
7
  {
8
    getParam();
9

10
    //Hier arbeite ich mit den Zahlen dann weiter
11

12
  }    
13
    
14
}
15

16
void serISR (void) interrupt 4
17
{
18
  if(RI)
19
  {
20
    RI=0;
21
    rec=SBUF;
22

23
    if((rec=='\r')||(rec=='\n'))
24
    {  
25
      serflag=1;
26
    }
27

28
    else
29
    {
30
      receive[cnt]=rec;
31
      cnt++;
32
    }
33
  }
34
}
35

36
void getParam (void)  
37
{  
38
  if(serflag==1)
39
  { 
40
    serflag=0;
41
          
42
    if(strstr(receive, "PARAM"))
43
    {                
44
      zahl1=0;
45
      zahl2=0;
46
      zahl3=0;
47
      zahl1=atoi(strchr(receive,'#')+1);
48
      zahl2=atoi(strchr(receive,'*')+1);
49
      zahl3=(atoi(strchr(receive,'+')+1))*10;
50

51
      //Leds zum Testen
52
      if(zeit==2)
53
        led2=0;
54
        
55
      if(kP==50)
56
        led3=0;
57
        
58
      if(maxPegelstand==1000)
59
        led4=0;  
60
    }
61
    clearString();
62
    cnt=0;
63
  }
64
}
65

66
void clearString (void)
67
{
68
  int i;
69

70
  for(i=0; i<200; i++)
71
  {
72
    receive[i]=0;
73
  }
74
}

  
#554707
Lesenswert?

Überleg Dir mal, was passiert, wenn in receive gerade "PAR" steht, wenn 
getParam aufgerufen wird...

Ich sehe gerade, daß das nicht vorkommen sollte...

Aber was passiert, wenn Zeichen kommen, bevor getParam cnt = 0 
ausgeführt hat?

Ich würde sagen, dann zerklopft die ISR Dir den Speicher...

Wenn das nicht zum Absturz führt, können natürlich trotzdem Daten 
überschrieben werden. Das Mindeste ist jedoch, daß der betreffende 
Datensatz verloren geht.
Gast #554713
Lesenswert?

Wie gesagt, es handelt sich um Codeauszüge.

Hätte ich aber dazuposten können, mein Fehler:

Also, main noch einmal:
1
void main (void)
2
{
3
  init();
4
  led1=0;
5
    
6
  while(1)
7
  {
8

9
    if((serflag==1))  
10
    {
11
      serflag=0;
12
      if(strstr(receive,"ready"))
13
      {
14
        getParam();
15
      }
16
    }
17

18
    //Hier arbeite ich mit den Zahlen dann weiter
19

20
  }    
21
    
22
}

Aber außerdem wird serflag=1 erst gesetzt, wenn '\n' oder '\r' kommt und 
'\n' oder '\r' kommen erst am Ende des Strings, also muss der String 
schon komplett sein.
#554723
Lesenswert?

Du nullst den Puffer aus und dann finden die str*-Routinen den hinten 
angepappten Datensatz nicht, weil sie vorher auf ein NUL-Zeigen gestoßen 
sind.

Wahrscheinlich bekommst Du es folgendermaßen hin:

Wenn die ISR einen vollständigen Satz empfangen hat, kopiert sie ihn in 
einen separaten Puffer, der von getParam gelesen wird, setzt cnt selbst 
zurück und ist dann sofort wieder Empfangsbereit.

GetParam nullt nur seinen eigenen Puffer aus, wenn sie fertig ist.

Die ISR kopiert keine Daten in den Ausgabepuffer, wenn dessen erstes 
Byte != 0 ist und setzt stattdessen ein Overrun-Flag. Dabei geht ein 
Satz verloren.

Übrigens: Keiner der beiden Puffer muß volatile sein - überleg Dir 
warum...
Gast #554734
Lesenswert?

Also, hab ich das richtig verstanden?
1
void main (void)
2
{
3
  init();
4
  led1=0;
5
    
6
  while(1)
7
  {
8

9
    if((serflag==1))  
10
    {
11
      serflag=0;
12
      if(strstr(receive2,"ready"))
13
      {
14
        getParam();
15
      }
16
    }
17

18
    //Hier arbeite ich mit den Zahlen dann weiter
19

20
  }    
21
    
22
}
23

24
void serISR (void) interrupt 4
25
{
26
  if(RI)
27
  {
28
    RI=0;
29
    rec=SBUF;
30

31
    if((rec=='\r')||(rec=='\n'))
32
    {  
33
      serflag=1;
34
      strcpy(receive2, receive);
35
      cnt=0;
36
      clearString();
37
    }
38

39
    else
40
    {
41
      receive[cnt]=rec;
42
      cnt++;
43
    }
44
  }
45
}
46

47
void getParam (void)  
48
{  
49
  if(serflag==1)
50
  { 
51
    serflag=0;
52
          
53
    if(strstr(receive2, "PARAM"))
54
    {                
55
      zahl1=0;
56
      zahl2=0;
57
      zahl3=0;
58
      zahl1=atoi(strchr(receive2,'#')+1);
59
      zahl2=atoi(strchr(receive2,'*')+1);
60
      zahl3=(atoi(strchr(receive2,'+')+1))*10;
61

62
      //Leds zum Testen
63
      if(zahl1==2)
64
        led2=0;
65
        
66
      if(zahl2==50)
67
        led3=0;
68
        
69
      if(zahl3==1000)
70
        led4=0;  
71
    }
72
    clearString2();
73
  }
74
}
75

76
void clearString (void)
77
{
78
  int i;
79

80
  for(i=0; i<200; i++)
81
  {
82
    receive[i]=0;
83
  }
84
}
85

86
void clearString2 (void)
87
{
88
  int i;
89

90
  for(i=0; i<200; i++)
91
  {
92
    receive2[i]=0;
93
  }
94
}

Meiner Meinung nach müssen receive und receive2 schon volatile sein, 
sonst steht ja nicht immer der aktuelle Wert drinnen, oder? Und 
übergeben tu ich die Strings ja nie.
#554742
Lesenswert?

Ja, das kommt dem sehr nahe...

Ein paar kleine Optimierungen:

Sieh mal nach, ob Dein Compiler eine Funktion memset, oder setmem kennt 
- dann brauchst Du das Rad nicht nochmal zu erfinden.

Aber generell brauchst Du den zweiten Puffer garnicht auszunullen, denn 
die ISR benutzt strcpy und die kopiert die abschließende 0 mit.

Das clearString() in der ISR kannst Du Dir auch sparen, wenn Du im Block

    if((rec=='\r')||(rec=='\n')) {
       receive[cnt]= 0;
       ...
    }

den ersten Puffer ordentlich terminierst.

Zum Thema 'volatile':
Der Puffer, der nur von der ISR zugegriffen wird, muß nicht volatile 
sein - es gibt keine konkurrierenden Zugriffe.

Statt den zweiten Puffer volatile zu machen, reicht es aus, serflag 
volatile zu definieren; wenn die von der ISR gesetzt ist, greift die ja 
nicht mehr darauf zu.

Ein weiteres mögliches Problem: 'if((rec=='\r')||(rec=='\n'))'.

Was wenn die Gegenstelle - wie das unter Windows üblich ist - eine Zeile 
mit \r\n abzuschließt?
Gast #554756
Lesenswert?

Mit '\n' und '\r' hast du wahrscheinlich recht.

Ich hab mir nun folgendes überlegt:

In meinem Programm kommen öfter solche Abfragen vor und es funktioniert 
immer, bis auf das eine mal, also könnte ich es doch einfach so machen:
1
void getParam (void)  
2
{  
3
  if(serflag==1)
4
  { 
5
    serflag=0;
6
          
7
    if(strstr(receive2, "PARAM"))
8
    {  
9
      strcpy(receive2,receive);              
10
      zahl1=0;
11
      zahl2=0;
12
      zahl3=0;
13
      zahl1=atoi(strchr(receive2,'#')+1);
14
      zahl2=atoi(strchr(receive2,'*')+1);
15
      zahl3=(atoi(strchr(receive2,'+')+1))*10;
16

17
      //Leds zum Testen
18
      if(zeit==2)
19
        led2=0;
20
        
21
      if(kP==50)
22
        led3=0;
23
        
24
      if(maxPegelstand==1000)
25
        led4=0;  
26
    }
27
  }
28
}

Also nur bei dieser Abfrage mit receive2 arbeiten, dann kann mir ja auch 
nichts mehr reinpfuschen, oder?
#554776
Lesenswert?

Das ist keine gute Idee, denn die Synchronisation zwischen ISR und 
Vordergrund wird dadurch gelockert. Das Umkopieren sollte die ISR 
machen.

Wenn sich Deine ISR merkt, daß sie gerade den Puffer umkopiert hat, 
reicht es, alle Zeichen kleiner ' ' einfach wegzuschmeißen, bis
was >= ' ' kommt und dann wieder in den ersten Puffer kopieren.

Der Vorteil ist, daß Du unabhängig von Betriebssystemgebräuchen 
bezüglich Zeilenende wirst - solange \r und/oder \n benutzt werden.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren