String-Bibliothek

C4CL 4.12 4.12.1 8. April 2013 Datenstrukturen/String-Bibliothek Vergleich von Strings: strcmp() Ein Programm soll zu einem eingegebenen Bauelemen...
7 downloads 0 Views 115KB Size
C4CL

4.12 4.12.1

8. April 2013

Datenstrukturen/String-Bibliothek Vergleich von Strings: strcmp()

Ein Programm soll zu einem eingegebenen Bauelement die Betriebsmittelkennzeichnung ausgeben. Ein erster Entwurf sieht so aus (src/betriebsmittel0.c): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

#include #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } p r i n tf (” Betriebsmittelkennzeichnung : ” ) ; if ( wort==” T r a n s i s t o r ” ) p r i n t f ( ”V\n” ) ; e l s e i f ( wort==” Widerstand ” ) p r i n t f ( ”R\n” ) ; e l s e i f ( wort==” Kondensator ” ) p r i n t f ( ”C\n” ) ; e l s e i f ( wort==” Spule ” ) p r i n t f ( ”L\n” ) ; else p r i n t f ( ” unbekannt \n” ) ; return 0 ; } Nun wird getestet: jk@r155pc1:˜$ gcc -o src/betriebsmittel0 betriebsmittel0.c jk@r155pc1:˜$ betriebsmittel0 Bauelement eingeben:Transistor Betriebsmittelkennzeichnung: unbekannt Warum wird keine Eingabe richtig erkannt? Warum werden gleiche Strings nicht als gleich angesehen? Die L¨ osung liegt darin, dass mit dem Zugriff auf den Namen der Arrays nur die Adressen verglichen werden und nicht die Inhalte. Hier noch ein Beispiel (src/vergleichsoperator_demo.c):

1 2 3 4 5

#include i nt main ( void ) { char f r u c h t 1 [ ] = ” K i r s c h e ” ; char f r u c h t 2 [ ] = ” K i r s c h e ” ;

6 7

i f ( f r u c h t 1==f r u c h t 2 ) p r i n t f ( ” g l e i c h \n” ) ; else p r i n t f ( ” u n g l e i c h \n” ) ;

8 9 10 11 12

i f ( ” Z i t r o n e ”==” Z i t r o n e ” ) p r i n t f ( ” g l e i c h \n” ) ; else p r i n t f ( ” u n g l e i c h \n” ) ; return 0 ;

13 14 15 16 17

} jk@r155pc1:˜$ gcc -o vergleichsoperator_demo src/vergleichsoperator_demo.c jk@r155pc1:˜$ ./vergleichsoperator_demo ungleich gleich 1

C4CL

8. April 2013

Die beiden Variablen, die zuerst verglichen werden, liegen an unterschiedliche Adressen, also sind sie ungleich. Die beiden literalen (w¨ortlichen) Stringkonstanten, die danach verglichen werden, liegen interessanterweise bei diesem Compiler (GNU-C) an derselben Adresse. Dadurch werden sie als gleich angesehen.1 Wenn man die Inhalte zweier Strings vergleichen will, braucht man also eine andere L¨osung. Sie besteht aus der Funktion strcmp() und einigen verwandten Funktionen. strcmp() wird ¨ in der C-Standard-Bibliothek mitgeliefert. Uber wird sie eingebunden. strcmp() erh¨alt zwei Strings (d¨ urfen auch konstant sein) als Parameter und gibt bei Gleichheit eine Null aus (Beispiel: src/strcmp_demo.c): 1 2 3 4 5 6 7 8 9

#include #include < s t r i n g . h> i nt main ( void ) { char s t r i n g 1 [ 2 1 ] = ” ” ; char s t r i n g 2 [ 2 1 ] = ” ” ; p r i n t f ( ” Zeichenkette 1 eingeben : ” ) ; s c a n f ( ” %20[ˆ\n ] ” , s t r i n g 1 ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) ;

10

p r i n t f ( ” Zeichenkette 2 eingeben : ” ) ; s c a n f ( ” %20[ˆ\n ] ” , s t r i n g 2 ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) ;

11 12 13 14

p r i n t f ( ” strcmp (\”% s \” ,\”% s \ ” ) e r g i b t %i \n” , s t r i n g 1 , s t r i n g 2 , strcmp ( s t r i n g 1 , s t r i n g 2 ) ) ; return 0 ;

15 16 17 18

} Bei Ungleichheit wird eine Zahl gr¨ oßer oder kleiner als null ausgegeben2 . Das Programm vom Beginn des Abschnitts kann damit so geschrieben werden (src/betriebsmittel1.c):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

