Forum: Projekte & Code Hexastring konvertieren


von Tim S. (suxx)


Lesenswert?

Hi Leute,

das sind meine ersten Gehversuche auf einem AVR, bislang hab ich "nur" 
in Windows programmiert und schon nach kurzer Zeit brauchte ich eine 
Funktion die Hexa-Strings wieder in Zahlen umwandelt, also hab ich mir 
folgende Funktionen geschrieben und da es sicher noch andere Menschen 
mit diesem Problem gibt, hab ich sie hier gleich mal veröffentlicht.
Wenn noch jemand optimierungspotential hat bitte posten.
1
uint8_t getHexValue( char cChar )
2
{
3
  if( cChar >= '0' && cChar <= '9' )
4
    return cChar - '0';
5
  else if( cChar >= 'A' && cChar <= 'F' )
6
    return cChar - 'A' + 10;
7
  else if( cChar >= 'a' && cChar <= 'f' )
8
    return cChar - 'a' + 10;
9
  else
10
    return 0xFF;
11
}
12
13
uint8_t atohex( char* pString, uint8_t* pBuffer )
14
{
15
  uint8_t    cStore[4];
16
  uint8_t    uiTmp;  
17
  uint8_t    i;
18
  uint8_t    iLength  = strlen ( pString );
19
  div_t    sDiv  = div( iLength, 4 );
20
  
21
  cStore[0] = 0; cStore[1] = 0;
22
  cStore[2] = 0; cStore[3] = 0;
23
24
  if( sDiv.rem ) sDiv.quot++;
25
26
  for( i = 0; i < sDiv.quot; i++ )
27
  {
28
    if( sDiv.rem )
29
    {
30
      for( uiTmp = 4 - sDiv.rem; uiTmp < 4; uiTmp ++ )
31
        cStore[ uiTmp ] = getHexValue( *pString++ );
32
      sDiv.rem = 0;
33
    }
34
    else
35
    {
36
      for( uiTmp = 0; uiTmp < 4; uiTmp++ )
37
        cStore[ uiTmp ] = getHexValue( *pString++ );
38
    }
39
    
40
    uiTmp = cStore[3] | cStore[2] | cStore[1] | cStore[0];
41
    if( uiTmp == 0xFF )
42
      return 0;
43
44
    *pBuffer = ( ( cStore[2] << 4 ) & 0xF0 ) | ( cStore[3] & 0xF );
45
    pBuffer++;
46
    *pBuffer = ( ( cStore[0] << 4 ) & 0xF0 ) | ( cStore[1] & 0xF );
47
    pBuffer++;
48
  }
49
50
  return 1;
51
}

Tim

von Karl H. (kbuchegg)


Lesenswert?

Könntest du mal näher erläutern, welche Nebenbeigungung
für die Funktion atohex gelten?

So wie ich das sehe
* Der String muss ein Vielfaches von 4 lang sein. Du versuchst
  anscheinend immer 2 Bytes zu konvertieren.
  Warum eigentlich? Das würde ich von einer allgemeinen
  Funktion nicht erwarten. Ein Vielfaches von 2 würde mir
  einleuchten, da ja immer 2 char eine Hex-Ziffer bilden.

* Was hat es hiermit auf sich?
    uiTmp = cStore[3] | cStore[2] | cStore[1] | cStore[0];
    if( uiTmp == 0xFF )
      return 0;
  Dafür finde ich überhaupt keine einleuchtende Erklärung

* Wäre es nicht vernünftig, wenn die Funktion die Anzahl
  der konvertierten Bytes zurückliefern würde?

* Deine Funktion hat einen Kardinalfehler, wie ihn auch die
  Funktion gets() hat:
  Du übergibst nirgends wie gross den der Buffer ist, in den
  die konvertierten Bytes geschrieben werden. So wie sie jetzt
  ist, hat die Funktion atohex() keine Chance sich gegen Buffer-
  überläufe zur Wehr zu setzen

* Der strlen() da drinnen stösst mir sauer auf.

* Alles in allem denke ich, dass diese Funktion nicht so universell
  ist, wie du denkst. Da steckt einiges an Spezialwissen drinnen,
  wie der String aufgebaut sein muss, dass du aber nicht beschreibst.

von Thomas (Gast)


Lesenswert?

* Die Deklaration des Datentyps / der Struktur "div_t" fehlt auch 
völlig.

von Thomas (Gast)


Lesenswert?

zumindest wenn stdlib.h nicht eingebunden ist

von Tim S. (suxx)


Lesenswert?

