452 lines
21 KiB
HTML
452 lines
21 KiB
HTML
<!--startcut ==========================================================-->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
|
|
<HTML>
|
|
<HEAD>
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
|
|
<META NAME="GENERATOR" CONTENT="Mozilla/4.03 [en] (X11; I; Linux 2.0.32 i586) [Netscape]">
|
|
<META NAME="Author" CONTENT="James M. Rogers">
|
|
<META NAME="Description" CONTENT="This article is the second in a series designed to explore the Standard C library implementation available for Linux">
|
|
<META NAME="Keywords" CONTENT="linux, standard c library, character functions">
|
|
<META NAME="Classification" CONTENT="Second Year Programming ">
|
|
<TITLE>The Standard C Library for Linux Issue 31</TITLE>
|
|
</HEAD>
|
|
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000FF" VLINK="#A000A0" ALINK="#FF0000">
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4>
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
Hello, I wrote the first of these
|
|
articles in January. I am planning on many more articles in the future
|
|
but have just finished moving my family to the Olympic Peninsula from Ohio
|
|
after having been hired to work as a systems programmer for the University
|
|
of Washington.
|
|
<P> <HR> <P>
|
|
|
|
<CENTER>
|
|
<H1><font color="maroon">
|
|
The Standard C Library for Linux</font></H1>
|
|
<H3><font color="navy">
|
|
Part Two: <stdio.h> character input/output</font></H3>
|
|
<H4>
|
|
By <A HREF="mailto:jrogers@u.washington.edu">James M. Rogers</A></H4></CENTER>
|
|
|
|
<P> <HR>
|
|
|
|
<P>The last article was on file operations in the standard input/output
|
|
library <stdio.h>. This article is on reading and writing characters,
|
|
strings and arrays to and from a stream. I am assuming a knowledge
|
|
of c programming on the part of the reader. There is no guarantee
|
|
of accuracy in any of this information nor suitability for any purpose.
|
|
|
|
<P>As an example of character based processing we will use a program that
|
|
reads the number of characters, words and lines of a file from standard
|
|
input and prints the results out to standard out. Any errors encountered
|
|
will be printed to standard error. This will be a weak version of
|
|
wc. (type `man wc` for more information on this UNIX utility program.
|
|
|
|
<P>The code examples given for each function will typically not run unless
|
|
the the <angle bracked> items are replaced with real code.
|
|
Normally these are things that have to be treated differently depending
|
|
on what you are trying to do. As always, if you see an error in my
|
|
documentation please tell me and I will correct myself in a later document.
|
|
|
|
<P><TT>-----------------------------------------------------------------</TT>
|
|
<BR><TT><FONT SIZE=-2> </FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>#include <stdio.h> /*
|
|
include the proper headers */</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2>#define IN 1
|
|
/* looking inside a word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>#define OUT 0
|
|
/* looking at white space */</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2>/* count the number of lines, words and characters
|
|
in standard input */</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2>main() {</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2> int c,
|
|
/* holds the character returned by getchar */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> characters,
|
|
/* the number of characters */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> lines,
|
|
/* the number of lines */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> words,
|
|
/* the number of words */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> state;
|
|
/* are we currently in or out of a word */</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2> /* initialize the count and set
|
|
the state to outside a word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> state=OUT;</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> characters = words = lines = 0;</FONT></TT>
|
|
|
|
<P><TT><FONT SIZE=-2> /* get one character at a time
|
|
from standard in, until EOF */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> while ((c = getchar()) != EOF)
|
|
{</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> characters++;
|
|
/* increment the count of characters */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> switch(c)
|
|
{</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> case '\n'
|
|
:</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
lines++; /* increment the
|
|
count of lines */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
state = OUT; /* new-line is white space, outside
|
|
word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
break;</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> case '
|
|
' :</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
state = OUT; /* space is white space, outside word
|
|
*/</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
break;</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> case '\t':</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
state = OUT; /* tab is white space, outsides word
|
|
*/</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
break;</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> default
|
|
: /*
|
|
otherwise we are in a word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
if (state == OUT) {</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
/* if state is still out and we are in a word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
/* then we are at the first letter of the word */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
state = IN; /* set the state to in */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
words++; /* increment the
|
|
count of words */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
}</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>
|
|
break;</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> }</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> }</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> /* print the results with
|
|
a formatted print statement */</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2> printf("%d %d %d\n", characters,
|
|
words, lines);</FONT></TT>
|
|
<BR><TT><FONT SIZE=-2>}</FONT></TT>
|
|
|
|
<P><TT>-----------------------------------------------------------------</TT>
|
|
<BR>
|
|
|
|
<P><B>To read a character from a stream,</B>
|
|
|
|
<P><TT> int getchar (void);</TT>
|
|
<BR><TT> int getc(FILE *stream);</TT>
|
|
<BR><TT> int fgetc(FILE *stream);</TT>
|
|
<BR><TT> int ungetc(int c, FILE *stream);</TT>
|
|
|
|
<P><TT>void </TT>this is left blank.
|
|
<BR><TT>FILE *stream </TT>is an already existing stream.
|
|
<BR><TT>int c </TT>is a character to be pushed back into the stream.
|
|
|
|
<P>These functions return an int with the value of the next character from
|
|
the stream. If there are no more characters then the end-of-file
|
|
indicator is set for the stream and the function returns EOF. If
|
|
there was a read error then the error indicator is set for the stream and
|
|
the function returns EOF.
|
|
|
|
<P><B>getchar</B> is used to read a single character from standard input.
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> while((c = <B>getchar()</B>) != EOF) {</TT>
|
|
<BR><TT> <while not
|
|
the end of file read and process each character></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>getc</B> is used to read a single character from a stream.
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "r")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> while((c = <B>getc(</B>stream<B>)</B>)
|
|
!= EOF) {</TT>
|
|
<BR><TT> <process each
|
|
character></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <do error handling></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>fgetc</B> is used to read a single character from a stream.
|
|
getchar and getc are written in terms of fgetc: <TT>getchar()</TT> is the
|
|
same as <TT>fgetc(STDIN)</TT> and <TT>getc(x)</TT> is the same as <TT>fgetc(x)</TT>.
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "r")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> while((c = <B>fgetc(</B>stream<B>)</B>)
|
|
!= EOF) {</TT>
|
|
<BR><TT> <process each
|
|
character></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <do error handling</TT>>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>ungetc</B> is used to push a character back into the stream when
|
|
you have read one character too many. This is a common problem for
|
|
compilers and pattern scanners. It is possible to push back more
|
|
than one character but this is not recommended as it is not portable.
|
|
An ungetc should follow a read and only push back a single character.
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "r")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> while((c = fgetc(stream))
|
|
!= EOF) {</TT>
|
|
<BR><TT> <process each
|
|
character></TT>
|
|
<BR><TT> if (some_condition)
|
|
{</TT>
|
|
<BR><TT>
|
|
<B>ungetc(</B>c,stream<B>);</B></TT>
|
|
<BR><TT>
|
|
break;</TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> do error handling</TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>To write a character to a stream,</B>
|
|
|
|
<P><TT> int putchar(int c);</TT>
|
|
<BR><TT> int putc(int c, FILE *stream);</TT>
|
|
<BR><TT> int fputc(int c, FILE *stream);</TT>
|
|
|
|
<P><TT>FILE *stream </TT>is an already existing stream.
|
|
<BR><TT>int c </TT>is the character to be written to the stream.
|
|
|
|
<P>These functions return the character written upon success. If
|
|
a write error occurs the error indicator is set for the stream and the
|
|
function returns an EOF.
|
|
|
|
<P><B>putchar</B> writes a character to standard out. <TT>putchar(x)</TT>
|
|
is the same as <TT>fputc(x, STDIN)</TT>
|
|
<BR>
|
|
<BR><TT> <B>putchar(</B>'x'<B>)</B>;</TT>
|
|
|
|
<P><B>putc</B> writes a character to the stream. <TT>putc(x,y)</TT>
|
|
is the same as <TT>fputc(x,y)</TT>
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> c='x';</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "w")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> <B>putc(</B>c, stream<B>)</B>;</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <error handling></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>fputc</B> writes a character to the stream.
|
|
|
|
<P><TT> int c;</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> c='y';</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "w")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> <B>fputc(</B>c, stream<B>)</B>;</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <error handling></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>To read a string from a stream,</B>
|
|
|
|
<P><TT> char *gets(char *s);</TT>
|
|
<BR><TT> char *fgets(char *s, int n, FILE *stream);</TT>
|
|
|
|
<P><TT>char *s </TT>the string that will hold the result.
|
|
<BR><TT>int n </TT>the maximum number of characters to read.
|
|
<BR><TT>FILE *stream </TT>is an already existing stream.
|
|
|
|
<P>If the read is successful then the pointer to s is returned. If
|
|
EOF is encountered and no characters have been read into the string then
|
|
the string remains unchanged and a null pointer is returned. If a
|
|
read error occurs then the string contents are possibly changed in an undefined
|
|
manner and a null pointer is returned.
|
|
|
|
<P><B>gets</B> reads from the stream into the string until the new line
|
|
character or end-of-file marker is reached. <B>Never use this function.
|
|
Use fgets instead.</B> There is no bounds checking to see if the
|
|
returned string fits into the space allowcated for it. Many applications
|
|
have been used as security holes in the past based on overwriting the end
|
|
of a string.
|
|
|
|
<P><B>fgets</B> reads at most <B>n</B> characters from the stream into
|
|
the string.
|
|
|
|
<P><TT> char s[1024];</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "r")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> while((<B>fgets(</B>s, 1023,
|
|
stream<B>)</B>) != (char *)0 ) {</TT>
|
|
<BR><TT> <process each
|
|
line></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <do fopen error handling></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>To write a string to a stream,</B>
|
|
|
|
<P><TT> int puts(const char *s);</TT>
|
|
<BR><TT> int fputs(const char *s, FILE *stream);</TT>
|
|
|
|
<P><TT>const char *s</TT>
|
|
<BR><TT>FILE *stream </TT>is an already existing stream.
|
|
|
|
<P>Returns a non-negative value upon success. Returns an EOF on a
|
|
write error.
|
|
|
|
<P><B>puts</B> writes the string pointed to by s to the stream STDIO and
|
|
appends a new-line to the end. The terminating null character is
|
|
not written to the stream.
|
|
|
|
<P><TT> char s[1024];</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> strcpy(s,"a typical string");</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "w")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> if(<B>puts(</B>s, stream<B>)</B>
|
|
== EOF ) {</TT>
|
|
<BR><TT> <handle error
|
|
on write></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <handle error on
|
|
open></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>fputs</B> writes the string pointed to by s to the named stream.
|
|
The terminating null character is not written to the stream.
|
|
|
|
<P><TT> char s[1024];</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> strcpy(s,"a typical string");</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "w")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> if(<B>fputs(</B>s, stream<B>)</B>
|
|
== EOF ) {</TT>
|
|
<BR><TT> <handle error
|
|
on write></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <handle error on
|
|
open></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>To read/write between arrays and streams,</B>
|
|
|
|
<P><TT> size_t fread(const void *ptr, size_t size, size_t
|
|
nmemb, FILE *stream);</TT>
|
|
<BR><TT> size_t fwrite(const void *ptr, size_t size,
|
|
size_t nmemb, FILE *stream);</TT>
|
|
|
|
<P><TT>const void *ptr </TT>is a pointer to the array.
|
|
<BR><TT>size_t size </TT>is the size of each element of the array
|
|
<BR><TT>size_t nmemb </TT>is the number of elements to be processed.
|
|
<BR><TT>FILE *stream </TT>is an already existing stream.
|
|
|
|
<P><B>fread</B> reads into the array pointed to by ptr, no more than nmemb
|
|
elements of the size size, from the stream. The function returns
|
|
the number of elements that were successfully read, this value can be less
|
|
than what was requested, if the function encounters a read failure or an
|
|
EOF. A read failure leaves the element that failed in an undefine
|
|
state. If size or nmemb are zero then the function returns a zero.
|
|
|
|
<P><TT> int a[10];</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "r")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> if (<B>fread(</B>a,
|
|
sizeof(a), 10, stream<B>)</B> < 10){</TT>
|
|
<BR><TT>
|
|
<handle a read error></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <handle a file open
|
|
error></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P><B>fwrite</B> writes from the array pointed to by ptr, no more than
|
|
nmeb elements of the size size, to the stream. The function returns
|
|
the number of elements successfully written, which should match nmemb only
|
|
if no write errors were encountered.
|
|
|
|
<P><TT> int a[10];</TT>
|
|
<BR><TT> FILE *stream;</TT>
|
|
<BR><TT> if((stream = fopen ("filename", "w")) != (FILE
|
|
*)0) {</TT>
|
|
<BR><TT> if (<B>fwrite(</B>a,
|
|
sizeof(a), 10, stream<B>)</B> < 10){</TT>
|
|
<BR><TT>
|
|
<handle a write error></TT>
|
|
<BR><TT> }</TT>
|
|
<BR><TT> } else {</TT>
|
|
<BR><TT> <handle a file open
|
|
error></TT>
|
|
<BR><TT> }</TT>
|
|
|
|
<P>
|
|
<HR>
|
|
<H4>
|
|
Bibilography:</H4>
|
|
<I>The ANSI C Programming Language, Second Edition</I>, Brian W. Kernighan,
|
|
Dennis M. Ritchie, Printice Hall Software Series, 1988
|
|
|
|
<P><I>The Standard C Library</I>, P. J. Plauger, Printice Hall P T R, 1992
|
|
|
|
<P><I>The Standard C Library, Parts 1, 2, and 3</I>, Chuck Allison, <I>C/C++
|
|
Users Journal</I>, January, February, March 1995
|
|
|
|
<P>STDIO(3), BSD MANPAGE, <I>Linux Programmer's Manual</I>, 29 November
|
|
1993
|
|
<p>
|
|
<P> <hr> <P>
|
|
<center><H4>Previous "The Standard C Library for Linux" Articles</H4></center>
|
|
<P><I><A HREF="http://www.linuxgazette.com/issue24/rogers.html">The Standard
|
|
C Library for Linux, Part One</A></I><A HREF="http://www.linuxgazette.com/issue24/rogers.html">,
|
|
James M. Rogers, January 1998</A>
|
|
|
|
<!--===================================================================-->
|
|
<P> <hr> <P>
|
|
<center><H5>Copyright © 1998, James M. Rogers <BR>
|
|
Published in Issue 31 of <i>Linux Gazette</i>, August 1998</H5></center>
|
|
|
|
<!--===================================================================-->
|
|
<P> <hr> <P>
|
|
<A HREF="./index.html"><IMG ALIGN=BOTTOM SRC="../gx/indexnew.gif"
|
|
ALT="[ TABLE OF CONTENTS ]"></A>
|
|
<A HREF="../index.html"><IMG ALIGN=BOTTOM SRC="../gx/homenew.gif"
|
|
ALT="[ FRONT PAGE ]"></A>
|
|
<A HREF="./richardson.html"><IMG SRC="../gx/back2.gif"
|
|
ALT=" Back "></A>
|
|
<A HREF="./lg_backpage31.html"><IMG SRC="../gx/fwd.gif" ALT=" Next "></A>
|
|
<P> <hr> <P>
|
|
<!--startcut ==========================================================-->
|
|
</BODY>
|
|
</HTML>
|
|
<!--endcut ============================================================-->
|