Sei sulla pagina 1di 19

Strings

Stings in C are stored as null character, '\0', terminated character arrays. This means
that the length of a string is the number of characters it contains plus one to store
the null character. Common string operations include finding lengths, copying,
searching, replacing and counting the occurrences of specific characters and words.
Here is a simple way to determine the length of a string.

#include <stdio.h>

int main()
{
char sentence[] = "Hello World";
int count = 0;
int i;

for (i = 0; sentence[i] != '\0'; i++)


{
count++;
}
printf("The string %s has %d characters ",
sentence,count);
printf("and is stored in %d bytes\n",
count+1);

return 0;
}

Each character within the array sentence is compared with the null character
terminator until the end of the string is encountered. This technique can be
generalized to search for any character.

As an example, here is a program that counts the occurrences of each lowercase


letter of the alphabet in a string.

#include <stdio.h>

int main()
{
int i,j;
char buffer[120]; /* Holds input strings */
char *pt; /* A pointer to data type char */
char alphabet[27] = "abcdefghijklmnopqrstuvwxyz";
int alphaCount[26]; /* an array of counts */

/* Initialize counts */
for (i = 0; i < 26; i++)
{
alphaCount[i] = 0;
}

while ((pt = gets(buffer)) != NULL)


{
for (i = 0; i < 120 && buffer[i] != '\0'; i++)
{
for (j = 0; j < 26; j++)
{
if (buffer[i] == alphabet[j])
{
alphaCount[j]++;
}
}
}
}

for (i = 0; i < 26; i++)


{
printf("Found %d occurrences of %c\n",
alphaCount[i],alphabet[i]);
}

return 0;
}

gets reads a line of input into a character array. Its prototype is:
char *gets(char *buffer);

It returns a pointer to the character string if successful, or NULL if end of file is


reached or if an error has occurred. The string is also read into the character array
specified as an argument. The character string will be terminated be a "\0", which is
standard for C.

The first step in this program is to initialize the count array. When you use an
automatic variable, you must initialize it. Otherwise, it contains whatever data
happened to be at the memory location it is stored in. This will be discussed further
in the lesson on variable scope.

The while loop in this program reads lines of input until an end of file is encountered.
If you run this program, remember that an end of file, EOF, is entered as a control-d
or a control-z from you keyboard. This is, after you type the last line of text, type
control-z or control-d to enter the End of File character.

For each line read in, each character in the line is compared against each letter in the
alphabet. If a match is found the appropriate count is incremented. The last step in
this program writes out the results.
Practice:
1) Type in the above program. Compile and run.
2) Modify the above program to count the occurrences of the digits 0-9, rather than
letters.
Solution
#include <stdio.h>

int main()
{
int i,j;
char buffer[120]; /* Holds input strings */
char *pt; /* A pointer to data type char */
char digits[11] = "0123456789";
int digitCount[10]; /* an array of counts */

/*Initialize counts */
for (i = 0; i < 10; i++)
{
digitCount[i] = 0;
}

while ((pt = gets(buffer)) != NULL)


{
for (i = 0; i < 120 && buffer[i] != '\0'; i++)
{
for (j = 0; j < 10; j++)
{
if (buffer[i] == digits[j])
{
digitCount[j]++;
}
}
}
}

for (i = 0; i < 10; i++)


{
printf("Found %d occurences of %c\n",
digitCount[i],digits[i]);
}

return 0;
}

Let's look at an example of using fgets, and then we'll talk about some pitfalls to watch out for.

For a example:
#include <stdio.h>

int main()
{
/* A nice long string */
char string[256];

printf( "Please enter a long string: " );

/* notice stdin being passed in */


fgets ( string, 256, stdin );

printf( "You entered a very long string, %s", string );

getchar();
}
Remember that you are actually passing the address of the array when you pass string because
arrays do not require an address operator (&) to be used to pass their addresses, so the values in
the array string are modified.

The one thing to watch out for when using fgets is that it will include the newline character ('\n')
when it reads input unless there isn't room in the string to store it. This means that you may need
to manually remove the input. One way to do this would be to search the string for a newline and
then replace it with the null terminator. What would this look like? See if you can figure out a way to
do it before looking below:

char input[256];
int i;

fgets( input, 256, stdin );