Eins vorweg, ich hatte einen Denkfehler als ich die Funktion schrieb und 
zwar dachte ich, was vollig abwegig ist auf einem 8 bit risc Kontroller, 
das er den speicher in 16 bit blöcken verwaltet.
1
uint8_t atohex( char* pString, uint8_t* pBuffer )
2
{
3
  uint8_t    cStore[2];
4
  uint8_t    uiTmp;  
5
  uint8_t    i;
6
  uint8_t*  iHexLength  = pBuffer;
7
  uint8_t    iLength    = strlen ( pString );
8
  div_t    sDiv    = div( iLength, 2 );
9
  
10
  cStore[0] = 0; cStore[1] = 0;
11
12
  if( sDiv.rem ) sDiv.quot++;
13
14
  for( i = 0; i < sDiv.quot; i++ )
15
  {
16
    if( sDiv.rem )
17
    {
18
      for( uiTmp = 2 - sDiv.rem; uiTmp < 2; uiTmp ++ )
19
        cStore[ uiTmp ] = getHexValue( *pString++ );
20
      sDiv.rem = 0;
21
    }
22
    else
23
    {
24
      for( uiTmp = 0; uiTmp < 2; uiTmp++ )
25
        cStore[ uiTmp ] = getHexValue( *pString++ );
26
    }
27
    
28
    uiTmp = cStore[1] | cStore[0];
29
    if( uiTmp == 0xFF )
30
      return ( pBuffer - iHexLength );
31
    
32
    *pBuffer = ( ( cStore[0] << 4 ) & 0xF0 ) | ( cStore[1] & 0xF );
33
    pBuffer++;
34
  }
35
36
  return ( pBuffer - iHexLength );
37
}

Karl heinz Buchegger wrote:
> Könntest du mal näher erläutern, welche Nebenbeigungung
> für die Funktion atohex gelten?
>
> So wie ich das sehe
> * Der String muss ein Vielfaches von 4 lang sein. Du versuchst
>   anscheinend immer 2 Bytes zu konvertieren.
>   Warum eigentlich? Das würde ich von einer allgemeinen
>   Funktion nicht erwarten. Ein Vielfaches von 2 würde mir
>   einleuchten, da ja immer 2 char eine Hex-Ziffer bilden.
1
if( sDiv.rem )
2
    {
3
      for( uiTmp = 4 - sDiv.rem; uiTmp < 4; uiTmp ++ )
4
        cStore[ uiTmp ] = getHexValue( *pString++ );
5
      sDiv.rem = 0;
6
    }

und jetzt
1
if( sDiv.rem )
2
    {
3
      for( uiTmp = 2 - sDiv.rem; uiTmp < 2; uiTmp ++ )
4
        cStore[ uiTmp ] = getHexValue( *pString++ );
5
      sDiv.rem = 0;
6
    }

Dieser Code rechnet damit, das die Länge vom String kein Vielfaches von 
2 oder 4 ist.
>
> * Was hat es hiermit auf sich?
>     uiTmp = cStore[3] | cStore[2] | cStore[1] | cStore[0];
>     if( uiTmp == 0xFF )
>       return 0;
>   Dafür finde ich überhaupt keine einleuchtende Erklärung

Das ist eine einfache Fehlerkontrolle, wenn die Funktion "getHexValue" 
nur bei einem Wert einen Fehler erkennt bricht die Funktion ab, da der 
Rückgabewert der Funktion "getHexValue" bei einem Fehler 0xFF ist, was 
bei einem richtigen Wert nie der Fall sein kann. Ist also das logische 
ODER dieser Werte gleich 0xFF ist ein Fehler aufgetreten.

>
> * Wäre es nicht vernünftig, wenn die Funktion die Anzahl
>   der konvertierten Bytes zurückliefern würde?

das war der Grund wieso ich überhaupt einen Rückgabewert mit integriert 
hatte, nur hab ich diese Funktionalität dann wohl irgendwie aus den 
Augen verlohren. :P

>
> * Deine Funktion hat einen Kardinalfehler, wie ihn auch die
>   Funktion gets() hat:
>   Du übergibst nirgends wie gross den der Buffer ist, in den
>   die konvertierten Bytes geschrieben werden. So wie sie jetzt
>   ist, hat die Funktion atohex() keine Chance sich gegen Buffer-
>   überläufe zur Wehr zu setzen

Wenn man ordentlich programmiert muss man solch unnötige Kontrollen 
nicht durchführen.
>
> * Der strlen() da drinnen stösst mir sauer auf.

ob ich nun strlen oder while(*String++) iLen++; mache ist ja egal, 
letztentlich brauch ich die länge des Strings.

>
> * Alles in allem denke ich, dass diese Funktion nicht so universell
>   ist, wie du denkst. Da steckt einiges an Spezialwissen drinnen,
>   wie der String aufgebaut sein muss, dass du aber nicht beschreibst.

der String wird einfach in dem Format "fE0fAc98F" übergeben, wobei die 
Länge egal ist.

Tim

von Karl H. (kbuchegg)


Lesenswert?

>>
>> * Deine Funktion hat einen Kardinalfehler, wie ihn auch die
>>   Funktion gets() hat:
>>   Du übergibst nirgends wie gross den der Buffer ist, in den
>>   die konvertierten Bytes geschrieben werden. So wie sie jetzt
>>   ist, hat die Funktion atohex() keine Chance sich gegen Buffer-
>>   überläufe zur Wehr zu setzen
>
> Wenn man ordentlich programmiert muss man solch unnötige Kontrollen
> nicht durchführen.

