文章目录
- 前言
- 一、单字符I/O
- 二、缓冲区
- 为什么要引入缓冲区?
- 文件结尾
- 补充
- ==**注意几点**==
- 重定向和文件
- 重定向输入
- cmd是什么?
- 重定向输出
- 组合重定向
- 小试牛刀:文件输出
- 创建更友好的用户界面
- 使用缓冲输入
- 如果您发现文章有错误请与我留言,感谢
前言
`
一、单字符I/O
getchar()和putchar()每一次只能处理一个字符。
你可能认为这种方法实在太笨拙了,毕竟与我们的阅读方式相差甚远。但是,这种方法很适合计算机。而且,这是绝大多数文本(即,普通文字)处理程序所用的核心方法。为了帮助读者回忆这些函数的工作方式,请看程序 。该程序获取从键盘输入的字符,并把这些字符发送到屏幕上。程序使用while循环,当读到#字符时停止。
#include<stdio.h>
int main()
{char ch;while ((ch = getchar()) != '#')putchar(ch);return 0;
}
读者可能好奇,为何输入的字符能直接显示在屏幕上?如果用一个特殊字符(如,#)来结束输入,就无法在文本中使用这个字符,是否有更好的方法结束输入?要回答这些问题,首先要了解C程序如何处理键盘输入,尤其是缓冲和标准输入文件的概念。
二、缓冲区
每一个数都是重复的,像这样回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符。
对于大部分系统在用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并储存在一个被称为缓冲区(buffer )的临时存储区,按下Enter键后,程序才可使用用户输入的字符。
为什么要引入缓冲区?
首先,把若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果用户打错字符,可以直接通过键盘修正错误。当最后按下Enter键时,传输的是正确的输入。
虽然缓冲输入好处很多,但是某些交互式程序也需要无缓冲输入。
例如,在游戏中,你希望按下一个键就执行相应的指令,因此,缓冲输入和无缓冲输入都有用武之地。
缓冲分为两类:
完全缓冲I/O和行缓冲I/O。
完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。
缓冲区的大小取决于系统,常见的大小是512字节和4096字节。
行缓冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
程序使用的缓冲是行缓冲。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
本章着重理解C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示设备视为每个C程序自动打开的文件。stdin流表示键盘输入,stdout流表示屏幕输出。 getchar()、putchar()、printf()和scanf()函数都是标准I/O包的成员,处理这两个流。
以上讨论的内容说明,可以用处理文件的方式来处理键盘输入。例如,程序读文件时要能检测文件的末尾才知道应在何处停止,因此,C的输入函数内置了文件结尾检测器。既然可以把键盘输入视为文件,那么也应该能使用文件结尾检测器结束键盘输入。下面我们从文件开始,学习如何结束文件。
文件结尾
计算机操作系统要以某种方式判断文件的开始和结束。检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。 检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。
这个特殊的字符标记一般是:Ctrl+Z。
在下面的程序中,要结束输入,在按下 Ctrl+Z后,程序会就结束。
补充
getchar()函数
int getchar (void)
getchar()函数的返回类型为 int 整型 参数为 void
此时大家肯定会想,getchar()函数不是用来输入单个字符的吗,为什么返回类型为 int 整型呢 ?
1、getchar其实返回的是字符的ASCII码值(整数)。
2、getchargetchar在读取结束或者失败的时候,会返回EOF。
无论操作系统实际使用何种方法检测文件结尾,
在C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,
即EOF(end of file 的缩写)。
scanf()函数检测到文件结尾时也返回EOF。通常, EOF定义在stdio.h文件中:
#define EOF (-1)
为什么是-1?
因为getchar()函数的返回值通常都介于0~127,这些值对应标准字符集。
但是,如果系统能识别扩展字符集,该函数的返回值可能在0~255之间。
所以无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾。
某些系统也许把EOF定义为-1以外的值,但是定义的值一定与输入字符所产生的返回值不同。
另外,char型本身就是无符号型
#include <stdio.h>
#include <string.h>
int main()
{int ch = 0; //因为 getchar() 返回类型为 intwhile ((ch = getchar()) != EOF) // 连续输入单个字符{printf("%c",ch); // 输出一个字符//putchar(ch); // 此时 printf("%c",ch) 与 putchar(ch) 输出结果一样}return 0;
}
此时 printf(“%c”,ch) 与 putchar(ch) 输出结果一样
注意几点
- 不用定义EOF,因为stdio.h中已经定义过了
- 不用担心EOF的实际值,因为在stdio.h中用#define 预处理指令定义,可以直接使用
- 变量ch的值从char变成int,因为char类型的变量只能表示0~255的无符号整形,但是EOF的值是-1,还好,getchar()的实际返回值是int.所以他可以读取EOF字符
- 使用该程序进行键盘输入,要设法输入EOF字符,而不是字符EOF,也不能输入-1(输入-1会传输两个值,一个连字符和一个字符1。)正确的方法是,找到当前系统的需求,许多系统会把一行最开始处的Ctrl + Z作为文件结尾的信号。
我们暂停一会。既然程序能把用户输入的内容拷贝到屏幕上,那么考虑一下该程序还可以做什么。(上面的程序文件名是pp.cpp)
假设以某种方式把一个文件传送给它,然后它把文件中的内容打印在屏幕上,当到达文件结尾发现EOF信号时停止。
或者,假设以某种方式把程序的输出定向到一个文件,然后通过键盘输入数据,用pp.cpp 来储存在文件中输入的内容。
假设同时使用这两种方法:把输入从一个文件定向到pp.cpp中,并把输出发送至另一个文件,然后便可以使用pp.cpp来拷贝文件。
重定向和文件
设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出,换言之,把stdin流重新赋给文件(这时,是用键盘输入,输出给文件而不是屏幕)。继续使用getchar()函数从输入流中获取数据,但它并不关心从流的什么位置获取数据。虽然这种重定向的方法在某些方面有些限制,但是用起来比较简单,而且能让读者熟悉普通的文件处理技术。
重定向输入
我们找到pp.exe的位置
输入cmd
cmd是什么?
CMD是Windows操作系统中的命令提示符(Command Prompt)程序,它是一种命令行工具,可以让用户通过键入命令来与计算机进行交互。
CMD是Windows中一个基本的系统组件,它提供了一个简单的方式来执行诸如文件管理、网络管理、系统配置等各种任务。通过命令提示符,用户可以通过简单的命令来执行这些任务,而无需打开图形用户界面(GUI)。
————————————————
原文链接:https://blog.csdn.net/weixin_43783942/article/details/129470999
会出现这个界面
我们打开pp.exe,程序是可以运行的,输入Ctrl+Z终止程序。
创建一个pp.txt(文本文件),内容在右侧
下来把pp.txt的内容导入pp.exe程序
pp.exe<pp.txt
如果跨文件夹访问要加上文件位置
重定向输出
下来用键盘输入,输出给文件而不是屏幕
确实是输出到文件里了
组合重定向
把输入从一个文件pp2.txt定向到 pp.exe中,并把输出发送至另一个文件pp3.txt
没有任何问题
注意:在一条命令中,输入文件名和输出文件名不能相同。 echo_eof < mywords > mywords…<–错误
原因是> mywords在输入之前已导致原mywords的长度被截断为0.
在UNIX、Linux或Windows/DOS系统中使用两个重定向运算符(<和>)时,要遵循以下原则。
-
重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
-
使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个文件。
小试牛刀:文件输出
不使用重定向,因为重定向的可移植性差,用C语言程序直接打开文件。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{char fname[100];FILE* fp;int ch;printf("Enter the name of the file :");scanf("%s", fname);fp = fopen(fname, "r");if (fp == NULL){printf("Failed open the file,bye");exit(1);}while ((ch = getc(fp)) != EOF)putchar(ch);fclose(fp);return 0;
}
我想看看我写的代码,文件名是源.cpp
非常成功。
创建更友好的用户界面
大部分人偶尔会写一些中看不中用的程序。还好,C提供了大量工具让输入更顺畅,处理过程更顺利。不过,学习这些工具会导致新的问题。本节的目标是,指导读者解决这些问题并创建更友好的用户界面,让交互数据输入更方便,减少错误输入的影响。
使用缓冲输入
缓冲输入用起来比较方便,因为在把输入发送给程序之前,用户可以编辑输入。
但是,在使用输入的字符时,它也会给程序员带来麻烦。前面示例中看到的问题是,缓冲输入要求用户按下Enter键发送输入。这一动作也传送了换行符,程序必须妥善处理这个麻烦的换行符。
我们以一个猜谜程序为例。用户选择一个数字,程序猜用户选中的数字是多少。
我们关注的重点在输入和输出。缓冲区
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int guess = 1;printf("Pick an integer from 1 to 100. I will try to guess it.\n");printf("Respond with a y if my guess is right and with an n if it's wrong.\n");printf("Uh...is your number %d?\n", guess);while(getchar()!='y')//scanf('%c',ch)=getchar(){printf("Well, then, is it %d?\n",++guess);}printf("I knew I could do it!\n");return 0;
}
可以看出,每次输入 n 时,程序打印了两条消息。这是因为输入 n 后,我们还按下了 Enter 进行换行,然后还读取了一个换行符,下一次循环中,getchar() 读取了这个换行符并打印了第二条消息。当我们输入多个字符时也会出现多行消息。
改进
使循环读取并丢弃输入的数据
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int guess = 1;printf("Pick an integer from 1 to 100. I will try to guess it.\n");printf("Respond with a y if my guess is right and with an n if it's wrong.\n");printf("Uh...is your number %d?\n", guess);while (getchar() != 'y')//no sir输入1.n{printf("Well, then, is it %d?\n", ++guess);while (getchar() != '\n')//2.o不等于\n继续循环while (getchar() != '\n')直到\ncontinue;}printf("I knew I could do it!\n");return 0;
}
continue的理解
1.continue本身的性质一直读到末尾
2.书上的意思是他只是一个占位符,
这的确是解决了换行符的问题。但是,该程序还是会把f被视为n。我们用if语句筛选其他响应.首先,添加一个char类型的变量储存响应:char r ;
修改后的循环如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int guess = 1;char r;printf("Pick an integer from 1 to 100. I will try to guess it.\n");printf("Respond with a y if my guess is right and with an n if it's wrong.\n");printf("Uh...is your number %d?\n", guess);while ((r = getchar()) != 'y'){if ( r == 'n')printf("Well, then, is it %d?\n", ++guess);elseprintf("I understand only n or y\n");while (getchar() != '\n');}printf("I knew I could do it!\n");return 0;
}