for ( i = 0; i < 256; i++ )


{
if ( input[i] == '\n' )
{
input[i] = '\0';
break;
}
}

Here, we just loop through the input until we come to a newline, and when we do, we replace it with
the null terminator. Notice that if the input is less than 256 characters long, the user must have hit
enter, which would have included the newline character in the string! (By the way, aside from this
example, there are other approaches to solving this problem that use functions from string.h.)

Manipulating C strings using string.h


string.h is a header file that contains many functions for manipulating strings. One of these is the
string comparison function.

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


strcmp will accept two strings. It will return an integer. This integer will either be:
Negative if s1 is less than s2.
Zero if s1 and s2 are equal.
Positive if s1 is greater than s2.
Strcmp performs a case sensitive comparison; if the strings are the same except for a difference in
cAse, then they're countered as being different. Strcmp also passes the address of the character
array to the function to allow it to be accessed.

char *strcat ( char *dest, const char *src );


strcat is short for "string concatenate"; concatenate is a fancy word that means to add to the end,
or append. It adds the second string to the first string. It returns a pointer to the concatenated
string. Beware this function; it assumes that dest is large enough to hold the entire contents of src
as well as its own contents.

char *strcpy ( char *dest, const char *src );


strcpy is short for string copy, which means it copies the entire contents of src into dest. The
contents of dest after strcpy will be exactly the same as src such that strcmp ( dest, src ) will return
0.

size_t strlen ( const char *s );


strlen will return the length of a string, minus the termating character ('\0'). The size_t is nothing to
worry about. Just treat it as an integer that cannot be negative, which is what it actually is. (The
type size_t is just a way to indicate that the value is intended for use as a size of something.)

Here is a small program using many of the previously described functions:

#include <stdio.h> /* stdin, printf, and fgets */


#include <string.h> /* for all the new-fangled string functions */

/* this function is designed to remove the newline from the end of a string
entered using fgets. Note that since we make this into its own function, we
could easily choose a better technique for removing the newline. Aren't
functions great? */
void strip_newline( char *str, int size )
{
int i;

/* remove the null terminator */


for ( i = 0; i < size; ++i )
{
if ( str[i] == '\n' )
{
str[i] = '\0';

/* we're done, so just exit the function by returning */


return;
}
}
/* if we get all the way to here, there must not have been a newline! */
}

int main()
{
char name[50];
char lastname[50];
char fullname[100]; /* Big enough to hold both name and lastname */
printf( "Please enter your name: " );
fgets( name, 50, stdin );

/* see definition above */


strip_newline( name, 50 );

/* strcmp returns zero when the two strings are equal */


if ( strcmp ( name, "Alex" ) == 0 )
{
printf( "That's my name too.\n" );
}
else
{
printf( "That's not my name.\n" );
}
// Find the length of your name
printf( "Your name is %d letters long", strlen ( name ) );
printf( "Enter your last name: " );
fgets( lastname, 50, stdin );
strip_newline( lastname, 50 );
fullname[0] = '\0';
/* strcat will look for the \0 and add the second string starting at
that location */
strcat( fullname, name ); /* Copy name into full name */
strcat( fullname, " " ); /* Separate the names by a space */
strcat( fullname, lastname ); /* Copy lastname onto the end of fullname
*/
printf( "Your full name is %s\n",fullname );

getchar();

return 0;
}

Library Functions for String Manipulation


To use any of these functions in your code, the header file "strings.h" must be
included. It is the programmer's responsibility to check that the destination array is
large enough to hold either what is copied or appended to it. C performs no error
checking in this respect. See the array tutorial for more details. The data type size_t
is equivalent to unsigned integer.

strcpy
strcpy copies a string, including the null character terminator from the source string
to the destination. This function returns a pointer to the destination string, or a NULL
pointer on error. Its prototype is:

char *strcpy(char *dst, const char *src);

strncpy
strncpy is similar to strcpy, but it allows the number of characters to be copied to be
specified. If the source is shorter than the destination, than the destination is padded
with null characters up to the length specified. This function returns a pointer to the
destination string, or a NULL pointer on error. Its prototype is:

char *strncpy(char *dst, const char *src, size_t len);

strcat
This function appends a source string to the end of a destination string. This function
returns a pointer to the destination string, or a NULL pointer on error. Its prototype
is:

