由标准库(Standard Library)支持的输入输出模型是非常简单的。文本的输入与输出是以字符流(streams of characters)的形式被处理的。 一个字符流(text stream)是一序列(sequence)被分至数行的字符。每一行都由零个或者多个字符(character)构成, 并且每一行末跟了一个换行符(newline character)。 库(Libraby)的职责就是使得每一个输入流或者输出流满足这个模型。
标准库(Standard Libraby)提供了一系列用于一次读写单个字符的函数(function),而其中函数getchar以及putchar是最简单的。
每次被呼叫的时候,getchar会在一个文本流(text stream)中读取下一个输入字节(input character)然后将其作为函数返回值返回。
字符们通常情况下来自于键盘输入。
函数putchar是在每次被呼叫的时候打印(print)一个字符。 通常是打印在屏幕(screen)上的。


1. 文件拷贝(File Copying)

借着getcharputchar函数, 你可以在没有很多输入输出相关知识加持的情况下写出很多实用的代码。

书中给了一个例子 - Version 1:

#include <stdio.h>

main() {  
    int heihei; 

    heihei = getchar(); 
    while(heihei != EOF) {
        putchar(heihei); 
        heihei = getchar(); 
    }
}

在计算机内部,数据是以Bit的形式被储存起来的。 我们在这里的目的是要输入和输出字符。 理所当然的, 我们会想到用类型char来完成这项工作。 但是值得注意的是, 我们该如何判断再无有效输入了? 我们势必要有一个值去帮助我们判断, 啊, 是不是到底啦? 那么这个值势必要区分于任何其他有效的字符值(character)。 这个值我们称为EOF("end of file"的简称)。所以我们程序中的变量heihei的类型必须能涵盖所有getchar函数能返回的值的范围。 因此我们在定义变量heihei的时候是选择了类型int的。
EOF是一个在<stdio.h>中被定义的整数(Integer), 但这个值不同于任何一个字符的值(我自己输出了下, 得到EOF的值是-1), 因此就能无错地达到我们的要求。

#include <stdio.h> 

main() {  
    printf("%d", EOF); 
}

当然, 上述的例子 - Version 1还可以更加精简, 因为比如在C中, 任何一个赋值, 例如: heihei = getchar()是一个表达式(expression)并且有对应的值。 而这意味着一个赋值可以作为一个大的或者说复杂的的表达式的一部分。

书中提供的例子 - Version 2

#include <stdio.h>

main() {  
    int heihei; 

    while ((heihei = getchar()) != EOF) {
        putchar(heihei); 
    }
}

在此, 需要注意的是, while语句中的条件(condition)中这样的表达方式由于优先性需要对heihei = getchar(), 否则会有执行上顺序的错误。

2 数字符(Character Counting)

书中例子 - Version 1

#include <stdio.h>

main() {  
    long counts; 

    counts = 0; 
    while (getchar() != EOF) 
        ++counts; 
    printf("%ld\n", counts); 
}

自己感受下。

书中例子 - Version 2

#include <stdio.h> 

main() {  
    double counts; 

    for (counts = 0; getchar() != EOF; ++counts)
        ;
    printf("%.0f\n", counts); 
}

这里要注意的是, 例子2中, for语句,没有自己的代码块, 是以分号结尾的, 这个分号叫做null语句(这个不知道该怎么翻译), 用来满足for语句需要自己的代码这个需求。

3 数行数(Line Counting)

开头我们已了解到, 标准库确保了输入文本流作为一序列的行(简而言之: 数行)的形式存在,而每行结尾都是跟一个换行符, 那么我们要数行数的话, 只要数有多少个'\n', 那就代表有多少行了。
看书中例子:

#include <stdio.h> 

main() {  
    int c, counts_nl; 
    counts_nl=0; 
    while ((c=getchar()) != EOF) 
        if (c == '\n') 
            ++counts_nl; 
    printf("%d\n", counts_nl); 
}

程序部分很简单。
要注意的是, 单个字符被两个'单引号括起来, 其结果是一个整数, 并且是一个等于这个字符在计算机字符集(character set)里对应字符的数值的整数。

书后练习题1.8:

Write a programm to count blanks, tabs and newlines.

我写的版本:

#include <stdio.h>

main() {  
    long counts_blank, counts_tab, counts_newline; 
    counts_blank = counts_tab = counts_newline = 0;  
    int c;  
    int limit; 

    while (((c=getchar()) != EOF) && ((limit=counts_blank+counts_tab+counts_newline)<=10)) {
        switch (c) { 
            case ' ': 
                counts_blank++; 
                break; 
            case '\t': 
                counts_tab++; 
                break; 
            case '\n':
                counts_newline++; 
                break; 
            default: 
                ;
        }
    }
    printf("Blanks: %ld Tab: %ld Newline: %ld\n", counts_blank, counts_tab, counts_newline); 
}

书后练习题1.9:

Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.

我写的版本:

#include <stdio.h>

main() {  
    int c; 

    while ((c=getchar()) != EOF) {
        if (c ==' ') {
            while ((c=getchar()) == ' ')
                ;
            putchar(' '); 
        }
        putchar(c); 
    }
}

书后练习题1.10:

Write a program to copy its input to its output, replacing each tab by \t, each backspace by \b, and each backslash by \. This makes tabs and backspaces visible in an unambiguous way.

我写的版本:

#include <stdio.h> 

main() {  
    int c; 

    while ((c=getchar()) != EOF) {
        switch (c) {
            case '\t': 
                putchar('\\'); 
                putchar('t'); 
                break; 
            case '\b': 
                putchar('\\'); 
                putchar('b'); 
                break; 
            case '\\':
                putchar('\\'); 
                putchar('\\'); 
                break; 
            default: 
                putchar(c);  
        }
    }
}

4 数单词(Word Counting)

书中提供了一个UNIX程序wc的简化版本用来数行数,数单词以及数字符。
代码如下,可以认真读一读再自己上手写一写。

#include <stdio.h>  
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
main()  
{
    int c, nl, nw, nc, state;
    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
}

书后练习题1-12:

Write a program that prints its input one word per line.

我写的版本:

#include <stdio.h> 

#define INSIDE 1 // Inside a Word
#define OUTSIDE 0 // Outside a Word

main() {  
    int c, state; 

    state = OUTSIDE; 
    while ((c = getchar()) != EOF) {
        if ((c == ' ') || (c == '\n') || (c == '\t')) {
            if (state == INSIDE) putchar('\n'); 
            state = OUTSIDE; 
        } else {
            if (state == OUTSIDE) state = INSIDE; 
            putchar(c);             
        }
    }
}

参考: 《The C Programming Language》- Chapter 1.5