Forum: Mikrocontroller und Digitale Elektronik Hex zahl auf richtigkeit überprüfen


von benny 15 (Gast)


Lesenswert?

Guten Morgen ich mache in den Ferien ein kleines Projekt mit einem 
Atmega in C.

Nach der Eingabe wird der String in ein Char-Array gelegt.

Nun suche ich nach einer Lösung wie ich einen Wert aus einem Char-Array 
am besten überprüfe, ob es sich um eine gültige Hex-Zahl handelt.

beste Grüße

Ben

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Indem du jedes Zeichen prüfst ob es für dich eine gültige HEX Ziffer 
ist...

von Phantomix X. (phantomix)


Lesenswert?

Du machst eine Schleife die alle chars durchgeht.
Dann überprüfst du, ob der char 0-9, a-f, A-F ist. Wenn nicht, ist es 
keine hex-zahl

Optional/gesteigerter Schwierigkeitsgrad: eine 0x am Anfang erkennen :-)

von Marcus O. (marcus6100)


Lesenswert?

für beiden Zeichen überprüfen, ob sie erlaubt sind, z.B. so:

bool valid_hex(const char *p)
{
 for (int i = 0; i < 2; i++)
 {
   if (!strchr("0123456789AaBbCcDdEeFf", *p++))
     return false;
 }
 return true;
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

benny 15 schrieb:
> Nach der Eingabe wird der String in ein Char-Array gelegt.
Mach die Prüfung doch einfach schon während der Eingabe. Dann weißt du 
anschleissend, dass nur gültige Ziechen im Puffer sind....

von benny 15 (Gast)


Lesenswert?

Danke dass ging ja schnell :)

Werde ich gleich mal ausprobieren.

von Klaus (Gast)


Lesenswert?

#include <ctype.h>

int isxdigit(int c);

MfG Klaus

von benny 15 (Gast)


Lesenswert?

Vielen Dank noch einmal.

funktioniert alles 1A

habe folgende Funktion geschrieben die eine 0 zurückgibt wenn alles 
übereinstimmt und ein zahl größer 0 wenn etwas nicht übereinstimmt.

char Hex_Compare (char c[])
{
  size_t str_len_i = strlen(c);
    while (isxdigit(c[str_len_i-1]))
    {
      str_len_i--;
    }
    return str_len_i;
}

Beste Grüße

Ben

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

autsch so greifst du aber erheblich außerhalb der grenzen des arrays 
zu wenns blöd kommt!