char *strcat(char *dst, const char *src);

strncat
This function appends at most N characters from the source string to the end of the
destination string. This function returns a pointer to the destination string, or a NULL
pointer on error. Its prototype is:

char *strncat(char *dst, const char *src, size_t N);

strcmp
This function compares two strings. If the first string is greater than the second, it
returns a number greater than zero. If the second string is greater, it returns a
number less than zero. If the strings are equal, it returns 0. Its prototype is:

int strcmp(const char *first, const char *second);

strncmp
This function compares the first N characters of each string. If the first string is
greater than the second, it returns a number greater than zero. If the second string
is greater, it returns a number less than zero. If the strings are equal, it returns 0.
Its prototype is:

int strncmp(const char *first, const char *second, size_t N);

strlen
This function returns the length of a string, not counting the null character at the
end. That is, it returns the character count of the string, without the terminator. Its
prototype is:

size_t strlen(const char *str);

memset
memset is useful to initialize a string to all nulls, or to any character. It sets the first
N positions of the sting to the value passed. The destination may be specified by a
pointer to any type, char, int, float, etc. A void pointer is used to allow different types
to be specified. Its prototype is:

void *memset(const void *dst,int c, site_t N);

Here is a simple program to illustrate the use of some of these functions.

#include <stdio.h>
#include <string.h>

int main()
{
char str1[] = "Hello";
char str2[] = "World";
char str3[20];
char *cpt;
int length;
int rc;

/* Find length of str1 */


length = strlen(str1);
printf("String 1, %s, is of length %d\n",str1,length);

/* Copy str1 to str3, print both */


printf("\nCopying str1 to str3.\n");
if ((cpt = strcpy(str3,str1)) == NULL)
{
printf("Error on strcpy");
return 1;
}
printf("String 1 is %s\n",str1);
printf("String 3 is %s\n",str3);

/* Clear str3 */
memset(str3,'\0',20);
printf("\nstr3 is cleared\n");

/* Copy first 2 characters of str1 to str3 */


printf("Copying the first two characters of str1 to str3\n");
strncpy(str3,str1,2);
printf("String 1 is %s\n",str1);
printf("String 3 is %s\n",str3);

/* Compare the first 2 characters of str1, str3 */


printf("\nComparing the first two characters of str1 and str3\n");
if ((rc = strncmp(str1,str3,2)) == 0)
{
printf("First two characters of str1 and str3 match\n");
}
else
{
printf("First two characters of str1 and str3 don't match\n");
}

/* Compare all characters of str1 and str3 */


printf("\nComparing all characters of str1 and str3\n");
rc = strcmp(str1,str3);
if (rc == 0)
{
printf("str1 equals str3\n");
}
else if (rc > 0)
{
printf("str1 is greater\n");
}
else /* rc < 0 */
{
printf("str3 is greater\n");
}

/* Append to "he" in str3 */


printf("\nAppending to str3\n");
str3[2] = 'y';
str3[3] = ' ';
strcat(str3,str2);
printf("%s\n",str3);

return 0;
}

In this example error checking on the library calls was not done in order to simplify
the presentation. As an example, only the first call was checked.

Here are the results. #include <stdio.h>

int main()
{
int i,j;
char buffer[120]; /* Holds input strings */
char *pt; /* A pointer to data type char */
char digits[11] = "0123456789";
int digitCount[10]; /* an array of counts */

/*Initialize counts */
for (i = 0; i < 10; i++)
{
digitCount[i] = 0;
}

while ((pt = gets(buffer)) != NULL)


{
for (i = 0; i < 120 && buffer[i] != '\0'; i++)
{
for (j = 0; j < 10; j++)
{
if (buffer[i] == digits[j])
{
digitCount[j]++;
}
}
}
}

for (i = 0; i < 10; i++)


{
printf("Found %d occurences of %c\n",
digitCount[i],digits[i]);
}

return 0;
}Here is a simple program to illustrate the use of some of these functions.

#include <stdio.h>
#include <string.h>