Na dann viel Spass im wirklichen Leben :-)
Die Anzahl von Programmabstürzen, Hackerangriffe und sonstigen
Ungereimtheiten die auf das Konto von gets() wegen fehlender
Prüfmöglichkeiten gehen sind Legende.
Du wärst der allererste Programmierer dem ein so läppischer
Fehler wie falsche Buffergrösse noch nie passiert ist.
Gratulation. Oder besser: Warte mal ein Weilchen, das kommt
schon noch.

>> * Der strlen() da drinnen stösst mir sauer auf.
>
> ob ich nun strlen oder while(*String++) iLen++; mache ist ja egal,
> letztentlich brauch ich die länge des Strings.

Was ich meine und andeuten wollte: Wozu musst du überhaut
die Länge im Vorfeld kennen. Geh durch den String durch bis
du auf ein '\0' stösst oder ein Zeichen, das nicht
konvertiert werden kann und gut ists. So wies jetzt ist,
durchläufst du den String 2-mal: Einmal um die Länge
festzustellen und einmal um die Konvertierung zu machen.

Nur so als Skizze ohne Fehlerhandling

uint8_t atohex( char* pString, uint8_t* pBuffer )
{
  uint8_t* pTmp = pBuffer;

  while( *pString ) {
    *pBuffer = getHexValue( *pString++ ) << 4;
    if( *pString == '\0' )
      return pBuffer - pTemp;
    *pBuffer++ |= getHexValue( *pString++ );
  }

  return pBuffer - pTemp;
}

Das könnte doch die Grundstruktur sein. Dazu brauch ich keine
Länge im Vorfeld wissen.


von Tim S. (suxx)


Lesenswert?

> Was ich meine und andeuten wollte: Wozu musst du überhaut
> die Länge im Vorfeld kennen. Geh durch den String durch bis
> du auf ein '\0' stösst oder ein Zeichen, das nicht
> konvertiert werden kann und gut ists. So wies jetzt ist,
> durchläufst du den String 2-mal: Einmal um die Länge
> festzustellen und einmal um die Konvertierung zu machen.
>
> Nur so als Skizze ohne Fehlerhandling
>
> uint8_t atohex( char* pString, uint8_t* pBuffer )
> {
>   uint8_t pTmp = pBuffer;
>
>   while( *pString ) {
>     *pBuffer = getHexValue( *pString++ ) << 4;
>     if( *pString == '\0' )
>       return pBuffer - pTemp;
>     *pBuffer++ |= getHexValue( *pString++ );
>   }
>
>   return pBuffer - pTemp;
> }
>
> Das könnte doch die Grundstruktur sein. Dazu brauch ich keine
> Länge im Vorfeld wissen.

an sich ja, nur hast du dann dein eigenes Szenario außer Acht gelassen 
und zwar, wenn der String kein Vielfaches von 2 ist, aber du hast recht, 
da ist Verbesserungspotential, ich kann die div weglassen und auf das 
bit 0 testen. Denn ich geh davon aus, das wenn der String kein 
Vielfaches von 2 ist, das die erste HexZahl nicht komplett übergeben 
wurde. In etwa so "Fa3" da man ja meist dann die führende 0 weglässt.

Tim

von Karl H. (kbuchegg)


Lesenswert?

> wenn der String kein Vielfaches von 2 ist

Genau das habe ich berücksichtigt.
Darum kümmert sich der if in der Schleife.
Zumindest passiert nichts. Ich nehme dann halt an,
dass noch eine 0 kommen muesste. Ist nur eine Annahme (*)
und sollte eigentlich nicht auftreten.

> In etwa so "Fa3" da man ja meist dann die führende 0 weglässt.
Ist auch so eine Annahme (*). Allerdings komplizierter zu
berücksichtigen. IN so einem Fall kommt man um den strlen()
nicht mehr rum. Wenn ich aber die Daten per Übertragung von
einem anderen µC kriege, dann schreibe ich dem vor, dass
er führende Nullen auch mit angeben muss. Für das
gewöhnliche Fussvolk, dass den Hex-String per Tastatur
eingibt mach ich eine 2-te Funktion, die per strlen checkt
ob die Anzahl stimmen kann. Wenn nicht hänge ich eine "0"
davor und rufe atohex() auf.


Das einzige was ich unberücksichtigt lies ist, wenn
sich ein falsches Zeichen im String befindet.
Aber ich sagte ja: Fehlerbehandlung mal aussen vor.
Das zu integrieren + überprüfen ob der Ausgabebuffer
nicht überlaufen wird, ist trivial.


(*)letztendlich hängt es davon ab, wer am anderen Ende
der Leitung sitzt, welche Annahme ich treffe. Wichtig
ist nur, dass in der Funktion nicht Verbotenes passiert.

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.