Ich werde wohl nie verstehen wieso man immer versuch auf Krampf alles 
mit einer While-Schleife und abstrusen Abbruchbedingungen zu arbeiten 
wenn man über ein Menge bekannter Größe iterieren will.
Nebenbei bemerkt ist der Rückgabetyp falsch! size_t != char, und wenn du 
0 zurückgeben willst falls alles geklappt hat tu das doch anstatt 
implizit darauf zu vertrauen dass eine Variable schon 0 sein wird (wie 
gesagt wenns blöd läuft hat sie irgendeinen Wert...).
1
size_t Hex_Compare (char c[]) {
2
  size_t len = strlen(c);
3
  for(size_t i = 0; i < len; i--) {
4
   if(!isxdigit(c[i]) {
5
    return i;
6
   }
7
  }
8
  return 0;
9
}
Auf diese Weise erhälst du:
0: Alles hat geklappt, sonst: Position des ersten fehlerhaften Zeichens 
im String, du greifst nicht über die Stringgrenzen hinaus und man sieht 
auf den ersten Blick was geschieht.

von benny 15 (Gast)


Lesenswert?

Hallo Läubi,

Ich entschuldige meine While ich bin noch recht neu in der 
Programmierung. Das mit dem Char != size_t war lediglich ein 
schreibfehler sonst würde ja die funktion bei mir nicht laufen.

Nun hab ich aber ein Frage zu der For Schleife

Okay:
i wird 0 : verstanden
i < len  : Abbruchbedingung verstanden
i --     : verstehe ich NULL !

Wenn ich richtig liege wende ich im ersten Schleifendurchlauf 
"isxdigit(c[i]" im Feld 0 des Arrays an. Im nächsten wäre ich bei -1 im 
array wie geht das ?

Und würde ich nicht, wenn im ersten durchlauf ein falsches Zeichen wäre, 
sofort die Aktuelle 0 von i zurückgeben und somit das Ergebnis 
verfälschen?

Beste Grüße

Ben

von paranoider Datenschützer (Gast)


Lesenswert?

benny 15 schrieb:
> [...]
Treffer versenkt, Läubi hat sich geirrt.

Richtig:
1
for(size_t i = 0; i < len; i++) {

i++ ist die Kurzfassung für i=i+1.

von benny 15 (Gast)


Lesenswert?

Theoretisch muss ich doch am sinnvollsten i mit 1 zu definieren. Weil 
egal ob i-- oder i++ bietet mir der erste Durchlauf keine Sicherheit.

Wäre das erste zeichen kein Hexwert hätte ich ein return i; was in dem 
Fall ein return 0; wäre. Und dann würde ja die Funktionen falsche werte 
liefern.

Ben

von paranoider Datenschützer (Gast)


Lesenswert?

Nur so nebenbei und ohne Gewähr:

benny 15 schrieb:
> Im nächsten wäre ich bei -1 im array
Nicht unbedingt, das hängt davon ab ob size_t vorzeichenbehaftet oder 
-los ist. Im zweiten Fall würde i nach
1
i=0; i--;
 die größtmögliche Zahl enthalten die in size_t gespeichert werden kann 
(also z.B. 65535 bei 16 Bit).

von paranoider Datenschützer (Gast)


Lesenswert?

benny 15 schrieb:
> Theoretisch muss ich doch am sinnvollsten i mit 1 zu definieren. Weil
> egal ob i-- oder i++ bietet mir der erste Durchlauf keine Sicherheit.
>
> Wäre das erste zeichen kein Hexwert hätte ich ein return i; was in dem
> Fall ein return 0; wäre. Und dann würde ja die Funktionen falsche werte
> liefern.

Gut gesehen! i mit 1 zu initialisieren ist aber keine Lösung weil dann 
das erste Zeichen nicht geprüft wird. By the way, ich bekomme den Code 
oben sowieso nicht durch den GCC, irgendwelche Syntaxfehler (und das 
size_t in der for-Schleife will er auch nicht aber das ist wohl eine 
Einstellungssache).

Eine Möglichkeit (nicht besonders elegant finde ich):
1
signed int Hex_Compare (char c[])
2
{
3
  size_t len = strlen(c);
4
  size_t i ;
5
  for (i = 0; i < len; i++)
6
  {
7
    if (!isxdigit(c[i]))
8
      return i;
9
  }
10
  return -1;
11
}
Alternativ bei einem ungültigen Zeichen immer 1 zurückgeben (und 0 bei 
einem gültigen), dann verliert man aber die Information an welcher 
Stelle das Problem ist. (Was man durch einen Pointer auf einen int als 
zweiten Parameter lösen könnte aber das ist hässlich.)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

logisch gemeint ist i++, sorry für die Verwirrung.

benny 15 schrieb:
> Und würde ich nicht, wenn im ersten durchlauf ein falsches Zeichen wäre,
> sofort die Aktuelle 0 von i zurückgeben und somit das Ergebnis
> verfälschen?

Korrekt habe ich nicht bedacht. Das beste wäre vermutlich folgendermaßen 
abzuwandeln:
1
int Hex_Compare (char c[]) {
2
  size_t len = strlen(c);
3
  for(size_t i = 0; i < len; i++) {
4
   if(!isxdigit(c[i]) {
5
    return 1;
6
   }
7
  }
8
  return 0;
9
}
Dann erhält man nur die Info ob es geklappt hat oder nicht, sollte aber 
ausreichen.

von Phantomix X. (phantomix)


Lesenswert?

Herrje...

Ungetestet aber sollte so funktionieren:

1
char is_hex(char* input);
2
3
int main(void)
4
{
5
    char buffer[30];
6
7
    //wert einlesen
8
    //...
9
10
    if(is_hex(buffer))
11
        printf("Stimmt!\n");
12
    else
13
        printf("Stimmt nicht!\n");
14
}
15
16
char is_hex(char* input)
17
{
18
    char c;
19
    if((input[0] == '0') && (input[1] == 'x'))
20
        input += 2;
21
22
    for(; c = *input; input++)
23
    {
24
        if((c >= '0') && (c <= '9'))    continue;
25
        c |= 0x20;
26
        if((c >= 'a') && (c <= 'f'))    continue;
27
        return 0;
28
    }
29
    return 1;
30
}

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:
> Herrje...
>
> Ungetestet aber sollte so funktionieren:

In deinem Code sind gleich mehrere Probleme

> char is_hex(char* input);
>

char ist ein denkbar ungeeigneter Returnwert.
Gewöhn dir folgende Sicht der Dinge an

   char            benuzt du immer dann, wenn du Textverarbeitung
                   machst. Also Einzelzeichen oder Strings

   singned char    benutzt du immer dann, wenn du einen kleinen
                   Integer brauchst. In diesem Fall mit Vorzeichen

   unsigned char   benutzt du ebenfalls immer dann, wenn du einen
                   kleinen Integer benötigst, diesmal aber ohne
                   Vorzeichen.


Das heißt: char alleine, also ohne signed oder unsigned, nimmst du 
ausschliesslich nur dann her, wenn du Textverarbeitung machst. In allen 
anderen Fällen, überlässt du es nicht dem Compiler ob er einen char als 
signed oder unsigned ansiehst, sondern du bist da explizit.

Auch wenn ein char alleine auch nur entweder signed oder unsigned sein 
kann, ist es doch besser, man arbeitet konzeptionell mit 3 verschiedenen 
Datentypen

   char
   signed char
   unsigned char

der erste wird ausschliesslich für Textverarbeitung benutzt (un nur 
dafür), die anderen beiden für Rechnen oder Logik-Sachen.


> char is_hex(char* input)
> {
>     char c;
>     if((input[0] == '0') && (input[1] == 'x'))
>         input += 2;
>
>     for(; c = *input; input++)
>     {
>         if((c >= '0') && (c <= '9'))    continue;
>         c |= 0x20;
>         if((c >= 'a') && (c <= 'f'))    continue;
>         return 0;

Du hast auf die Grossbuchstaben vergessen.
Und genau aus diesem Grunde ist es besser, die vordefinierte Funktion 
isxdigit zu benutzen. Auch aus einem anderen Grunde ist das besser, 
wobei ich zugeben muss, dass dieser Grund heutzutage kaum mehr eine 
Rolle spielt: Wer garantiert dir, dass die Buchstaben hintereinander 
kommen? Der C-Standard garantiert dir das nämlich nicht.

von Phantomix X. (phantomix)


Lesenswert?

Hihi, schön, mal als schlechtes Beispiel zu dienen :D

Der Code ist absichtlich wie mit der Axt geschrieben...


>> char is_hex(char* input);
>>
> char ist ein denkbar ungeeigneter Returnwert.

Wo du Recht hast. Normalerweise nehm ich uint8_t, aber wollt jetzt nicht 
noch mit includes ankommen. Für den Fall "is_hex" mit Rückgabewert 
0=false, 1=true funktioniert sowohl signed als auch unsigned char. Egal 
wie man es bei der Auswertung betrachtet. Trotzdem danke für den Einwand 
:-)

>
>> char is_hex(char* input)
>> {
>>     char c;
>>     if((input[0] == '0') && (input[1] == 'x'))
>>         input += 2;
>>
>>     for(; c = *input; input++)
>>     {
>>         if((c >= '0') && (c <= '9'))    continue;
>>         c |= 0x20;
>>         if((c >= 'a') && (c <= 'f'))    continue;
>>         return 0;
>
> Du hast auf die Grossbuchstaben vergessen.

Nein hab ich nicht. Großbuchstaben handle ich über c |= 0x20; ab. 
Dadurch wird A-F -> a-f.

> Und genau aus diesem Grunde ist es besser, die vordefinierte Funktion
> isxdigit zu benutzen. Auch aus einem anderen Grunde ist das besser,
> wobei ich zugeben muss, dass dieser Grund heutzutage kaum mehr eine
> Rolle spielt: Wer garantiert dir, dass die Buchstaben hintereinander
> kommen? Der C-Standard garantiert dir das nämlich nicht.

Der Ansi und Ascii-Standard definiert das, und um den gehts ja bei der 
String-Auswertung :-)
wollte nur zeigen, dass man so ein Problem auch effizient "zu fuß" 
angehen kann, wenn man will

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:

> Nein hab ich nicht. Großbuchstaben handle ich über c |= 0x20; ab.
> Dadurch wird A-F -> a-f.

Uuuu
schlechter Stil. Ganz schlechter Stil.


> Der Ansi und Ascii-Standard definiert das,

Der ASCII Standard ist uninteressant.
Hier geht es um C. Und C schreibt nicht vor, welcher Zeichensatz zu 
verwenden ist. Schon mal auf einem IBM Grossrechner älterer Bauart 
programmiert? Dort gibt es nämlich kein ASCII, dort gibt es EBCDIC.
Und wenn man sein C richtig schreibt, und nur das benutzt, was einem 
garantiert wird (zb. das die Character '0' bis '9' in dieser Reihenfolge 
aufsteigende Codes haben müssen, aber alle anderen Character können frei 
in den Codetabellen angeordnet sein) dann kann man Code problemlos von 
einer Maschine auf die andere portieren UND gleichzeitig effizient sein.

von Phantomix X. (phantomix)


Lesenswert?

> Hier geht es um C. Und C schreibt nicht vor, welcher Zeichensatz zu
> verwenden ist. Schon mal auf einem IBM Grossrechner älterer Bauart
> programmiert? Dort gibt es nämlich kein ASCII, dort gibt es EBCDIC.
> Und wenn man sein C richtig schreibt, und nur das benutzt, was einem
> garantiert wird (zb. das die Character '0' bis '9' in dieser Reihenfolge
> aufsteigende Codes haben müssen, aber alle anderen Character können frei
> in den Codetabellen angeordnet sein) dann kann man Code problemlos von
> einer Maschine auf die andere portieren UND gleichzeitig effizient sein.

Und wieder hast du Recht. Und ich hab mit meinem Code das erreicht was 
ich erreichen wollte: Eine funktionierende Variante präsentieren, die 
mit der Axt programmiert wurde :-) Wenn ich auf Arbeit über solchen Code 
meines Vorgängers stolper[n würde], gäb es erstmal einen derben Fluch, 
dann würd ichs neu machen...

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:

> ich erreichen wollte: Eine funktionierende Variante präsentieren,

Na, dann ruf deine Funktion mal so auf

  is_hex( "" );

> meines Vorgängers stolper[n würde], gäb es erstmal einen derben Fluch,
> dann würd ichs neu machen...

:-)
Kenn ich. Ich fluch jeden Tag.

von Phantomix X. (phantomix)


Lesenswert?

ein leerer string liefert 1 zurück. weiß ich auch. man könnte das jetzt 
noch extra abfangen je nach Zweck
Anders ausgedrückt: ein leerer String ist ein valider 0-Bit-Hexwert.

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:
> ein leerer string liefert 1 zurück.

Ein leerer String generiert vor allen Dingen hier

>>     if((input[0] == '0') && (input[1] == 'x'))

eine Access Violation.


Komm, geschenkt. Du hast selbst gesagt, das ist mit der groben Axt 
gemacht.

von Phantomix X. (phantomix)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Ein leerer String generiert vor allen Dingen hier
>>>     if((input[0] == '0') && (input[1] == 'x'))
> eine Access Violation.

sch...eibenkleister, du hast Recht. Dann eben jetzt die Deluxe-Version 
mit uint8_t, Null-String-ist-falsch, A-F
1
#include <stdint.h>
2
uint8_t is_hex(char* input);
3
4
int main(void)
5
{
6
    char buffer[30];
7
8
    //wert einlesen
9
    //...
10
11
    if(is_hex(buffer))
12
        printf("Stimmt!\n");
13
    else
14
        printf("Stimmt nicht!\n");
15
}
16
17
uint8_t is_hex(char* input)
18
{
19
    char c;
20
    if(!*input) return 0;
21
    if((input[0] == '0') && (input[1] == 'x'))
22
        input += 2;
23
24
    for(; c = *input; input++)
25
    {
26
        if((c >= '0') && (c <= '9'))    continue;
27
        if((c >= 'a') && (c <= 'f'))    continue;
28
        if((c >= 'A') && (c <= 'F'))    continue;
29
        return 0;
30
    }
31
    return 1;
32
}

von bitte löschen (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> Nein hab ich nicht. Großbuchstaben handle ich über c |= 0x20; ab.
>> Dadurch wird A-F -> a-f.
>
> Uuuu
> schlechter Stil. Ganz schlechter Stil.

Ja, aber auch nur, weil es nicht entsprechend dokumentiert wurde. Hin 
und wieder muss man halt mit Bits frunzeln, wenn es den Code schneller 
und kleiner macht und es auf Schnelligkeit und Größe ankommt.

Was eher schlechter Programmierstil ist, ist ein if-Statement inklusive 
bedingter Anweisung in eine Zeile zu packen. Hier empfehle ich 
grundsätzlich die 4+ Zeilen-Variante mit jeweils einer eigenen Zeile für 
"{" und "}".

Den Ausdruck "c = *input" würde ich in ((c = *input) != 0) 
umformulieren, da das aus Compiler-Sicht auf das Gleiche hinauskommt, 
aber sofort ins Auge sticht, was es bedeuten soll und dass es Absicht 
und nicht etwa ein Programmierfehler ist.

Die Verwendung von "continue" möchte ich auch mal als zumindest 
problematisch betrachten. Es erinnert ein wenig an goto. Lieber ein if() 
mehr und den Rest einrücken.

Um auch noch etwas nettes zu sagen, möchte ich die Leerzeichen vor und 
nach Operatoren positiv anmerken!

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.