int main()
{
char str1[] = "Hello";
char str2[] = "World";
char str3[20];
char *cpt;
int length;
int rc;

/* Find length of str1 */


length = strlen(str1);
printf("String 1, %s, is of length %d\n",str1,length);

/* Copy str1 to str3, print both */


printf("\nCopying str1 to str3.\n");
if ((cpt = strcpy(str3,str1)) == NULL)
{
printf("Error on strcpy");
return 1;
}
printf("String 1 is %s\n",str1);
printf("String 3 is %s\n",str3);

/* Clear str3 */
memset(str3,'\0',20);
printf("\nstr3 is cleared\n");

/* Copy first 2 characters of str1 to str3 */


printf("Copying the first two characters of str1 to str3\n");
strncpy(str3,str1,2);
printf("String 1 is %s\n",str1);
printf("String 3 is %s\n",str3);

/* Compare the first 2 characters of str1, str3 */


printf("\nComparing the first two characters of str1 and str3\n");
if ((rc = strncmp(str1,str3,2)) == 0)
{
printf("First two characters of str1 and str3 match\n");
}
else
{
printf("First two characters of str1 and str3 don't match\n");
}

/* Compare all characters of str1 and str3 */


printf("\nComparing all characters of str1 and str3\n");
rc = strcmp(str1,str3);
if (rc == 0)
{
printf("str1 equals str3\n");
}
else if (rc > 0)
{
printf("str1 is greater\n");
}
else /* rc < 0 */
{
printf("str3 is greater\n");
}

/* Append to "he" in str3 */


printf("\nAppending to str3\n");
str3[2] = 'y';
str3[3] = ' ';
strcat(str3,str2);
printf("%s\n",str3);

return 0;
}
In this example error checking on the library calls was not done in order to simplify
the presentation. As an example, only the first call was checked.

#include <stdio.h>

int main()
{
int i,j;
char buffer[120]; /* Holds input strings */
char *pt; /* A pointer to data type char */
char digits[11] = "0123456789";
int digitCount[10]; /* an array of counts */

/*Initialize counts */
for (i = 0; i < 10; i++)
{
digitCount[i] = 0;
}

while ((pt = gets(buffer)) != NULL)


{
for (i = 0; i < 120 && buffer[i] != '\0'; i++)
{
for (j = 0; j < 10; j++)
{
if (buffer[i] == digits[j])
{
digitCount[j]++;
}
}
}
}

for (i = 0; i < 10; i++)


{
printf("Found %d occurences of %c\n",
digitCount[i],digits[i]);
}

return 0;
}

As an example, here is a program that counts the occurrences of each lowercase


letter of the alphabet in a string.
#include <stdio.h>

int main()
{
int i,j;
char buffer[120]; /* Holds input strings */
char *pt; /* A pointer to data type char */
char alphabet[27] = "abcdefghijklmnopqrstuvwxyz";
int alphaCount[26]; /* an array of counts */

/* Initialize counts */
for (i = 0; i < 26; i++)
{
alphaCount[i] = 0;
}

while ((pt = gets(buffer)) != NULL)


{
for (i = 0; i < 120 && buffer[i] != '\0'; i++)
{
for (j = 0; j < 26; j++)
{
if (buffer[i] == alphabet[j])
{
alphaCount[j]++;
}
}
}
}

for (i = 0; i < 26; i++)


{
printf("Found %d occurrences of %c\n",
alphaCount[i],alphabet[i]);
}

return 0;
}

gets reads a line of input into a character array. Its prototype is:
char *gets(char *buffer);

It returns a pointer to the character string if successful, or NULL if end of file is


reached or if an error has occurred. The string is also read into the character array
specified as an argument. The character string will be terminated be a "\0", which is
standard for C.
The first step in this program is to initialize the count array. When you use an
automatic variable, you must initialize it. Otherwise, it contains whatever data
happened to be at the memory location it is stored in. This will be discussed further
in the lesson on variable scope.

The while loop in this program reads lines of input until an end of file is encountered.
If you run this program, remember that an end of file, EOF, is entered as a control-d
or a control-z from you keyboard. This is, after you type the last line of text, type
control-z or control-d to enter the End of File character.

For each line read in, each character in the line is compared against each letter in the
alphabet. If a match is found the appropriate count is incremented. The last step in
this program writes out the results.