#include #include < s t r i n g . h> #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } p r i n tf (” Betriebsmittelkennzeichnung : ” ) ; if ( strcmp ( wort , ” T r a n s i s t o r ”)==0) p r i n t f ( ”V\n” ) ; e l s e i f ( strcmp ( wort , ” Widerstand ”)==0) p r i n t f ( ”R\n” ) ; e l s e i f ( strcmp ( wort , ” Kondensator ”)==0) p r i n t f ( ”C\n” ) ; e l s e i f ( strcmp ( wort , ” Spule ”)==0) p r i n t f ( ”L\n” ) ; else p r i n t f ( ” unbekannt \n” ) ; return 0 ; } 1 Ein anderer Compiler, der vielleicht nicht merkt, dass die beiden Konstanten gleich sind, legt die beiden Strings einzeln an zwei verschiedene Adressen. Bei ihm ergibt der Vergleich dann eine Ungleichheit. 2 Je nachdem, ob der erste String in alphabetischer Sortierung hinter oder vor dem zweiten String kommt

2

C4CL

4.12.2

8. April 2013

Kopieren von Strings: strcpy()

Das Programm von oben soll eleganter gestaltet werden. Die Antwort soll in einen dritten String gegeben werden, der erst zum Schluss ausgegeben werden soll (src/betriebsmittel2.c): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

#include #include < s t r i n g . h> #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; char a ntwo r t [LEN+1]=” unbekannt ” ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } p r i n tf (” Betriebsmittelkennzeichnung : ” ) ; if ( strcmp ( wort , ” T r a n s i s t o r ”)==0) a ntwo r t=”V” ; e l s e i f ( strcmp ( wort , ” Widerstand ”)==0) a ntwo r t=”R” ; e l s e i f ( strcmp ( wort , ” Kondensator ”)==0) a ntwo r t=”C” ; e l s e i f ( strcmp ( wort , ” Spule ”)==0) a ntwo r t=”L” ; p r i n t f ( ”%s \n” , a ntwo r t ) ; return 0 ; } Der Test ergibt: jk@r155pc1:˜$ gcc -o src/betriebsmittel2 betriebsmittel2.c betriebsmittel2.c: In function ’main’: betriebsmittel2.c:11: error: incompatible types when assigning to type ’char[81]’ from type ’char *’ Stimmt, man darf an ein Array (also auch an einen String) nichts zuweisen. Aber auch f¨ ur dieses Problem gibt es eine L¨ osung aus , n¨amlich die Funktion strcpy(). Auch strcpy() bekommt zwei Strings als Parameter, der erst davon muss eine Variable sein. Dann wird der zweite String in den ersten kopiert (Beispiel: src/strcpy_demo.c):

1 2 3 4 5 6 7

#include #include < s t r i n g . h> i nt main ( void ) { i nt c ; char s2 [ 4 0 0 1 ] = ” ” ; char s1 [ 5 ] = ”” ;

8

p r i n t f ( ” Z e i c h e n k e t t e nach s2 e i n g e b e n (max . 4 Z e i c h e n ) : ” ) ; s c a n f ( ” %4000[ˆ\ n ] ” , s2 ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { }

9 10 11 12

p r i n t f ( ” k o p i e r e s2 nach s1 mit A u f r u f s t r c p y ( s1 , s2 ) ; \ n” ) ; s t r c p y ( s1 , s2 ) ; p r i n t f ( ” I n h a l t von s1 : >>%s%sLala>Lala #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; char a ntwo r t [LEN+1]=” unbekannt ” ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } p r i n tf (” Betriebsmittelkennzeichnung : ” ) ; if ( strcmp ( wort , ” T r a n s i s t o r ”)==0) s t r c p y ( antwort e l s e i f ( strcmp ( wort , ” Widerstand ”)==0) s t r c p y ( antwort e l s e i f ( strcmp ( wort , ” Kondensator ”)==0) s t r c p y ( antwort e l s e i f ( strcmp ( wort , ” Spule ”)==0) s t r c p y ( antwort p r i n t f ( ”%s \n” , a ntwo r t ) ; return 0 ; }

4.12.3

, ”V” ) ; , ”R” ) ; , ”C” ) ; , ”L” ) ;

Verketten von Strings: strcat()

Nun soll das Programm – ganz nach dem Anspruch des E-V-A-Prinzips – alle Ausgaben im Antwortstring sammeln und erst zum Schluss mit einem printf()-Befehl ausgeben. Hier ist die erste Version (src/betriebsmittel4.c): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

#include #include < s t r i n g . h> #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; char a ntwo r t [LEN+1]=” unbekannt ” ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } s t r c p y ( antwort , ” B e t r i e b s m i t t e l k e n n z e i c h n u n g : ” ) ; a ntwo r t=a ntwo r t+wort ; if ( strcmp ( wort , ” T r a n s i s t o r ”)==0) a ntwo r t=a ntwo r t+”V” ; e l s e i f ( strcmp ( wort , ” Widerstand ”)==0) a ntwo r t=a ntwo r t+”R” ; e l s e i f ( strcmp ( wort , ” Kondensator ”)==0) a ntwo r t=a ntwo r t+”C” ; e l s e i f ( strcmp ( wort , ” Spule ”)==0) a ntwo r t=a ntwo r t+”L” ; else a ntwo r t=a ntwo r t+” unbekannt ” ; p r i n t f ( ”%s \n” , a ntwo r t ) ; return 0 ; 4

