String Functions

I l @ ve RuBoard

String Functions

The C library supplies several string-handling functions; ANSI C uses the string.h header file to provide the prototypes . We'll look at some of the most useful and common ones: strlen() , strcat() , strncat () , strcmp() , strncmp () , strcpy () , and strncpy () . We'll also examine sprintf() , supported by the stdio.h header file. For a complete list of the string.h family of functions, see Appendix F, "The Standard ANSI C Library."

The strlen() Function

The strlen() function, as you already know, finds the length of a string. It's used in the next example, a function that shortens lengthy strings:

 /* fit.c -- procrustean function */ void fit(char * string, unsigned int size) {     if (strlen(string) > size)         *(string + size) = ` 
  /* fit.c -- procrustean function */ void fit(char * string, unsigned int size) { if (strlen(string) > size) *(string + size ) = `\0'; }  
'; }

This function does change the string, so the function header doesn't use const in declaring the formal parameter string .

Try the fit() function in the test program of Listing 11.13.

Listing 11.13 The test.c program.
 /* test.c -- try the string-shrinking function */ #include <stdio.h> #include <string.h> /* contains string function prototypes */ void fit(char *, unsigned int); int main(void) {     char mesg[] = "Hold on to your hats, hackers.";     puts(mesg);     fit(mesg,7);     puts(mesg);     return 0; } void fit(char *string, unsigned int size) {     if (strlen(string) > size)         *(string + size) = ` 
  /* test.c -- try the string-shrinking function */ #include <stdio.h> #include <string.h> /* contains string function prototypes */ void fit(char *, unsigned int); int main(void) { char mesg[] = "Hold on to your hats, hackers."; puts(mesg); fit(mesg,7); puts(mesg); return 0; } void fit(char *string, unsigned int size) { if (strlen(string) > size) *(string + size) = `\0'; }  
'; }

The output is this:

 Hold on to your hats, hackers. Hold on 

The fit() function placed a `\0' character in the eighth element of the array, replacing a blank. The rest of the array is still there, but puts() stops at the first null character and ignores the rest of the array.

The ANSI string.h file contains function prototypes for the C family of string functions, so we will include it in our examples.

Note

Some pre-ANSI systems use strings.h instead, and others might lack a string header file entirely.


The strcat() and strncat() Functions

Listing 11.14 illustrates what strcat() can do.

Listing 11.14 The str_cat.c program.
 /* str_cat.c -- joins two strings */ #include <stdio.h> #include <string.h>  /* declares the strcat() function */ #define SIZE 80 int main(void) {    char flower[SIZE];    char addon[] = "s smell like old shoes.";    puts("What is your favorite flower?");    gets(flower);    strcat(flower, addon);    puts(flower);    puts(addon);    return 0; } 

This is the output:

 What is your favorite flower?  Rose  Roses smell like old shoes. s smell like old shoes. 

As you can see, strcat() (for str ing con cat enation) takes two strings for arguments. A copy of the second string is tacked onto the end of the first, and this combined version becomes the new first string. The second string is not altered . Function strcat() is type char * , that is, a pointer to char . It returns the value of its first argument ”the address of the first character of the string to which the second string is appended.

The strcat() function does not check to see whether the second string will fit in the first array. If you fail to allot enough space for the first array, you will run into problems as excess characters overflow into adjacent memory locations. Of course, you can use strlen() to look before you leap, as shown in Listing 11.15. Note that we add 1 to the combined lengths to allow space for the null character. Or you can use strncat() , which takes a second argument indicating the maximum number of characters to add. For example, strncat ( bugs , addon , 13 ) will add the contents of the addon string to bugs, stopping when it reaches 13 additional characters or the null character, whichever comes first. Therefore, counting the null character (which is appended in either case), the bugs array should be large enough to hold the original string (not counting the null character), a maximum of 13 additional characters, and the terminal null character. Listing 11.15 uses this information to calculate a value for the available variable, which is used as the maximum number of additional characters allowed.

Listing 11.15 The join_chk.c program.
 /* join_chk.c -- joins two strings, check size first */ #include <stdio.h> #include <string.h> #define SIZE 30 #define BUGSIZE 13 int main(void) {    char flower[SIZE];    char addon[] = "s smell like old shoes.";    char bug[BUGSIZE];    int available;    puts("What is your favorite flower?");    gets(flower);    if ((strlen(addon) + strlen(flower) + 1) <= SIZE)        strcat(flower, addon);    puts(flower);    puts("What is your favorite bug?");    gets(bug);    available = BUGSIZE - strlen(bug) - 1;    strncat(bug, addon, available);    puts(bug);    return 0; } 

Here is a sample run:

 What is your favorite flower?  Rose  Roses smell like old shoes. What is your favorite bug?  Aphid  Aphids smell 

The strcmp() and strncmp() Functions

Suppose you want to compare someone's response to a stored string, as shown in Listing 11.16.

Listing 11.16 The nogo.c program.
 /* nogo.c -- will this work? */ #include <stdio.h> #define ANSWER "Grant" int main(void) {     char try[40];     puts("Who is buried in Grant's tomb?");     gets(try);     while (try != ANSWER)     {         puts("No, that's wrong. Try again.");         gets(try);     }     puts("That's right!");     return 0; } 

As nice as this program might look, it will not work correctly. ANSWER and try really are pointers, so the comparison try != ANSWER doesn't check to see whether the two strings are the same. Rather, it checks to see whether the two strings have the same address. Because ANSWER and try are stored in different locations, the two addresses are never the same, and the user is forever told that he or she is wrong. Such programs tend to discourage people.

What you need is a function that compares string contents , not string addresses . You could devise one, but the job has been done for you with strcmp() (for string compa rison). This function does for strings what relational operators do for numbers . In particular, it returns if its two string arguments are the same. The revised program is shown in Listing 11.17.

Listing 11.17 The compare.c program.
 /* compare.c -- this will work */ #include <stdio.h> #include <string.h>   /* declares strcmp() */ #define ANSWER "Grant" #define MAX 40 int main(void) {     char try[MAX];     puts("Who is buried in Grant's tomb?");     gets(try);     while (strcmp(try,ANSWER) != 0)     {         puts("No, that's wrong. Try again.");         gets(try);     }     puts("That's right!");     return 0; } 

Note

Because any nonzero value is "true," some programmers would abbreviate the while statement to while (strcmp(try,ANSWER )).


One of the nice features of strcmp() is that it compares strings, not arrays. Although the array try occupies 40 memory cells and "Grant" only 6 (one for the null character), the comparison looks only at the part of try up to its first null character. Therefore, strcmp() can be used to compare strings stored in arrays of different sizes.

What if the user answers "GRANT" or "grant" or "Ulysses S. Grant" ? The user is told that he or she is wrong. To make a friendlier program, you have to anticipate all possible correct answers. There are some tricks. You can #define the answer as "GRANT" and write a function that converts all input to uppercase. That eliminates the problem of capitalization, but you still have the other forms to worry about. We leave that as an exercise for you.

The strcmp() Return Value

What value does strcmp() return if the strings are not the same? Listing 11.18 shows a sample.

Listing 11.18 The compback.c program.
 /* compback.c -- strcmp returns */ #include <stdio.h> #include <string.h> int main(void) {   printf("%d\n", strcmp("A", "A"));   printf("%d\n", strcmp("A", "B"));   printf("%d\n", strcmp("B", "A"));   printf("%d\n", strcmp("C", "A"));   printf("%d\n", strcmp("Z", "a"));   printf("%d\n", strcmp("apples", "apple"));   return 0; } 

Here is the output on one system:

 0 -1 1 1 -1 1 

Comparing "A" to itself returns a . Comparing "A" to "B" gives a -1 , and reversing the comparison gives a 1 . These results suggest that strcmp() returns a negative number if the first string precedes the second alphabetically and that it returns a positive number if the order is the other way. Therefore, comparing "C" to "A" gives a 1 . Other systems might return 2 ”the difference in ASCII code values. The ANSI standard says that strcmp() returns a negative number if the first string comes before the second alphabetically, returns a if they are the same, and returns a positive number if the first string follows the second alphabetically. The exact numerical values, however, are left open to the implementation.

What if the initial characters are identical? In general, strcmp() moves along until it finds the first pair of disagreeing characters. It then returns the corresponding code. For instance, in the very last example, "apples" and "apple" agree until the final s of the first string. This matches up with the sixth character in "apple" , which is the null character, ASCII 0. Because the null character is the very first character in the ASCII sequence, s comes after it, and the function returns a positive value.

The last comparison points out that strcmp() compares all characters, not just letters , so instead of saying the comparison is alphabetic, we should say that strcmp() goes by the machine collating sequence . That means characters are compared according to their numeric representation, typically the ASCII values. In ASCII, the codes for uppercase letters precede those for lowercase letters. Therefore, strcmp ( "Z" , "a" ) is negative.

Most often, you won't care about the exact value returned. You just want to know if it is zero or nonzero ”that is, whether there is a match or not ”or you might be trying to sort the strings alphabetically, in which case you want to know if the comparison is positive, negative, or zero.

Note

The strcmp() function is for comparing strings , not characters . So you can use arguments like "apples" and "A" , but you cannot use character arguments, such as `A' . However, recall that the char type is an integer type, so you can use the relational operators for character comparisons. Suppose word is a string stored in an array of char and that ch is a char variable. Then the following statements are valid:

 if (strcmp(word, "quit") == 0)     puts("Bye!"); if (ch == `q')     puts("Bye!"); 

However, don't use ch or `q ' as arguments for strcmp() .


Listing 11.19 uses the strcmp() function for checking to see whether a program should stop reading input.

Listing 11.19 The input.c program.
 /* input.c -- beginning of some program */ #include <stdio.h> #include <string.h> #define SIZE 81 #define LIM 100 continued on next page continued from previous page #define STOP "quit" int main(void) {   char input[LIM][SIZE];   int ct = 0;   printf("Enter up to %d lines (type quit to quit):\n");   while (ct < LIM && gets(input[ct]) != NULL &&          strcmp(input[ct],STOP) != 0)   {       ct++;   }   printf("%d strings entered\n", ct);   return 0; } 

This program quits reading input when it encounters an EOF character ( gets() returns null in that case), or when you enter the word quit , or when you reach the limit LIM .

Incidentally, sometimes it is more convenient to terminate input by entering an empty line, that is, by pressing the Enter key or Return key without entering anything else. To do so, you can modify the while loop control statement so that it looks like this:

 while (ct < LIM && gets(input[ct]) != NULL && input[ct][0] !=` 
  while (ct < LIM && gets(input[ct]) != NULL && input[ct][0] !=`\0')  
')

Here, input[ct] is the string just entered and input[ct][0] is the first character of that string. If the user enters an empty line, gets() places the null character in the first element, so the expression

 input[ct][0] != ` 
  input[ct][0] != `\0')  
')

tests for an empty input line.

The strncmp() Variation

The strcmp() function compares strings until it finds corresponding characters that differ , and that could take the search to the end of one of the strings. The strncmp() function compares the strings until they differ or until it has compared a number of characters specified by a third argument. For example, if you wanted to search for strings that begin with "astro" , you could limit the search to the first five characters. Listing 11.20 shows how.

Listing 11.20 The starsrch.c program.
 /* starsrch.c -- use strncmp() */ #include <stdio.h> #include <string.h> #define LISTSIZE 5 int main() {     char * list[LISTSIZE] = {             "astronomy", "astounding",             "astrophysics", "ostracize",             "asterism"      };     int count = 0;     int i;     for (i = 0; i < LISTSIZE; i++)         if (strncmp(list[i],"astro", 5) == 0)             count++;     printf("The list contained %d words beginning"            " with astro.\n", count);     return 0; } 

Here is the output:

 The list contained 2 words beginning with astro. 

The strcpy() and strncpy() Functions

We've said that if pts1 and pts2 are both pointers to strings, then the expression

 pts2 = pts1; 

copies only the address of a string, not the string itself. Suppose, though, that you do want to copy a string. Then you can use the strcpy() function. Listing 11.21 asks the user to enter words beginning with q . The program copies the input into a temporary array, and if the first letter is a q , the program uses strcpy() to copy the string from the temporary array to a permanent destination. The strcpy() function is the string equivalent of the assignment operator.

Listing 11.21 The copy1.c program.
 /* copy1.c -- strcpy() demo */ #include <stdio.h> #include <string.h>  /* declares strcpy() */ #define SIZE 40 #define LIM 5 int main(void) {    char qwords[LIM][SIZE];    char temp[SIZE];    int i = 0;    printf("Enter %d words beginning with q:\n", LIM);    while (i < LIM && gets(temp))    {       if (temp[0] != `q')          printf("%s doesn't begin with q!\n", temp);       else       {           strcpy(qwords[i], temp);           i++;       }     }    puts("Here are the words accepted:");    for (i = 0; i < LIM; i++)        puts(qwords[i]);    return 0; } 

Here is a sample run:

 Enter 5 words beginning with q:  quackery   quasar   quilt   quotient   no more  no more doesn't begin with q!  quiz  Here are the words accepted: quackery quasar quilt quotient quiz 

Note that the counter i is incremented only when the word entered passes the q test. Also note that the program uses a character-based test:

 if (temp[0] != `q') 

That is, is the first character in the temp array not a q ? Another possibility is using a string-based test:

 if (strncmp(temp, "q", 1) != 0) 

That is, are the strings temp and "q" different from each other in the first element?

Note that the string pointed to by the second argument ( temp ) is copied into the array pointed to by the first argument ( qword[i] ). The copy is called the target , and the original string is called the source . You can remember the order of the arguments by noting that it is the same as the order in an assignment statement: The target string is on the left.

 char target[20]; int x; x = 50;                    /* assignment for numbers */ strcpy(target, "Hi ho!");  /* assignment for strings */ target = "So long";        /* syntax error           */ 

It is your responsibility to make sure the destination array has enough room to copy the source. The following is asking for trouble:

 char * str; strcpy(str, "The C of Tranquility"); /* a problem */ 

The function will copy the string "The C of Tranquility" to the address specified by str , but str is uninitialized , so the copy might wind up anywhere .

In short, strcpy() takes two string pointers as arguments. The second pointer, which points to the original string, can be a declared pointer, an array name , or a string constant, but the first pointer, which points to the copy, should point to a data object, such as an array, roomy enough to hold the string.

Further strcpy() Properties

The strcpy() function has two more properties that you might find useful. First, it is type char * . It returns the value of its first argument ”the address of a character. Second, the first argument need not point to the beginning of an array; this lets you copy just part of an array. Listing 11.22 illustrates both these points.

Listing 11.22 The copy2.c program.
 /* copy2.c -- strcpy() demo */ #include <stdio.h> #include <string.h>             /* declares strcpy() */ #define WORDS  "beast" #define SIZE 40 int main(void) {    char *orig = WORDS;    char copy[SIZE] = "Be the best that you can be.";    char * ps;    puts(orig);    puts(copy);    ps = strcpy(copy + 7, orig);    puts(copy);    puts(ps);    return 0; } 

Here is the output:

 beast Be the best that you can be. Be the beast beast 

Note that strcpy() copies the null character from the source string. In this example, the null character overwrites the t in that in copy so that the new string ends with beast (see Figure 11.4). Also note that ps points to the eighth element (index of 7) of copy because the first argument is copy + 7 . Therefore, puts(ps) prints the string starting at that point.

Figure 11.4. strcpy() uses pointers.
graphics/11fig04.jpg
The Careful Choice: strncpy()

The strcpy() function shares a problem with gets() ” neither check to see whether the source string actually fits in the target string. The safer way to copy strings is to use strncpy() . It takes a third argument, which is the maximum number of characters to copy. Listing 11.23 is a rewrite of Listing 11.21, using strncpy() instead of strcpy() . To illustrate what happens if the source string is too large, it uses a rather small size (seven elements, six characters) for the target strings.

Listing 11.23 The copy3.c program.
 /* copy1.c -- strncpy() demo */ #include <stdio.h> #include <string.h>  /* declares strncpy() */ #define SIZE 40 #define TARGSIZE 7 #define LIM 5 int main(void) {    char qwords[LIM][TARGSIZE];    char temp[SIZE];    int i = 0;    printf("Enter %d words beginning with q:\n", LIM);    while (i < LIM && gets(temp))    {       if (temp[0] != `q')          printf("%s doesn't begin with q!\n", temp);       else       {           strncpy(qwords[i], temp, TARGSIZE - 1);           qwords[i][TARGSIZE - 1] = ` 
 /* copy1.c -- strncpy() demo */ #include <stdio.h> #include <string.h> /* declares strncpy() */ #define SIZE 40 #define TARGSIZE 7 #define LIM 5 int main(void) { char qwords[LIM][TARGSIZE]; char temp[SIZE]; int i = 0; printf("Enter %d words beginning with q:\n", LIM); while (i < LIM && gets(temp)) { if (temp[0] != `q') printf("%s doesn't begin with q!\n", temp); else { strncpy(qwords[i], temp, TARGSIZE - 1); qwords[i][TARGSIZE - 1] = `\0'; i++; } } puts("Here are the words accepted:"); for (i = 0; i < LIM; i++) puts(qwords[i]); return 0; } 
'; i++; } } puts("Here are the words accepted:"); for (i = 0; i < LIM; i++) puts(qwords[i]); return 0; }

Here is a sample run:

 Enter 5 words beginning with q:  quacik   quadratic   quisling   quota   quagga  Here are the words accepted: quack quadra quisli quota quagga 

The function call strncpy ( target , source , n ) copies up to n characters or up through the null character (whichever comes first) from source to target . Therefore, if the number of characters in source is less than n , the entire string is copied, including the null character. The function never copies more than n characters, so if it reaches the limit before reaching the end of the source string, no null character is added. As a result, the final string may or may not have a null character. For this reason, the program sets n to one less than the size of the target array and then sets the final element in the array to the null character:

 strncpy(qwords[i], temp, TARGSIZE - 1); qwords[i][TARGSIZE - 1] = ` 
 strncpy(qwords[i], temp, TARGSIZE - 1); qwords[i][TARGSIZE - 1] = `\0'; 
';

This ensures that you've stored a string. If the source string actually fits, the null character copied with it marks the true end of the string. If the source string doesn't fit, this final null character marks the end of the string.

The sprintf() Function

The sprintf() function is declared in stdio.h instead of string.h . It works like printf() , but it writes to a string instead of writing to a display. Therefore, it provides a way to combine several elements into a single string. The first argument to sprintf() is the address of the target string. The remaining arguments are the same as for printf() ”a conversion specification string followed by a list of items to be written.

Listing 11.24 uses sprintf() to combine three items (two strings and a number) into a single string. Note that it uses sprintf() the same way you would use printf() , except that the resulting string is stored in the array formal instead of being displayed on the screen.

Listing 11.24 The format.c program.
 /* format.c -- format a string */ #include <stdio.h> #define MAX 20 int main(void) {     char first[MAX];     char last[MAX];     char formal[2 * MAX + 10];     double prize;     puts("Enter your first name:");     gets(first);     puts("Enter your last name:");     gets(last);     puts("Enter your prize money:");     scanf("%lf", &prize);     sprintf(formal, "%s, %-19s: $%6.2f\n", last, first, prize);     puts(formal);     return 0; } 

Here's a sample run:

 Enter your first name:  Teddy  Enter your last name:  Behr  Enter your prize money:  450  Behr, Teddy       : 0.00 

The sprintf() command took the input and formatted it into a standard form, which it then stored in the string formal .

Other String Functions

The ANSI C library has more than 20 string-handling functions, and the following list summarizes some of the more commonly used ones:

  • char *strcpy(char * s1, const char * s2) ;

    This function copies the string (including the null character) pointed to by s2 to the location pointed to by s1 . The return value is s1 .

  • char *strncpy(char * s1, const char * s2, size_t n) ;

    This function copies to the location pointed to by s1 no more than n characters from the string pointed to by s2 . The return value is s1 . No characters after a null character are copied, and if the source string is shorter than n characters, the target string is padded with null characters. If the source string has n or more characters, no null character is copied. The return value is s1 .

  • char *strcat(char * s1, const char * s2) ;

    The string pointed to by s2 is copied to the end of the string pointed to by s1 . The first character of the s2 string is copied over the null character of the s1 string. The return value is s1 .

  • char *strncat(char * s1, const char * s2, size_t n) ;

    No more than the first n characters of the s2 string are appended to the s1 string, with the first character of the s2 string being copied over the null character of the s1 string. The null character and any characters following it in the s1 string are not copied, and a null character is appended to the result. The return value is s1 .

  • int strcmp(const char * s1, const char * s2) ;

    The function returns a positive value if the s1 string follows the s2 string in the machine collating sequence, the value if the two strings are identical, and a negative value if the first string precedes the second string in the machine collating sequence.

  • int strncmp(const char * s1, const char * s2, size_t n) ;

    This function works like strcmp() except that the comparison stops after n characters or when the first null character is encountered , whichever comes first.

  • char * strchr (const char * s, int c) ;

    This function returns a pointer to the first location in the string s that holds the character c . (The terminating null character is part of the string, so it can be searched for.) The function returns the null pointer if the character is not found.

  • char * strpbrk (const char * s1, const char * s2) ;

    This function returns a pointer to the first location in the string s1 that holds any character found in the s2 string. The function returns the null pointer if no character is found.

  • char * strrchr (const char * s, int c) ;

    This function returns a pointer to the last occurrence of the character c in the string s . (The terminating null character is part of the string, so it can be searched for.) The function returns the null pointer if the character is not found.

  • char * strstr (const char * s1, const char * s2) ;

    This function returns a pointer to the first occurrence of string s2 in string s1 . The function returns the null pointer if the string is not found.

  • size_t strlen(const char * s) ;

    This function returns the number of characters, not including the terminating null character, found in the string s .

Note that these prototypes use the keyword const to indicate which strings are not altered by a function. For example, consider the following:

 char *strcpy(char * s1, const char * s2); 

It means s2 points to a string that can't be changed, at least not by the strcpy() function, but s1 points to a string that can be changed. This makes sense, for s1 is the target string, which gets altered, and s2 is the source string, which should be left unchanged.

The size_t type is whatever type the sizeof operator returns. C states that the sizeof operator returns an integer type, but it doesn't specify which integer type, so size_t can be unsigned int on one system and unsigned long on another. Your string.h file defines size_t for your particular system or else refers to another header file having the definition.

As mentioned earlier, Appendix F, "The Standard ANSI C Library." lists all the functions in the string.h family. Many implementations provide additional functions beyond those required by the ANSI standard. You should check the documentation for your implementation to see what is available.

Now that we have outlined some string functions, let's look at a full program that handles strings.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net