Using Strings
Strings are useful for holding all types of long input. If you want the user to input his or her name,
you must use a string. Using scanf() to input a string works, but it will terminate the string after it
reads the first space, and moreover, because scanf doesn't know how big the array is, it can lead to
"buffer overflows" when the user inputs a string that is longer than the size of the string (which acts
as an input "buffer").

There are several approaches to handling this problem, but probably the simplest and safest is to
use the fgets function, which is declared in stdio.h.

The prototype for the fegets function is:

char *fgets (char *str, int size, FILE* file);


There are a few new things here. First of all, let's clear up the questions about that funky FILE*
pointer. The reason this exists is because fgets is supposed to be able to read from any file on disk,
not just from the user's keyboard (or other "standard input" device). For the time being, whenever
we call fgets, we'll just pass in a variable called stdin, defined in stdio.h, which refers to "standard
input". This effectively tells the program to read from the keyboard. The other two arguments to
fgets, str and size, are simply the place to store the data read from the input and the size of the
char*, str. Finally, fgets returns str whenever it successfully read from the input.

When fgets actually reads input from the user, it will read up to size - 1 characters and then place
the null terminator after the last character it read. fgets will read input until it either has no more
room to store the data or until the user hits enter. Notice that fgets may fill up the entire space
allocated for str, but it will never return a non-null terminated string to you.

Let's look at an example of using fgets, and then we'll talk about some pitfalls to watch out for.

For a example:

#include <stdio.h>

int main()
{
/* A nice long string */
char string[256];

printf( "Please enter a long string: " );


/* notice stdin being passed in */
fgets ( string, 256, stdin );

printf( "You entered a very long string, %s", string );

getchar();
}
Remember that you are actually passing the address of the array when you pass string because
arrays do not require an address operator (&) to be used to pass their addresses, so the values in
the array string are modified.

The one thing to watch out for when using fgets is that it will include the newline character ('\n')
when it reads input unless there isn't room in the string to store it. This means that you may need
to manually remove the input. One way to do this would be to search the string for a newline and
then replace it with the null terminator. What would this look like? See if you can figure out a way to
do it before looking below:

char input[256];
int i;

fgets( input, 256, stdin );

for ( i = 0; i < 256; i++ )


{
if ( input[i] == '\n' )
{
input[i] = '\0';
break;
}
}

Here, we just loop through the input until we come to a newline, and when we do, we replace it with
the null terminator. Notice that if the input is less than 256 characters long, the user must have hit
enter, which would have included the newline character in the string! (By the way, aside from this
example, there are other approaches to solving this problem that use functions from string.h.)

To obtain a line of text from the user via the keyboard, there are a few standard functions
available. These include:

char *fgets(char *s, int size, FILE *stream);
int scanf( const char *format, ...);
char *gets(char *s);

The first on the list, fgets(), it the best function to use, as it provides control over how many
characters are actually read into memory. The scanf() function can be used to do this too, but
it is really designed for use with formatted input. By this I mean a data stream that is in a
guaranteed format, which is something a user with a keyboard can rarely provide with any
accuracy The gets() function is old, and should not be used. It doesn't control the number of
characters it reads in, and will happily overwrite memory it doesn't own if the user provides an
unexpectedly long line of input.

fgets() reads in at most one less than size characters from stream and stores them into the
buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is
stored into the buffer. A '\0' is stored after the last character in the buffer.
One problem that people find with fgets(), is that it may place the newline character \n
([Enter]) into the buffer. That is, if there is enough room to store the newline in the array, it
will. This can cause problems with string comparisons, file names, and a number of other
things you use your input for. An example of this problem would be passing the array to the
fopen() function to open a file stream. The newline character would be taken as part of the file
name itself, and the operating system would look for a file named myfile.dat\n. Of course,
when debugging, it can be easy to miss this, as the variable contents is printed normally on
the screen.

Here is an example of how to use fgets(). It shows how to remove the newline from the end of
the array. Remember, it is important to check for the existence of this character before wiping
it out with a \0, as it is not always there.

Note the position of the < and > signs in the output. These help show the presence of the
newline character in the buffer.

#include <stdio.h> 
#include <string.h> 