C4CL

20

8. April 2013

} Dieses Programm enth¨ alt offensichtlich an mehreren Stellen denselben Fehler: Man kann Strings in C nicht einfach addieren. Gesucht wird eine Funktion, die einen String an einen anderen anh¨angt. Wieder gibt es eine Funktion aus , die diese Aufgabe l¨ost: strcat() heißt sie. Sie erh¨alt (wie strcpy()) zwei Strings als Parameter, von denen der erste variabel sein muss. Dann wird der zweite String an das Ende des ersten Strings kopiert (Beispiel: src/strcat_demo.c):

1 2 3 4 5 6 7 8

#include #include < s t r i n g . h> i nt main ( void ) { i nt c ; char s2 [ 1 1 ] = ” ” ; char s1 [ 1 1 ] = ” ” ;

9 10

p r i n t f ( ” Z e i c h e n k e t t e nach s1 e i n g e b e n (max . 10 Z e i c h e n ) : ” ) ; s c a n f ( ” %10[ˆ\n ] ” , s1 ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { }

11 12 13

p r i n t f ( ” Z e i c h e n k e t t e nach s2 e i n g e b e n (max . 10 Z e i c h e n ) : ” ) ; s c a n f ( ” %10[ˆ\n ] ” , s2 ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { }

14 15 16 17 18

p r i n t f ( ” haenge s2 an s1 an mit A u f r u f s t r c a t ( s1 , s2 ) ; \ n” ) ; s t r c a t ( s1 , s2 ) ; p r i n t f ( ” I n h a l t von s1 : >>%s%sToastBrot>Brot #define LEN 80 i nt main ( void ) { char wort [LEN+1]=” ” ; char a ntwo r t [LEN+ 1 ] ; p r i n t f ( ” Bauelement e i n g e b e n : ” ) ; s c a n f ( ”%80 s ” , wort ) ; while ( g e t c h a r ( ) ! = ’ \n ’ ) { } s t r c p y ( antwort , ” B e t r i e b s m i t t e l k e n n z e i c h n u n g : ” ) ; 5

C4CL

s t r c a t ( antwort , wort ) ; if ( strcmp ( wort , ” T r a n s i s t o r ”)==0) e l s e i f ( strcmp ( wort , ” Widerstand ”)==0) e l s e i f ( strcmp ( wort , ” Kondensator ”)==0) e l s e i f ( strcmp ( wort , ” Spule ”)==0) else p r i n t f ( ”%s \n” , a ntwo r t ) ; return 0 ;

12 13 14 15 16 17 18 19 20

8. April 2013

s t r c a t ( antwort s t r c a t ( antwort s t r c a t ( antwort s t r c a t ( antwort s t r c a t ( antwort

, ”V” ) ; , ”R” ) ; , ”C” ) ; , ”L” ) ; , ” unbekannt ” ) ;

}

4.12.4

Anf¨ ange vergleichen mit strncmp()

Die Funktion strncmp() ist eine Erweiterung zu strcmp(): Man kann damit die Anzahl der Zeichen, die verglichen werden sollen, begrenzen: 1

x=strncmp ( ” E i s z e i t ” , ” Eisenbahn ” , 3 ) ; In diesem Beispiel werden nur die ersten drei Buchstaben verglichen, und damit erh¨alt x den Wert null (=gleich). 4.12.5

Anfang kopieren mit strncpy()

Wesentlich h¨ aufiger wird eine Funktion ben¨otigt, die nur den Anfang von s2 nach s1 kopiert. Die bisherige Funktion strcpy() hat n¨ amlich einen Nachteil, der nicht sofort auff¨allt: jk@r155pc1:˜$ ./strcpy_demo Zeichenkette nach s2 eingeben (max. 4 Zeichen):123456789 kopiere s2 nach s1 mit Aufruf strcpy(s1,s2); Inhalt von s1: >>123456789>6789>1234_123456789>123456789>ABCDE12345>1234567890 i nt main ( void ) { char ∗p ; char s t r i n g [ ] = ”Das i s t das Haus , w e l c h e s dem N i k o l a u s g e h o e r t ” ; char t r e n n e r [ ] = ” , ; ” ; p r i n t f ( ” s t r t o k (\”% s \” ,\”% s \ ” ) e r g i b t : ” , s t r i n g , t r e n n e r ) ; p=s t r t o k ( s t r i n g , t r e n n e r ) ; p r i n t f ( ”>>%s%s