array auf inhalt überprüfen (is binary?)



  • eigentlich müßte sich dein Compiler beschwert haben, denn dein Programm hat mehrere Probleme:

    int is_bin(buf)
    

    für den Parameter 'buf' hast du keinen Typ angegeben. In dieser Definition setzt dein Compiler hier den Typ 'int' ein - bestimmt nicht das, was du möchtest.
    Deshalb solltest du IMMER den Typ einer Variablen angeben, egal wo du sie definierst - in der Parameterliste einer Funktion oder im Funktionsrumpf.

    Dein weitere Code zeigt, daß du diese Variable im Zusammenhang mit der C-Funktion 'fgetc' einsetzen möchtest, um zeichenweise aus 'buf' zu lesen.
    Jetzt sie dir mal die Deklaration dieser C-Funktion an:

    int fgetc ( FILE * stream );
    

    Hier siehst du, daß die Funktion 'fgetc' einen Parameter vom Typ 'FILE *' erwartet, d.h. der Parameter 'buf' in deiner Funktion muß vom Typ 'FILE *' sein.
    Die Zeile muß also folgendermaßen aussehen:

    int is_bin(FILE *buf)
    

    Der Typ 'FILE *' der Variablen 'buf' weist darauf hin, daß es sich hier um einen Typ zur Arbeit mit Dateien handelt. Ich nehme nicht an, daß du beabsichtigt hast, ausschließlich den Inhalt von Dateien zu prüfen. Ich mache aber erstmal mit der Prüfung von Dateien weiter und komme weiter unten auf das Prüfen von C-Strings:

    {
        char c;
        int x=1;
    
        do{
        c=fgetc(buf);
        if(c!='0' && c!='1' && c!=NULL && c!='\n')
    

    Für die 'if'-Zeile sollte Dein Compiler eine Warnung ausgegeben haben:
    problematisch ist der Ausdruck 'c!=NULL'. Das ist wieder ein Typproblem. 'c' ist vom Typ 'char'. Der Ausdruck 'NULL' hat nichts mit der Zahl '0' oder dem Zeichen '\0' zu tun, auch wenn er oft (aber nicht immer!) diesen Wert repräsentiert. Der Ausdruck 'NULL' steht für den Nullzeiger (einen Zeiger auf die {nicht existente} Speicheradresse '0'). somit vergleichst du einen 'char' mit einem Zeiger, was hier wohl zum gewünschten Ergebnis führt, aber unsauberer Programmierstil ist. Du solltest nur gleiche Typen miteinander vergleichen, hier also 'char' mit 'char', was dann so aussieht:

    c != '\0'
    

    oder in der gesamten Zeile:

    if(c!='0' && c!='1' && c!='\0' && c!='\n')
    

    zum nächsten Abschnitt:

    {
                x=0;
                return 0;
                break;
            }
    

    In diesem Abschnitt fange ich mal von hinten an:
    das 'break;' wird niemals erreicht und ist daher überflüssig aber nicht schädlich.
    Warum? Die Anweisung in der Zeile davor beendet nämlich die Abarbeitung deiner Funktion 'is_bin' und die Programmausführung geht an den Aufrufer zurück. Aus diesem Grund ist auch die Zuweisung eine Zeile höher 'x=0;' ohne Wirkung und überflüssig, aber wieder nicht schädlich.

    kommen wir zum Ende der do-while-Schleife:

    }while(c=='0' || c=='1');
    

    Überleg mal: Wie lange soll die Schleife wirklich laufen?
    Versuch 1: solange '0' oder '1' gelesen wird. Was ist aber mit den Zeichen '\0' oder '\n', die du ja innerhalb der do-while-Schleife bei der if-Verzweigung mit prüfst?
    Versuch 2: solange '0', '1', '\0' oder '\n' gelesen wird. Ist deine Binärzahl also gültig, wenn irgendwo mittendrinn '\0' oder '\n' auftaucht?
    Versuch 3: wenn das Ende der Datei erreicht ist und bis dahin ausschließlich '0' oder '1' gelesen wurde.
    Ich vermute, die letzte Variante entspricht dem, was du möchtest.
    Beim Lesen aus einer Datei wird das Dateiende durch den Wert 'EOF' (End of File) angezeigt.
    Das bedeutet für die Bedingung zum Ausführen der Schleife:

    }while(c != EOF);
    

    In der do-while-Schleife steckt noch ein logischer Fehler. Dazu erstmal die bis hierher geänderte Schleife:

    do{
            c=fgetc(buf);
            if(c!='0' && c!='1')
                {
                return 0;
                };
        }while(c!=EOF);
    

    Am Anfang der Schleife liest du das zu prüfende Zeichen ein. Dann prüfst du ob es gültig ist. In der Liste mit den gültigen Zeichen innerhalb der if-Verzweigung taucht aber 'EOF' nicht auf, sodaß spätestens das Dateiende-Zeichen eine fehlgeschlagene Prüfung erzeugt. Auch wenn die Datei eine gültige Binärzahl enthält, bekommst du einen Rückgabewert von 0.
    Hier führt die Wahl einer while-Schleife (nicht do-while) zum Erfolg:

    c=fgetc(buf);
    	if (c == EOF)
    		{
    		return 0;
    		};
    
    	while (c != EOF)
    		{
    		if(c!='0' && c!='1')
    			{
    			return 0;
    			};
    		c=fgetc(buf);
    		};
    

    Zunächst lese ich vor Eintritt in die Schleife das erste Zeichen aus der Datei.
    Wenn dieses Zeichen bereits 'EOF' ist, ist die Datei leer (es befindet sich keine Binärzahl darin).Also gebe ich eine fehlgeschlagene Prüfung zurück (Rückgabewert 0).
    Ist das Zeichen nicht 'EOF' trete ich in die Schleife ein und prüfe auf '0' oder '1'. Solange ich jetzt '0' oder '1' aus der Datei einlese und das Dateiende nicht erreicht wurde bleibe ich in der Schleife. Beim Erreichen des Dateiendes verlasse ich die Schleife dann normal, sonst in der if-Verzweigung.

    Somit sind wir bei der letzten Zeile angelangt:

    if(x!=0)return 1;
    

    hierher kommen wir nur, wenn alle Zeichen in der Datei gültig waren. Daher ist die if-Abfrage nicht mehr notwendig. Wir geben einfach den Wert '1' zurück.

    return 1;
    

    und jetzt der gesamte Code nochmal zusammen inclusive des Aufrufes der Funktion aus einem Hauptprogramm:

    #include <stdio.h>
    #include <stdlib.h>
    
    int is_bin(FILE * buf)
    {
       char c;
    
       c=fgetc(buf);
       if (c == EOF)
          {
          return 0;
          };
    
       while (c != EOF)
          {
          if( c != '0' && c != '1')
             {
             return 0;
             };
          c=fgetc(buf);
          };
       return 1;
    };
    
    int main(int argc, char *argv[])
    {
       FILE* pFile;
       pFile = fopen ("/tmp/x.txt","r");
       if (pFile == NULL)
          {
          perror("kann Datei nicht öffnen\n");
          return -1;
          };
    
       int testErgebnis;
       testErgebnis = is_bin(pFile);
       printf("Test ergab: %i\n", testErgebnis);
    
       return 0;
    };
    

    Im 'main' versuche ich zunächst, die Datei zu öffnen. Schlägt das fehl, wird mit einer Fehlermeldung abgebrochen.
    Noch eine Bemerkung zum Dateinamen:
    Ich arbeite auf einem Linux-System. Daher ist bei mir "/tmp/x.txt" korrekt.
    Wenn du auf Windows arbeitest muß als Dateiname irgendetwas wie "C:\\TEMP\\x.txt" stehen. Wichtig dabei sind die doppelten '\' für die Pfadtrenner, da in einem C-String das einzelne Zeichen '\' durch '\' dargestellt wird.Ein einzelner '\' dient als Escape-Zeichen (z.B. bei '\n'). Ich hoffe das war verständlich.

    So, jetzt die Variante für C-Strings, wahrscheinlich das, was du wolltest
    (Aufruf aus dem Hauptprogramm einmal mit fester Zeichenkette und einmal mit Variable:

    #include <stdio.h>
    #include <stdlib.h>
    
    int is_bin(char * buf)
    {
    	if (*buf == '\0')
    		{
    		return 0;
    		};
    
    	while (*buf != '\0')
    		{
    		if(*buf != '0' && *buf != '1')
    			{
    			return 0;
    			};
    		++buf;
    		};
        return 1;
    };
    
    int main(int argc, char *argv[])
    {
       int testErgebnis;
       testErgebnis = is_bin("000101001010100010010100101");
       printf("Test ergab: %i\n", testErgebnis);
    
       char* string="00010010101000100101a011";
       testErgebnis = is_bin(string);
       printf("Test ergab: %i\n", testErgebnis);
    
       return 0;
    };
    

    Hier wird an deine Funktion 'is_bin' ein Zeiger auf das erste Zeichen der zu prüfenden Zeichenkette übergeben (char * buf , Das '' bedeutet, das es sich um einen Zeiger handelt). Um an das eigentliche Zeichen zu kommen, muß der Zeiger dereferenziert werden. Das geschieht durch voranstellen von '' vor den Namen des Zeigers (hier durch '*buf'). Durch den Ausdruck '++buf' wird der Zeiger auf das nächste Zeichen der Zeichenkette gesetzt.

    C-Strings werden immer mit '\0' beendet. Daher ist die Schleifenbedingung folgendermaßen definiert

    while (*buf != '\0')
    

    Soweit ist alles in Ordnung wenn du mit C arbeitest.

    Bei der Verwendung von C++ schlage ich noch eine Änderung vor. Das betrifft den Rückgabewert deiner Funktion 'is_bin'. Bei C++ existiert noch ein Typ 'bool' für Wahrheitswerte. Der kann die Werte 'true' und 'false' annehmen. Damit kannst du deine Absichten auf Prüfung deutlicher machen:

    #include <stdio.h>
    #include <stdlib.h>
    
    bool is_bin(char * buf)
    {
       if (*buf == '\0')
          {
          return false;
          };
    
       while (*buf != '\0')
          {
          if(*buf != '0' && *buf != '1')
             {
             return false;
             };
          ++buf;
          };
       return true;
    };
    
    int main(int argc, char *argv[])
    {
       if (is_bin(const_cast<char*>("000101001010100010010100101")))
          printf("erster Test ergab: Zeichenkette ist Binärzahl\n");
       else
          printf("erster Test ergab: Zeichenkette enthält unerlaubte Zeichen\n");
    
       char* string=const_cast<char*>("00010010101000100101a011");
       if (is_bin(string))
          printf("zweiter Test ergab: Zeichenkette ist Binärzahl\n");
       else
          printf("zweiter Test ergab: Zeichenkette enthält unerlaubte Zeichen\n");
    
       return 0;
    };
    

    Eine kurze Erklärung zu dem Konstrukt

    char* string=const_cast<char*>("00010010101000100101a011");
    

    In C++ wird sehr genau zwischen Variablen und Konstanten unterschieden.
    Die Zeichenkette "00010010101000100101a011" stellt eine Konstante dar, während string eine Variable vom Typ 'char*' ist. Durch die Funktion 'const_cast<char*>' teile ich dem Compiler mit, daß ich bewußt diese Konstantheit entfernt haben möchte. Ohne diese Funktion beschwert sich der Compiler mit einer Warnung, daß die Umwandlung einer Stringkonstanten nach 'char*' veraltet (deprecated) ist.

    Nochmal zu meiner ersten Zeile und den Beschwerden des Compilers: Sieh nochmal in deine Hilfe zum Compiler und schalte die Ausgabe aller Fehler und Warnungen ein. Warnungen deuten oft auf logische Fehler hin. Versuche alle Warnungen zu verstehen und die entsprechenden Ursachen zu beseitigen, auch wenn das Programm scheinbar das macht, was es soll. Der Compiler gibt diese Warnungen nicht umsonst aus.

    Das war eine ganze Menge zu lesen und zu verstehen. Ich hoffe, dir geholfen zuhaben.
    Mein Standardwerk bei C++ ist 'Die C++ Programmiersprache' von Bjarne Stroustrup aus dem Verlag 'Addison-Wesley'. Wenn du mehr unter C++ mit Zeichenketten machen möchtest, schau dir doch mal die string-Klasse aus der 'Standard Template Library' (abgekürzt 'STL') an



  • Dir kann genauer geantwortet werden, wenn du folgende Angaben machst(hier ist ein ganz konkretes Mehr an Information sehr hilfreich:

    grundsetzliche Angaben:
    - das Betriebssystem, auf dem du arbeitest
    - der Compiler mit dem du arbeitest und dessen Version,
    denn einige Probleme tauchen nur bei bestimmten Betriebssystemen auf und auch dort nur bei bestimmten Compilern.

    Wenn du Code hier ins Forum stellst ist es für die Hilfe wichtig, wie du ihn verwendest. Viele Probleme werden erst im größeren Zusammenhang deutlich.

    Konstruiere also ein knappes Beispiel für die Verwendung deines Codeabschnittes. Dabei muß deutlich werden:
    - wie deklarierst du die in deinem Codeabschnitt verwendeten Variablen (Typ und Initialwert als Codefragment)?
    - Wie weist du diesen Variablen ihren Wert zu (in der Deklaration oder über eine Zuweisung - als Codefragment)?
    - Beschreibe mit deinen eigenen Worten, welchen Inhalt dise Variablen deiner Meinung nach ganz konkret haben (hier kann sich Vorstellung und Wirklichkeit unterscheiden). 'Konkret' heißt bezogen auf dein Problem zum Beispiel: die Variable 'buf' ist eine Zeichenkette mit dem Inhalt "000101000\0" oder "010011hhgghh\0"
    - gleiches gilt für die Inhalte der für dich interessanten Variablen am Ende deines Codeabschnittes (auch wenn es zunächst trivial erscheint, hier lauern subtile Fehlerquellen).

    Bei einer Funktion (wie hier von dir ins Netz gestellt) ist es wichtig zu wissen:
    - Wie und mit welchen Parametern rufst du diese Funktion auf? (sind die Parameter Variablen oder Konstanten, gilt das oben genannte - Deklaration und Zuweisung als Codeausschnitt sowie deine Vorstellung über den Inhalt mit deinen eigenen Worten)
    - Wie werden die Rückgabewerte verwendet (Zuweisung an eine andere Variable - dann auch angeben, wie diese deklariert ist - oder Verwendung als Bedingung in einer Schleife)?



  • Hallo,

    Da bin ich ja anscheinend auf einen Profi gestoßen. Vielen Dank für die ausführliche Hilfe. Jetzt funktioniert alles einwandfrei und ich hab einiges dazugelernt!

    mfg Christoph



  • Hallo,

    Leider hab ich jetzt noch ein kleines problem bei der Verarbeitung der Binärzahl. Es soll nun stdin (eine maximal 32bit große Binärzahl) in sbin eingelesen werden. Dann soll sbin in einen unsigned long integer (wegen 32bit) übernommen werden.
    Das ganze funktioniert bis zu 10bit (1111111111). Ab 11bit kommt irgendeine Zahl heraus. (-1773790777)

    Ich verwende eclipse mit MinGW unter vista x64.

    mfg Christoph

    int main()
    {
    	 char sbin[32+1];
    	 unsigned long int bin;
    
             scanf("%s",sbin);
    	 printf("%s\t",sbin);
    
    	 sscanf(sbin,"%li",&bin);
    	 printf("%li\t",bin);
    
    	 return 0;
    }
    


  • Hi!

    Mit sscanf geht das kaum mit einem Compiler binär. Was du suchst ist strtoul,
    guckst du hier:

    char sbin[64]; 
    	unsigned long num;
    
    	strcpy ( sbin, "11110000" );
    	num = strtoul ( sbin, NULL, 2 );
    	printf ( "%lu\n", num );
    


  • Hallo,

    Funktioniert mit strtoul leider auch nicht bei mehr als 10bit.

    mfg Christoph



  • symbian schrieb:

    Hallo,
    Funktioniert mit strtoul leider auch nicht bei mehr als 10bit.
    mfg Christoph

    ich glaube du weist nicht was ein Bit ist, 10 Bit = 2 hoch 10 also 1024 im Dezimalsystem d.h. du kannst mit 10 bit die Zahlen von 0-1023 darstellen, nur weil du 10 einser hintereinander sind das noch lange keine 10 Bit



  • Hallo,

    Sry mein Fehler. Ich bin mir nur leider nicht ganz klar bei folgender Formulierung:
    ...Programm, welches binäre Zahlen (bis max 32-bit) von stdin zeilenweise einliest.

    Sind damit 32 bit Zeichen (01), oder eine dezimal Zahl aus 32 bit (=2^32 = 4294967296 = 10 Zeichen) gemeint?

    mfg Christoph



  • Klappt doch:

    strcpy ( sbin, "11111111111" ); // 11 Bit
    	num = strtoul ( sbin, NULL, 2 );
    	printf ( "%lu\n", num );
    
    	strcpy ( sbin, "1111111111111111" ); // 16 Bit
    	num = strtoul ( sbin, NULL, 2 );
    	printf ( "%lu\n", num );
    
    	strcpy ( sbin, "11111111111111111111111111111111" ); // 32 Bit
    	num = strtoul ( sbin, NULL, 2 );
    	printf ( "%lu\n", num );
    

    Vielleicht machst du etwas anderes falsch.
    Achte auch auf den Formatstring; "%lu" steht z.B. für unsigned long int, "%li" für signed long int.
    Gruß,
    B.B.



  • So wie ich das sehe läuft es darauf hinaus daß du einen String einliest der aus einsen und nullen besteht. Z.B.

    1010010110 = 1*2^9 + 0*2^8 + 1*2^7 + 0*2^6 + 0*2^5 + 1*2^4 + 0*2^3 + 1*2^2 + 1*2^1 + 0*2^0
    

    das waren jetzt halt mal nur 10 Bit für 32 Bit analog so weitermachen, den String fährst du halt Zeichen für Zeichen durch und addiert die Teile.

    u_int CalcBuffer(char *buffer){
    
    	int	len, i;
    	u_int	result=0;
    
    	len = strlen(buffer);
    	if(len > 32){
    		sprintf("Your String is Scheissendreck);
    		return 0;
    	}
    	for(i = len - 1; i >= 0; i--){
    		if((buffer[i] < 48) || (buffer[i] > 49)){
    			sprintf("Your String is Scheissendreck);
    			return 0;
    		}
    		result += (buffer[i] - 48)*(u_int)pow(2, (double)i);
    	}
    	return result;
    }
    


  • Hallo,

    Leider kann ich diese Variante nicht verwenden (ich muss den gesamten String zur weiterverarbeitung als 32 stellige Zahl einlesen). macht also 10^32 als benötigten maximalwert für die zahl. 10^32 entspricht 2^106.3...
    Also benötige ich einen Ganzzahl-Datentyp mit min 107bit, den es nach meinem Wissen leider nicht gibt.

    mfg Christoph



  • @B.B.

    hab gerade gemekert, dass mein problem bei strtoul darin besteht, dass ich die funktion falsch interpretiert habe. strtoul interpretiert den string als binärzahl und konvertiert diesen. Ich möchte aber die binärzahl 1:1 übernehmen. also dass dann in bin auch wirklich die zahl 110 steht und nicht eine konvertierte 6.

    mfg Christoph



  • symbian schrieb:

    Ich möchte aber die binärzahl 1:1 übernehmen. also dass dann in bin auch wirklich die zahl 110 steht und nicht eine konvertierte 6.

    hä? Das System legt sowieso alles im Binäermodus. '6' ist nur eine Darstellung auf dem Bildschirm für die dummen Menschen, die mit '110' nichts anfangen können. Also Wert der Zahl mit ihrer Darstellung nicht verwechseln!

    strtoul("110", NULL, 2) liefert die 110 zurück. Was soll es sonst zurückgeben?

    edit: wenn du nur überprüfen willst, ob die Eingabe tatsächlich nur eine Binärzahl hat und sonst keine andere Zeichen, dann kannst folgendes machen:

    char *endptr;
    strtoul(eingabe, &endptr, 2);
    if(*endptr)
    {
        printf("Alter, gib nur eine Binärzahl ein ohne weitere Zeichen!!!\n");
        ....
    }
    

    [klugscheisser]
    btw: muss ich jede Woche erklären, wie strtoul benutzt wird?
    [/klugscheisser]



  • Hallo,

    Ich glaub wir reden ein wenig aneinander vorbei:
    Das Problem ist der Datentyp. Ich benötige minimum 107bit für eine 32stellige Zahl. strtoul liest aber in einen unsigned long int ein. der hat aber nur 32bit=10stellige Zahl. (4294967295).

    (wegen dem "konvertierungsproblem": strtoul(str,NULL,2) konvertiert. strtoul(str,NULL,0) liefert den wert 1:1.)

    mfg Christoph



  • symbian schrieb:

    Hallo,

    Leider kann ich diese Variante nicht verwenden (ich muss den gesamten String zur weiterverarbeitung als 32 stellige Zahl einlesen). macht also 10^32 als benötigten maximalwert für die zahl. 10^32 entspricht 2^106.3...
    Also benötige ich einen Ganzzahl-Datentyp mit min 107bit, den es nach meinem Wissen leider nicht gibt.

    mfg Christoph

    Nimmsu 4 unsigned long, z.B. als Array.
    Gruß,
    B.B.



  • symbian schrieb:

    Hallo,

    Ich glaub wir reden ein wenig aneinander vorbei:
    Das Problem ist der Datentyp. Ich benötige minimum 107bit für eine 32stellige Zahl. strtoul liest aber in einen unsigned long int ein. der hat aber nur 32bit=10stellige Zahl. (4294967295).

    (wegen dem "konvertierungsproblem": strtoul(str,NULL,2) konvertiert. strtoul(str,NULL,0) liefert den wert 1:1.)

    mfg Christoph

    strtoul mi 0 an Ende entscheidet autoamatisch, welches Zahlsystem er nimmt, bei einer 2 zwingst du dem System das dual System, das ist der große Unterschied.

    strtoull liefert ein unsigned long long int was in der Regel uint64_t darstellt. Wenn du aber 107 bits brauchst, dann gibt es keine Standardfunktion, die 107 bit breite Zahlen zurückliefert. Also muss du deine Eingabe selber splitten und 2 Mal strtoull aufrufen.



  • symbian schrieb:

    ... Ich benötige minimum 107bit für eine 32stellige Zahl. strtoul liest aber in einen unsigned long int ein. der hat aber nur 32bit=10stellige Zahl. (4294967295).
    mfg Christoph

    Wozu willst du den String in ein biäres Format quetschen?
    Wozu ist das gut?
    😕



  • Hallo Chritoph,

    Du möchtest also erreichen, daß überall wo deine Zeichenkette eine '1' enthält, das zugehörige Bit in der Integerzahl gesetzt wird und da, wo in deiner Zeichenkette eine '0' steht, das zugehörige Bit zurückgesetzt wird?

    Dann ist mir noch nicht gan klar, wie du auf eine Zahl mit 107 Bit kommst? Wie kommt die '107' zustande?

    Zur Klärung der Probleme hier ist wichtig zu wissen, was du mit der umgewandelten Zahl anstellen möchtest. Wenn du vor hast, mit was längerem als 'unsigned long' zu rechnen, dann viel Spaß. Die benötigten Funktionen mußt du dir dann wohl alle selbst schreiben oder eine gute Bibliothek finden. Aber ich glaube, das ist von dir nicht so gewollt.

    mfg Mario



  • Hallo,

    Ich möchte die eingelesenen (32 stelligen) Zahl verwenden um diese dann in decimal und hex zu konvertieren (ich weiß, dass es mit array funktioniert), aber ich würde es gerne numerisch machen:

    z.b. in dec

    for(bit=0,dec=0,exp=0;bin>0;)
    {
    		 bit=bin%10;
    		 bin/=10;
    		 for(i=0;i<exp;i++)bit*=2;
    		 dec+=bit;
    		 exp++;
    }
    

    Die 107bit kommen daher, dass die größte 32stellige Zahl 2^106.3 ist
    also 107bit benötigt um dargestellt zu werden.

    mfg christoph



  • Hallo Christoph,

    du bist dir sicher, daß du eine 32 stellige Dezimalzahl meinst?
    Bisher war immer die Rede von einer 32 stelligen Binärzahl. Und die paßt in einen unsigned long - ein Bit je Stelle.

    Willst du mit größeren Zahlen als 2^32 arbeiten, sieh dir mal die folgende Bibliothek an: GNU MP Bignum Library.
    Informationen zu dieser Bibliothek und die Bibliothek selbst findest du unter http://gmplib.org

    mfg Mario


Anmelden zum Antworten