# 1.5 字符输入输出

### 1.5.1 字符输入输出 <a href="#j7scy" id="j7scy"></a>

主要讨论 getchar() 和 putchar()

在实际的小程序中去学习这些规则，掌握规则很重要，然后去理解规则是怎么定义的，这是一个循序渐进的过程。

Exercise 1-7. Write a program to print the value of EoF.

{% code title="1.5.1 EOF.c" lineNumbers="true" %}

```c
# include <stdio.h>
int main()
{
    printf("EOF is %d\n", EOF);
    return 0;
}
```

{% endcode %}

<figure><img src="https://labspc.com/wp-content/uploads/2024/01/1705654589-word-image-260-1.png" alt=""><figcaption></figcaption></figure>

增加程序可移植性，EOF 的值的多少和具体环境也有关系，这里显示我这是 `-1`.

在这里我们要编写三个实用程序：

Exercise 1-8. Write a program to count blanks, tabs, and newlines. 统计

{% code title="1.5.2 count-nb-nt-nl.c" lineNumbers="true" %}

```c
#include <stdio.h>

int main()
{
    int c, nl, nb, nt;
    nl = 0;
    nb = 0;
    nt = 0;
    while ((c = getchar()) != EOF)
    {
        if (c == '\n')
            ++nl;
        else if (c == ' ')
            ++nb;
        else if (c == '\t')
            ++nt;
    }
    printf("nl = %d, nb = %d, nt = %d\n", nl, nb, nt);
    return 0;
}
```

{% endcode %}

Exercise 1-9. Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank. 将输入复制到输出，并将其中连续到多个空格用一个空格代替。

{% code title="1.5.3 getline-copy.c" lineNumbers="true" %}

```c
#include <stdio.h>

#define NONEBLANK 'a'

int main() 
{
    // 定义变量c和last
    int c, last;
    // 将last赋值为NONEBLANK
    last = NONEBLANK;
    // 循环读取字符
    while ((c = getchar()) != EOF)
    {
        // 如果当前字符不是空格
        if (c != ' ')
            // 将当前字符打印出来
            putchar(c);
        // 如果当前字符是空格
        else if (last != ' ')
            // 将当前字符打印出来
            putchar(c);
        // 将当前字符赋值给last
        last = c;
    }
    // 返回0
    return 0;
}
```

{% endcode %}

这个程序中间有几步，其实我还不是不太懂。 第二个 if 在处理多个空格的时候？

Exercise 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 \ 1 . This makes tabs and backspaces visible in an unambiguous way. 用可见的方式打印出来

{% code title="1.5.4 visible.c" lineNumbers="true" %}

```c
#include <stdio.h>

int main()
{
    int c;
    while ((c = getchar()) != EOF)
    {
        if (c == '\t')
        {
            printf("\\t");
        }
        else if (c == '\b')
        {
            printf("\\b");
        }
        else if (c == '\\')
        {
            printf("\\\\");
        }
        else
        {
            putchar(c);
        }
    }
    return 0;
}
```

{% endcode %}

UNIX 中 wc 程序主体：

统计行数、单词数、字符数

state 变量记录当前程序是否处于一个单词之中，默认是“OUT”不在之中。

{% code title="1.5.5 unix-wc.c" lineNumbers="true" %}

```c
#include <stdio.h>

#define IN 1
#define OUT 0

int main() 
{
    //Declare variables to store the number of newlines, words, and characters
    int c, nl, nw, nc, state;
    //Initialize the state to OUT
    state = OUT;
    //Initialize the values of nl, nw, and nc to 0
    nl = nw = nc = 0;
    //Read each character from the input
    while ((c = getchar()) != EOF) 
    {
        //Increment the number of characters
        ++nc;
        //If the character is a newline, increment the number of newlines
        if (c == '\n') 
        {
            ++nl;
        }
        //If the character is a space, newline, or tab, set the state to OUT
        if (c == ' ' || c == '\n' || c == '\t') 
        {
            state = OUT;
        }
        //Otherwise, if the state is OUT, set the state to IN and increment the number of words
        else if (state == OUT) 
        {
            state = IN;
            ++nw;
        }
    }
    //Print the number of newlines, words, and characters
    printf("%d %d %d\n", nl, nw, nc);
    return 0;
}
```

{% endcode %}

```
if (c == ' ' || c == '\n' || c == '\t') 
```

逻辑或 OR，从左至右求值；这一点要和赋值操作区分开。

### 1.5.2 格式说明符 <a href="#pwfpu" id="pwfpu"></a>

format specifiers 是一个用于格式化输出的概念，它们指示在输出文本时如何处理和显示不同类型的数据。它们告诉格式化输出函数（如 printf）应该在输出中的哪个位置插入相应类型的数据，并如何表示这些数据。这是一个关于格式规则的指令。来标记变量应该在输出中的哪个位置出现，以及它们应该以何种方式呈现。格式指示符以 % 开头，后跟一个字符，表示数据类型，例如 %d 表示整数，%f 表示浮点数，%s 表示字符串，等等。

* %d：用于格式化和打印整数。示例：`printf("%d", 42);`
* %f：用于格式化和打印浮点数（小数）。示例：`printf("%f", 3.14159);`
* %c：用于格式化和打印单个字符。示例：`printf("%c", 'A');`
* %s：用于格式化和打印字符串（字符数组）。示例：`printf("%s", "Hello, World!");`
* %x：用于以十六进制（base 16）格式格式化和打印整数。示例：`printf("%x", 255);`（输出 "ff"）
* %o：用于以八进制（base 8）格式格式化和打印整数。示例：`printf("%o", 63);`（输出 "77"）
* %u：用于格式化和打印无符号整数。示例：`printf("%u", 123);`
* %p：用于格式化和打印指针（内存地址）。示例：`printf("%p", &variable);`
* %ld：用于格式化和打印长整数。示例：`printf("%ld", 1000000000L);`
* %lu：用于格式化和打印无符号长整数。示例：`printf("%lu", 4294967295UL);`
* %lld：用于格式化和打印长长整数。示例：`printf("%lld", 9000000000000000000LL);`
* %llu：用于格式化和打印无符号长长整数。示例：`printf("%llu", 18446744073709551615ULL);`

这些是C语言中用于不同数据类型的常用格式说明符。当你将这些格式说明符与诸如 `printf` 或 `scanf` 之类的函数一起使用时，它们指定了相应数据应该如何格式化或读取。\ <br>