int main()
{
  char buf[BUFSIZ];
  char *p;
  
  printf ("Please enter a line of text, max %d characters\n", sizeof(buf));
  
  if (fgets(buf, sizeof(buf), stdin) != NULL)
  {
    printf ("Thank you, you entered >%s<\n", buf);
    
    /*
     *  Now test for, and remove that newline character
     */
    if ((p = strchr(buf, '\n')) != NULL)
      *p = '\0';
      
    printf ("And now it's >%s<\n", buf);
  }
  
  return 0;
}

/*
 Program output:

Please enter a line of text, max 512 characters
this is a test
Thank you, you entered >this is a test
<
And now it's >this is a test<
*/
/*
 * This code is written in C, but it could just as easily be done in C++.
 * The rand() and srand() functions are available in both languages.
 */
 
#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  

int main(void)
{
  int i;
  
  srand(time(NULL));
  
  i = rand();
  
  printf ("Your random number is %d\n", i);  

  printf ("This compiler can generate random numbers from 0 to %d\n", RAND_MAX);

  return(0);
}
#include <stdio.h> 
#include <string.h> 

int main()
{
  char buf[BUFSIZ];
  char *p;
  
  printf ("Please enter a line of text, max %d characters\n", sizeof(buf));
  
  if (fgets(buf, sizeof(buf), stdin) != NULL)
  {
    printf ("Thank you, you entered >%s<\n", buf);
    
    /*
     *  Now test for, and remove that newline character
     */
    if ((p = strchr(buf, '\n')) != NULL)
      *p = '\0';
      
    printf ("And now it's >%s<\n", buf);
  }
  
  return 0;
}

/*
 Program output:

Please enter a line of text, max 512 characters
this is a test
Thank you, you entered >this is a test
<
And now it's >this is a test<
*/
This is system dependent as the most portable solution relies on the keyboard codes that map
to the directional keys. Next you need a raw input function that takes a single character such
as getch (a UNIX implementation is given later in this FAQ "How can I get input without
having the user hit [Enter]?"). To get the correct codes for your system, you must search by
way of a simple input function:

int get_code ( void )
{
  int ch = getch();

  if ( ch == 0 || ch == 224 )
    ch = 256 + getch();

  return ch;
}

Then build a list of key code constants so that they can be easily changed if the program is
ported:

enum
{
  KEY_UP    = 256 + 72,
  KEY_DOWN  = 256 + 80,
  KEY_LEFT  = 256 + 75,
  KEY_RIGHT = 256 + 77
};

Then it is only a matter of testing for those codes in your program and performing the correct
action:

#include <stdio.h> 
#include <conio.h> 

/* System dependent key codes */
enum
{
  KEY_ESC     = 27,
  ARROW_UP    = 256 + 72,
  ARROW_DOWN  = 256 + 80,
  ARROW_LEFT  = 256 + 75,
  ARROW_RIGHT = 256 + 77
};

static int get_code ( void )
{
  int ch = getch();

  if ( ch == 0 || ch == 224 )
    ch = 256 + getch();

  return ch;
}

int main ( void )
{
  int ch;

  while ( ( ch = get_code() ) != KEY_ESC ) {
    switch ( ch ) {
    case ARROW_UP:
      printf ( "UP\n" );
      break;
    case ARROW_DOWN:
      printf ( "DOWN\n" );
      break;
    case ARROW_LEFT:
      printf ( "LEFT\n" );
      break;
    case ARROW_RIGHT:
      printf ( "RIGHT\n" );
      break;
    }
  }

  return 0;
}

An alternative solution to this problem is the Win32 API:

#include <stdio.h> 
#include <limits.h> 
#include <stdlib.h> 
#include <windows.h> 

int main ( void )
{
  short esc = 0;

  while ( !esc ) {
    esc = GetAsyncKeyState ( VK_ESCAPE );

    if ( GetAsyncKeyState ( VK_UP ) & SHRT_MAX )
      puts ( "Up arrow is pressed" );
    else if ( GetAsyncKeyState ( VK_DOWN ) & SHRT_MAX )
      puts ( "Down arrow is pressed" );
    else if ( GetAsyncKeyState ( VK_LEFT ) & SHRT_MAX )
      puts ( "Left arrow is pressed" );
    else if ( GetAsyncKeyState ( VK_RIGHT ) & SHRT_MAX )
      puts ( "Right arrow is pressed" );
  }

  return EXIT_SUCCESS;
}

Potrebbero piacerti anche