Size: 7395
Comment:
|
Size: 8960
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 165: | Line 165: |
where the program prints the user prompt, the user types in ''10'', and the program then outputs the count to ''10''. Great. | where the program prints the user prompt, the user types in ''10'', and the program then outputs the count to ''10''. * ''that looks fine'' |
Line 172: | Line 173: |
You see here that the ''10'' generated by the ''echo'' does not appear on the screen: you just see the output of the program. | You see here that the ''10'' generated by the ''echo'' does not appear on the screen: you just see the output of the program * ''which is sort-of messed up'' |
Line 176: | Line 178: |
1. the ''UNIX way'' is usually to use command line arguments | 1. the ''UNIX way'' is to use command line arguments |
Line 185: | Line 187: |
* these rarely happen in this course | * these don't happen often But, for the sake of completeness, here is a program that * reads a number from a file ''input.txt'' * writes the count from 1 to that number to the file ''output.txt'' * it is ''user-friendly'' :) : it tells the user that an output file has been created {{{#!cplusplus // files.c // read a number 'num' from a file input.txt // write a count from 1 to 'num' to the file OUT #define IN "input.txt" #define OUT "output.txt" #include <stdio.h> #include <stdlib.h> #define NUMDIG 6 int main(void) { FILE *fpi, *fpo; // these are file pointers char s[NUMDIG]; fpi = fopen(IN, "r"); if (fpi == NULL) { // an important check fprintf(stderr, "Can't open %s\n", IN); return EXIT_FAILURE; } else { int num; if (fscanf(fpi, "%d", &num) != 1) { fprintf(stderr, "No number found in %s\n", IN); return EXIT_FAILURE; } else { fpo = fopen(OUT, "w"); if (fpo == NULL) { // an important check fprintf(stderr, "Can't create %s!\n", OUT); return EXIT_FAILURE; } else { fprintf(fpo, "%s", "Counts\n"); for (int i=1; i<=num; i++) { sprintf(s, "%d", i); fprintf(fpo, "%s\n", s); } fclose(fpo); printf("file %s created\n", OUT); return EXIT_SUCCESS; } } } } }}} |
Line 229: | Line 285: |
The vast majority of program can be written just using these I/O system calls | {{{#!wiki note The vast majority of program can be written just using these library I/O calls |
Line 234: | Line 291: |
}}} |
Contents
C I/O
Program input
There are 3 main sources of input for programs:
- from the command line
you get access to data on the command line by using argc and argv[][]
from standard input (also called stdin)
stdin can be the keyboard, a data file, or the output of another program
- from an 'internally-defined' file
open a file, use fscanf(), and don't forget to close the file
1. command line
To read from the command line:
include argc and argv in your parameter list for main().
use sscanf() to read the arguments (it stands for 'string scanf()')
the first argument of a sscanf() is a string
Here is a program that counts from 1 to num, where num is provided by the user on the command line
1 // countc.c
2 // reads an integer from the command line and counts
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 int main(int argc, char *argv[]) {
7 int num = 0;
8 if (argc < 2 || sscanf(argv[1], "%d", &num) != 1) { // num is defined here
9 fprintf(stderr,"Usage: %s integer\n", argv[0]);
10 return EXIT_FAILURE;
11 }
12 for (int i=1; i<=num; i++) {
13 printf("%d ", i);
14 }
15 printf("\n");
16 return EXIT_SUCCESS;
17 }
Notice the program prints a 'Usage' message if an integer argument is missing (discussed in next session)
To execute the program:
prompt$ dcc -o countc countc.c prompt$ ./count Usage: ./countc integer prompt$ ./countc !t#q Usage: ./countc integer prompt$ ./countc 10 1 2 3 4 5 6 7 8 9 10
2. standard input
To read from standard input (usually called simply stdin)
a scanf() is used (instead of sscanf())
a scanf() misses the string argument of a sscanf()
so where does num comes from?
... the default 'channel' stdin
1 //counts.c
2 // reads an integer from stdin and counts
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 int main(void) {
7 int num;
8 if (scanf("%d", &num) != 1) {
9 fprintf(stderr, "Usage: a number expected\n");
10 return EXIT_FAILURE;
11 }
12 // the rest of the program is exactly the same as the command-line version
13 for (int i=1; i<=num; i++) {
14 printf("%d ",i);
15 }
16 printf("\n");
17 return EXIT_SUCCESS;
18 }
Notice the Usage message this time is simpler than the command-line version above
... because we did not declare argc and argv, and so cannot use argv[0] this time!!
- we could have if we wanted to of course
There are many ways to 'test' a program that reads stdin.
- Using the keyboard
prompt$ dcc -o counts counts.c prompt$ ./counts 10 1 2 3 4 5 6 7 8 9 10
where the integer 10 was typed on the keyboard by the user, and the program generates the count from 1 to 10.
Using a data file, input.txt say, which contains the integer 10 (followed by a newline).
prompt$ more input.txt 10 prompt$ ./counts < input.txt 1 2 3 4 5 6 7 8 9 10
Using a pipe command. A pipe command joins the stdout of a program to the stdin of another program. If we have a program called write10.c:
then we can pipe its stdout to the stdin of our counting program
prompt$ dcc -o write10 write10.c prompt$ dcc -o counts counts.c prompt$ ./write10 | ./counts 1 2 3 4 5 6 7 8 9 10
But you can actually generate a string much more easily in UNIX using echo
prompt$ echo "10" | ./counts 1 2 3 4 5 6 7 8 9 10
User prompting
You can still use a 'user prompt' when you use stdin but it messes up the output.
1 // counts+.c
2 // reads an integer from stdin and counts
3 // prompts the user
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 int main(void) {
8 int num;
9 printf("Please input a number: "); // this line added to counts.c
10 if (scanf("%d", &num) != 1) {
11 fprintf(stderr, "Usage: a number expected\n");
12 return EXIT_FAILURE;
13 }
14 for (int i=1; i<=num; i++) {
15 printf("%d ",i);
16 }
17 printf("\n");
18 return EXIT_SUCCESS;
19 }
results in
prompt$ ./counts+ Please input a number: 10 1 2 3 4 5 6 7 8 9 10
where the program prints the user prompt, the user types in 10, and the program then outputs the count to 10.
that looks fine
If you instead use a pipe as input, then you do not see what the input is
prompt$ echo "10" | ./counts+ Please input a number: 1 2 3 4 5 6 7 8 9 10
You see here that the 10 generated by the echo does not appear on the screen: you just see the output of the program
which is sort-of messed up
User prompts are not used often in UNIX because:
the UNIX way is to use command line arguments
it doesn't fit well into stdin/stdout framework (as we saw above)
3. A file
A program can open and close, and read from, and write to, a file that is 'internally' defined.
This is generally done when you have large volumes of stored data, or complex data (such as structs) or non-printable data.
- these don't happen often
But, for the sake of completeness, here is a program that
reads a number from a file input.txt
writes the count from 1 to that number to the file output.txt
it is user-friendly
: it tells the user that an output file has been created
1 // files.c
2 // read a number 'num' from a file input.txt
3 // write a count from 1 to 'num' to the file OUT
4
5 #define IN "input.txt"
6 #define OUT "output.txt"
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #define NUMDIG 6
12
13 int main(void) {
14 FILE *fpi, *fpo; // these are file pointers
15 char s[NUMDIG];
16
17 fpi = fopen(IN, "r");
18 if (fpi == NULL) { // an important check
19 fprintf(stderr, "Can't open %s\n", IN);
20 return EXIT_FAILURE;
21 }
22 else {
23 int num;
24 if (fscanf(fpi, "%d", &num) != 1) {
25 fprintf(stderr, "No number found in %s\n", IN);
26 return EXIT_FAILURE;
27 }
28 else {
29 fpo = fopen(OUT, "w");
30 if (fpo == NULL) { // an important check
31 fprintf(stderr, "Can't create %s!\n", OUT);
32 return EXIT_FAILURE;
33 }
34 else {
35 fprintf(fpo, "%s", "Counts\n");
36 for (int i=1; i<=num; i++) {
37 sprintf(s, "%d", i);
38 fprintf(fpo, "%s\n", s);
39 }
40 fclose(fpo);
41 printf("file %s created\n", OUT);
42 return EXIT_SUCCESS;
43 }
44 }
45 }
46 }
Program output
There are two standard output 'streams', standard output stdout and standard error stderr.
- both are normally defined as the screen
The general form for a print statement is fprintf(stream, ...), where stream can be stdout, stderr or a user-defined file.
the call printf(...) is the same as fprintf(stdout, ...) and is default to the screen but may be re-directed
a fprintf(stderr, ...) is usually reserved for serious errors
for example, in countc.c above the 'Usage' message went to stderr:
1 fprintf(stderr,"Usage: %s integer\n", argv[0]);
you could argue over whether 'bad' usage is a serious error (maybe just a printf would have been enough)
There is 'systematic' naming here:
standard input is scanf(),
if you read from a string then use sscanf(), where the first argument is the string
standard output is printf(),
if you write to a file then use fprintf(), where the first argument is a stream
Like stdin, we can re-direct stdout to a file. For example:
dcc -o counts counts.c ./counts > output.txt 10
(where the integer 10 is input by the user) will result in the count from 1 to 10 going to the file output.txt
If you create a data file input.txt that contains the string 10, then the following will generate the same output text file.
./counts < input.txt > output.txt
As we saw before, you can let echo generate data and use that in a pipe. This also generates the same output text file.
echo "10" | ./counts > output.txt
Input/output design considerations
The vast majority of program can be written just using these library I/O calls
scanf() to read from stdin
sscanf() to read from the command line
printf() to write to stdout
fprintf() to write to stderr