《C Primer Plus》第八章 — 字符输入输出和输入验证(缓冲区,文件尾,重定向,菜单设计与输入验证,复习题和编程练习带答案)_"while (getchar() != 'y') /* 获取响应,与 y 做对比 */ print-程序员宅基地

技术标签: c语言  C Primer Plus  

加粗与下面的点是用来标记我的知识盲区,无特殊含义。

  • 重定向和文件及之前的点由加粗标记
  • 之后的知识应反复学习

字符输入/输出和输入验证

本章内容

本章介绍以下内容:

  • 更详细地介绍输入、输出以及缓冲输入和无缓冲输入的区别;

  • 如何通过键盘模拟文件结尾条件;

  • 如何使用重定向把程序和文件相连接;

  • 创建更友好的用户界面。

  • 最初,输入/输出函数不是C定义的一部分,C把开发这些函数的任务留给编译器的实现者来完成。

  • 在实际应用中,UNIX系统中的C实现为这些函数提供了一个模型。ANSI C库吸取成功的经验,把大量的UNIX I/O函数囊括其中,包括一些我们曾经用过的。

  • 由于必须保证这些标准函数在不同的计算机环境中能正常工作,所以它们很少使用某些特殊系统才有的特性。因此,许多C供应商会利用硬件的特性,额外提供一些I/O函数。

  • 自从ANSI C标准发布以后,C就把stdio.h头文件与使用getchar()和putchar()相关联,这就是为什么程序中要包含这个头文件的原因(其实,getchar()和putchar()都不是真正的函数,它们被定义为供预处理器使用的宏,我们在第16章中再详细讨论)。

缓冲区

如果在老式系统运行程序清单8.1,你输入文本时可能显示如下:

HHeelllloo, tthheerree… II wwoouulldd[enter]

lliikkee aa #

这样回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符。

/* echo.c -- 重复输入 */
#include <stdio.h>
int main(void)
{
    
    char ch;
    while ((ch = getchar()) != '#')
        putchar(ch);
    return 0;
}

Hello, there. I would[enter]

Hello, there. I wouldlike a #3 bag of potatoes.[enter]

like a

  • 大部分系统在用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并存储在一个被称为缓冲区(buffer)的临时存储区,按下Enter键后,程序才可使用用户输入的字符。
  • 缓冲分为两类:完全缓冲I/O和行缓冲I/O。
  • 完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是512字节和4096字节。
  • 行缓冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
  • ANSI C决定把缓冲输入作为标准的原因是:一些计算机不允许无缓冲输入。如果你的计算机允许无缓冲输入,那么你所用的C编译器很可能会提供一个无缓冲输入的选项。
  • 例如,许多IBM PC兼容机的编译器都为支持无缓冲输入提供一系列特殊的函数,其原型都在conio.h头文件中。这些函数包括用于回显无缓冲输入的getche()函数和用于无回显无缓冲输入的getch()函数(回显输入意味着用户输入的字符直接显示在屏幕上,无回显输入意味着击键后对应的字符不显示)。
  • UNIX系统使用另一种不同的方式控制缓冲。在UNIX系统中,可以使用ioctl()函数(该函数属于UNIX库,但是不属于C标准)指定待输入的类型,然后用getchar()执行相应的操作。在ANSI C中,用setbuf()和setvbuf()函数(详见第13章)控制缓冲,但是受限于一些系统的内部设置,这些函数可能不起作用。
  • 总之,ANSI没有提供调用无缓冲输入的标准方式,这意味着是否能进行无缓冲输入取决于计算机系统。在这里要对使用无缓冲输入的读者说声抱歉,本书假设所有的输入都是缓冲输入。

结束键盘输入

文件、流和键盘输入

  • 文件(file)是存储器中存储信息的区域。通常,文件都保存在某种永久存储器中(例如,硬盘、U盘或DVD等)。
  • 这些直接调用操作系统的函数被称为底层I/O
  • 较高层面上,C还可以通过标准I/O包(standard I/O package)来处理文件。这涉及创建用于处理文件的标准模型和一套标准I/O函数。在这一层面上,具体的C实现负责处理不同系统的差异,以便用户使用统一的界面。
  • 有些系统把文件的内容存储在一处,而文件相关的信息存储在另一处;有些系统在文件中创建一份文件描述。在处理文件方面,有些系统使用单个换行符标记行末尾,而其他系统可能使用回车符和换行符的组合来表示行末尾。有些系统用最小字节来衡量文件的大小,有些系统则以字节块的大小来衡量。
  • 如果使用标准I/O包,就不用考虑这些差异。因此,可以用if (ch == ‘\n’)检查换行符。即使系统实际用的是回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
  • 从概念上看,C程序处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的输入,由属性更统一的流来表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
  • 第13章将更详细地讨论文件。本章着重理解C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示设备视为每个C程序自动打开的文件。stdin流表示键盘输入,stdout流表示屏幕输出。getchar()、putchar()、printf()和scanf()函数都是标准I/O包的成员,处理这两个流。
    以上讨论的内容说明,可以用处理文件的方式来处理键盘输入。
  • 例如,程序读文件时要能检测文件的末尾才知道应在何处停止。因此,C的输入函数内置了文件结尾检测器。既然可以把键盘输入视为文件,那么也应该能使用文件结尾检测器结束键盘输入。

文件结尾

  • 检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。

  • CP/M、IBM-DOS和MS-DOS的文本文件曾经用过这种方法。如今,这些操作系统可以使用内嵌的Ctrl+Z字符来标记文件结尾。这曾经是操作系统使用的唯一标记,不过现在有一些其他的选择,例如记录文件的大小。所以现代的文本文件不一定有嵌入的Ctrl+Z,但是如果有,该操作系统会将其视为一个文件结尾标记。
    在这里插入图片描述

  • 操作系统使用的另一种方法是存储文件大小的信息。如果文件有3000字节,程序在读到3000字节时便达到文件的末尾。 MS-DOS及其相关系统使用这种方法处理二进制文件,因为用这种方法可以在文件中存储所有的字符,包括Ctrl+Z。新版的DOS也使用这种方法处理文本文件。UNIX使用这种方法处理所有的文件。

  • 无论操作系统实际使用何种方法检测文件结尾,在C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,即EOF(end of file的缩写)。scanf()函数检测到文件结尾时也返回EOF。

  • #define EOF (-1)

  • 为什么是-1?因为getchar()函数的返回值通常都介于0~127,这些值对应标准字符集。但是,如果系统能识别扩展字符集,该函数的返回值可能在0~255。无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾。

  • 这里关键要理解EOF是一个值,标志着检测到文件结尾,并不是在文件中找得到的符号。

    while ((ch = getchar()) != EOF)

  • getchar()的返回值和EOF作比较。如果两值不同,就说明没有到达文件结尾。

  • 绝大部分系统(不是全部)都有办法通过键盘模拟文件结尾条件。

/* echo_eof.c -- 重复输入,直到文件结尾 */
#include <stdio.h>
int main(void)
{
    
  int ch;
  while ((ch = getchar()) != EOF)
          putchar(ch);
  return 0;
}

注意:

  • ·不用定义EOF,因为stdio.h中已经定义过了。
  • ·不用担心EOF的实际值,因为EOF在stdio.h中用#define预处理指令定义,可直接使用,不必再编写代码假定EOF为某值。
  • ·变量ch的类型从char变为int,因为char类型的变量只能表示0~255的无符号整数,但是EOF的值是-1。还好,getchar()函数实际返回值的类型是int,所以它可以读取EOF字符。如果实现使用有符号的char类型,也可以把ch声明为char类型,但最好还是用更通用的形式。
  • ·由于getchar()函数的返回类型是int,如果把getchar()的返回值赋给char类型的变量,一些编译器会警告可能丢失数据。
  • ·ch是整数不会影响putchar(),该函数仍然会打印等价的字符。
  • ·使用该程序进行键盘输入,要设法输入EOF字符。不能只输入字符EOF,也不能只输入-1(输入-1会传送两个字符:一个连字符和一个数字1)。正确的方法是,必须找出当前系统的要求。例如,在大多数UNIX和Linux系统中,在一行开始处按下Ctrl+D会传输文件结尾信号。许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号,一些系统把任意位置的Ctrl+Z解释成文件结尾信号。
  • 注意 模拟EOF和图形界面
    模拟EOF的概念是在使用文本界面的命令行环境中产生的。在这种环境中,用户通过击键与程序交互,由操作系统生成EOF信号。但是在一些实际应用中,却不能很好地转换成图形界面(如Windows和Macintosh),这些用户界面包含更复杂的鼠标移动和按钮点击。程序要模拟EOF的行为依赖于编译器和项目类型。例如,Ctrl+Z可以结束输入或整个程序,这取决于特定的设置。

重定向和文件

  • 在默认情况下,C程序使用标准I/O包查找标准输入作为输入源。这就是前面介绍过的stdin流,它是把数据读入计算机的常用方式。它可以是一个过时的设备,如磁带、穿孔卡或电传打印机,或者(假设)是键盘,甚至是一些先进技术,如语音输入。然而,现代计算机非常灵活,可以让它到别处查找输入。尤其是,可以让一个程序从文件中查找输入,而不是从键盘。
  • 程序可以通过两种方式使用文件。
  • 第1种方法是,显式使用特定的函数打开文件、关闭文件、读取文件、写入文件,诸如此类。我们在第13章中再详细介绍这种方法。
  • 第2种方法是,设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出。换言之,把stdin流重新赋给文件。继续使用getchar()函数从输入流中获取数据,但它并不关心从流的什么位置获取数据。 虽然这种重定向的方法在某些方面有些限制,但是用起来比较简单,而且能让读者熟悉普通的文件处理技术。

UNIX、Linux和DOS重定向

  • UNIX(运行命令行模式时)、Linux(ditto)和Window命令行提示(模仿旧式DOS命令行环境)都能重定向输入、输出。重定向输入让程序使用文件而不是键盘来输入,重定向输出让程序输出至文件而不是屏幕。

    文本文件(text file)是内含文本的文件,其中存储的数据是我们可识别的字符。文件的内容可以是一篇散文或者C程序。内含机器语言指令的文件(如存储可执行程序的文件)不是文本文件。

  • 由于该程序的操作对象是字符,所以要使用文本文件。只需用下面的命令代替上面的命令即可:

    ./echo_eof < words

  • <符号是UNIX和DOS/Windows的重定向运算符。该运算符使words文件与stdin流相关联,把文件中的内容导入echo_eof程序。echo_eof程序本身并不知道(或不关心)输入的内容是来自文件还是键盘,它只知道这是需要导入的字符流,所以它读取这些内容并把字符逐个打印在屏幕上,直至读到文件结尾。因为C把文件和I/O设备放在一个层面,所以文件就是现在的I/O设备。

  • 现在假设要用echo_eof把键盘输入的内容发送到名为mywords的文件中。然后,输入以下命令并开始输入:

    ./echo_eof>mywords

  • >符号是第2个重定向运算符。它创建了一个名为mywords的新文件,然后把echo_eof的输出(即,你输入字符的副本)重定向至该文件中。重定向把stdout从显示设备(即,显示器)赋给mywords文件。如果已经有一个名为mywords的文件,通常会擦除该文件的内容,然后替换新的内容(但是,许多操作系统有保护现有文件的选项,使其成为只读文件)。所有出现在屏幕的字母都是你刚才输入的,其副本存储在文件中。在下一行的开始处按下Ctrl+D(UNIX)或Ctrl+Z(DOS)即可结束该程序。

  • 现在,假设你希望制作一份mywords文件的副本,并命名为savewords。只需输入以下命令即可:

    ./echo_eof < mywords > savewords

  • 下面的命令也起作用,因为命令与重定向运算符的顺序无关:

    ./echo_eof > savewords < mywords

注意:

  • ·重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
  • ·使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个文件。
  • ·通常,文件名和运算符之间的空格不是必须的,除非是偶尔在UNIX shell、Linux shell或Windows命令行提示模式中使用的有特殊含义的字符。例如,我们用过的./echo_eof<words。
  • UNIX、Linux或Windows/DOS还有>>运算符,该运算符可以把数据添加到现有文件的末尾,而|运算符能把一个文件的输出连接到另一个文件的输入。

创建更友好的用户界面

/* guess.c -- 一个拖沓且错误的猜数字程序 */
#include <stdio.h>
int main(void)
{
    
    int guess = 1;
    printf("Pick an integer from 1 to 100. I will try to guess ");
    printf("it.\nRespond with a y if my guess is right and with");
    printf("\nan n if it is wrong.\n");
    printf("Uh...is your number %d?\n", guess);
    while (getchar() != 'y') /* 获取响应,与 y 做对比 */
        printf("Well, then, is it %d?\n", ++guess);
    printf("I knew I could do it!\n");
    return 0;
}

Pick an integer from 1 to 100. I will try to guess it.

Respond with a y if my guess is right and with

an n if it is wrong.

Uh…is your number 1?n

Well, then, is it 2?

Well, then, is it 3?n

Well, then, is it 4?

Well, then, is it 5?y

I knew I could do it!

  • 撇开这个程序糟糕的算法不谈,我们先选择一个数字。注意,每次输入n时,程序打印了两条消息。这是由于程序读取n作为用户否定了数字1,然后还读取了一个换行符作为用户否定了数字2。
  • 一种解决方案是,使用while循环丢弃输入行最后剩余的内容,包括换行符。这种方法的优点是,能把no和no way这样的响应视为简单的n。
while (getchar() != 'y')   /* 获取响应,与 y 做对比*/
{
    
   printf("Well, then, is it %d?\n", ++guess);
   while (getchar() != '\n')
     continue;      /* 跳过剩余的输入行 */
}

Pick an integer from 1 to 100. I will try to guess it.

Respond with a y if my guess is right and with

an n if it is wrong.

Uh…is your number 1?n

Well, then, is it 2?no

Well, then, is it 3?no sir

Well, then, is it 4?forget it

Well, then, is it 5?y

I knew I could do it!

  • 这的确是解决了换行符的问题。但是,该程序还是会把f视为n。

  • 我们用if语句筛选其他响应。首先,添加一个char类型的变量存储响应:

    char response;

  • 修改后的循环如下:

while ((response = getchar()) != 'y') /* 获取响应 */
{
    
   if (response == 'n')
     printf("Well, then, is it %d?\n", ++guess);
   else
     printf("Sorry, I understand only y or n.\n");
   while (getchar() != '\n')
     continue; /* 跳过剩余的输入行 */
}

混合数值和字符输入

  • 该程序读入一个字符和两个数字,然后根据输入的两个数字指定的行数和列数打印该字符。
/* showchar1.c -- 有较大 I/O 问题的程序 */
#include <stdio.h>
void display(char cr, int lines, int width);
int main(void)
{
    
   int ch;        /* 待打印字符  */
   int rows, cols;    /* 行数和列数 */
   printf("Enter a character and two integers:\n");
   while ((ch = getchar()) != '\n')
   {
    
     scanf("%d %d", &rows, &cols);
     display(ch, rows, cols);
     printf("Enter another character and two integers;\n");
     printf("Enter a newline to quit.\n");
   }
   printf("Bye.\n");
   return 0;
}
void display(char cr, int lines, int width)
{
    
   int row, col;
   for (row = 1; row <= lines; row++)
   {
    
     for (col = 1; col <= width; col++)
        putchar(cr);
     putchar('\n');  /* 结束一行并开始新的一行 */
   }
}

Enter a character and two integers:c 2 3

ccc

ccc

Enter another character and two integers;

Enter a newline to quit.

Bye.

  • 该程序开始时运行良好。你输入c 2 3,程序打印c字符2行3列。然后,程序提示输入第2组数据,还没等你输入数据程序就退出了!

  • 换行符在捣乱,这次是输入行中紧跟在3后面的换行符。scanf()函数把这个换行符留在输入队列中。和scanf()不同,getchar()不会跳过换行符,所以在进入下一轮迭代时,你还没来得及输入字符,它就读取了换行符,然后将其赋给ch。而ch是换行符正式终止循环的条件。

/* showchar2.c -- 按指定的行列打印字符 */
#include <stdio.h>
void display(char cr, int lines, int width);
int main(void)
{
    
   int ch;        /* 待打印字符*/
   int rows, cols;    /* 行数和列数 */
   printf("Enter a character and two integers:\n");
   while ((ch = getchar()) != '\n')
   {
    
     if (scanf("%d %d", &rows, &cols) != 2)
        break;
     display(ch, rows, cols);
     while (getchar() != '\n')
        continue;
     printf("Enter another character and two integers;\n");
     printf("Enter a newline to quit.\n");
   }
   printf("Bye.\n");
   return 0;
}
void display(char cr, int lines, int width)
{
    
   int row, col;
   for (row = 1; row <= lines; row++)
   {
    
     for (col = 1; col <= width; col++)
        putchar(cr);
     putchar('\n');    /* 结束一行并开始新的一行 */
   }
}
  • while循环实现了丢弃scanf()输入后面所有字符(包括换行符)的功能,为循环的下一轮读取做好了准备。

  • 在if语句中使用一个break语句,可以在scanf()的返回值不等于2时终止程序,即如果一个或两个输入值不是整数或者遇到文件结尾就终止程序。

输入验证

  • 例如,假设你编写了一个处理非负数整数的循环,但是用户很可能输入一个负数。你可以使用关系表达式来排除这种情况:

    long n;

    scanf("%ld", &n); // 获取第1个值

    while (n >= 0) // 检测不在范围内的值

    {

    // 处理n

    scanf("%ld", &n); // 获取下一个值

    }

  • 另一类潜在的陷阱是,用户可能输入错误类型的值,如字符q。排除这种情况的一种方法是,检查scanf()的返回值。回忆一下,scanf()返回成功读取项的个数。因此,下面的表达式当且仅当用户输入一个整数时才为真:

    scanf("%ld", &n) == 1

  • 结合上面的while循环,可改进为:

    long n;

    while (scanf("%ld", &n) == 1 && n >= 0)

    {

    // 处理n

    }

long get_long(void)
{
    
   long input;
   char ch;
   while (scanf("%ld", &input) != 1)
   {
    
     while ((ch = getchar()) != '\n')
        putchar(ch); // 处理错误的输入
     printf(" is not an integer.\nPlease enter an ");
     printf("integer value, such as 25, -178, or 3: ");
   }
   return input;
}
  • 该函数要把一个long类型的值读入变量input中。如果读取失败,函数则进入外层while循环体。然后内层循环逐字符地读取错误的输入。注意,该函数丢弃该输入行的所有剩余内容。还有一个方法是,只丢弃下一个字符或单词,然后该函数提示用户再次输入。外层循环重复运行,直到用户成功输入整数,此时scanf()的返回值为1。
// checking.c -- 输入验证
#include <stdio.h>
#include <stdbool.h>
// 验证输入是一个整数
long get_long(void);
// 验证范围的上下限是否有效
bool bad_limits(long begin, long end,
          long low, long high);
// 计算a~b的整数平方和
double sum_squares(long a, long b);
int main(void)
{
    
   const long MIN = -10000000L;  // 范围的下限
   const long MAX = +10000000L;  // 范围的上限
   long start;           // 用户指定的范围最小值
   long stop;           // 用户指定的范围最大值
   double answer;
   printf("This program computes the sum of the squares of "
       "integers in a range.\nThe lower bound should not "
       "be less than -10000000 and\nthe upper bound "
       "should not be more than +10000000.\nEnter the "
       "limits (enter 0 for both limits to quit):\n"
       "lower limit: ");
   start = get_long();
   printf("upper limit: ");
   stop = get_long();
   while (start != 0 || stop != 0)
   {
    
     if (bad_limits(start, stop, MIN, MAX))
        printf("Please try again.\n");
     else
     {
    
        answer = sum_squares(start, stop);
        printf("The sum of the squares of the integers ");
        printf("from %ld to %ld is %g\n",
            start, stop, answer);
     }
     printf("Enter the limits (enter 0 for both "
          "limits to quit):\n");
     printf("lower limit: ");
     start = get_long();
     printf("upper limit: ");
     stop = get_long();
   }
   printf("Done.\n");
   return 0;
}
long get_long(void)
{
    
   long input;
   char ch;
   while (scanf("%ld", &input) != 1)
   {
    
     while ((ch = getchar()) != '\n')
        putchar(ch);      // 处理错误输入
     printf(" is not an integer.\nPlease enter an ");
     printf("integer value, such as 25, -178, or 3: ");
   }
   return input;
}
double sum_squares(long a, long b)
{
    
   double total = 0;
   long i;
   for (i = a; i <= b; i++)
     total += (double) i * (double) i;
   return total;
}
bool bad_limits(long begin, long end,
   long low, long high)
{
    
   bool not_good = false;
   if (begin > end)
   {
    
     printf("%ld isn't smaller than %ld.\n", begin, end);
     not_good = true;
   }
   if (begin < low || end < low)
   {
    
     printf("Values must be %ld or greater.\n", low);
     not_good = true;
   }
   if (begin > high || end > high)
   {
    
     printf("Values must be %ld or less.\n", high);
     not_good = true;
   }
   return not_good;
}

This program computes the sum of the squares of integers in a range.

The lower bound should not be less than -10000000 and

the upper bound should not be more than +10000000.

Enter the limits (enter 0 for both limits to quit):

lower limit: low

low is not an integer.

Please enter an integer value, such as 25, -178, or 3: 3

upper limit: a big number

a big number is not an integer.

Please enter an integer value, such as 25, -178, or 3: 12

The sum of the squares of the integers from 3 to 12 is 645

Enter the limits (enter 0 for both limits to quit):

lower limit: 80

upper limit: 10

80 isn’t smaller than 10.

Please try again.

Enter the limits (enter 0 for both limits to quit):

lower limit: 0

upper limit: 0

Done.

  • 程序遵循模块化的编程思想,使用独立函数(模块)来验证输入和管理显示。程序越大,使用模块化编程就越重要。

  • main()函数管理程序流,为其他函数委派任务。它使用get_long()获取值、while循环处理值、bad_limits()函数检查值是否有效、sum_squres()函数处理实际的计算:

菜单浏览

  • 许多计算机程序都把菜单作为用户界面的一部分。菜单给用户提供方便的同时,却给程序员带来了一些麻烦。我们看看其中涉及了哪些问题。

  • 菜单给用户提供了一份响应程序的选项。假设有下面一个例子:

    Enter the letter of your choice:

    a. advice b. bell

    c. count q. quit

  • 第1个目标是:当用户遵循指令时程序顺利运行;

  • 第2个目标是:当用户没有遵循指令时,程序也能顺利运行。显而易见,要实现第2个目标难度较大,因为很难预料用户在使用程序时的所有错误情况。

  • 现在的应用程序通常使用图形界面,可以点击按钮、查看对话框、触摸图标,而不是我们示例中的命令行模式。但是,两者的处理过程大致相同:给用户提供选项、检查并执行用户的响应、保护程序不受误操作的影响。除了界面不同,它们底层的程序结构也几乎相同。但是,使用图形界面更容易通过限制选项控制输入。

  • 我们来更具体地分析一个菜单程序需要执行哪些任务。它要获取用户的响应,根据响应选择要执行的动作。另外,程序应该提供返回菜单的选项。C的switch语句是根据选项决定行为的好工具,用户的每个选择都可以对应一个特定的case标签。使用while语句可以实现重复访问菜单的功能。因此,我们写出以下伪代码:

    获取选项

    当选项不是’q’时

    转至相应的选项并执行

    获取下一个选项

  • 当你决定实现这个程序时,就要开始考虑如何让程序顺利运行(顺利运行指的是,处理正确输入和错误输入时都能顺利运行)。例如,你能做的是让“获取选项”部分的代码筛选掉不合适的响应,只把正确的响应传入switch。这表明需要为输入过程提供一个只返回正确响应的函数。

  • 定义get_choice()函数只能返回’a’、‘b’、‘c’和’q’。

  • 下面的伪代码是设计这个函数的一种方案:

    显示选项
    获取用户的响应
    当响应不合适时
    提示用户再次输入
    获取用户的响应

    char get_choice(void)
    {
          
       int ch;
       printf("Enter the letter of your choice:\n");
       printf("a. advice      b. bell\n");
       printf("c. count       q. quit\n");
       ch = getchar();
       while ((ch < 'a' || ch > 'c') && ch != 'q')
       {
          
         printf("Please respond with a, b, c, or q.\n");
         ch = getchar();
       }
       return ch;
    }
    

    缓冲输入依旧带来些麻烦,程序把用户每次按下Return键产生的换行符视为错误响应。为了让程序的界面更流畅,该函数应该跳过这些换行符。

  • 这类问题有多种解决方案。一种是用名为get_first()的新函数替换getchar()函数,读取一行的第1个字符并丢弃剩余的字符。这种方法的优点是,把类似act这样的输入视为简单的a,而不是继续把act中的c作为选项c的一个有效的响应。我们重写输入函数如下:

    char get_choice(void)
    {
          
       int ch;
       printf("Enter the letter of your choice:\n");
       printf("a. advice       b. bell\n");
       printf("c. count       q. quit\n");
       ch = get_first();
       while ((ch < 'a' || ch > 'c') && ch != 'q')
       {
          
         printf("Please respond with a, b, c, or q.\n");
         ch = get_first();
       }
       return ch;
    }
    char get_first(void)
    {
          
       int ch;
       ch = getchar();  /* 读取下一个字符 */
       while (getchar() != '\n')
         continue;  /* 跳过该行剩下的内容 */
       return ch;
    }
    
    void count(void)
    {
          
       int n, i;
       printf("Count how far? Enter an integer:\n");
       scanf("%d", &n);
       for (i = 1; i <= n; i++)
         printf("%d\n", i);
    }
    
  • 如果输入3作为响应,scanf()会读取3并把换行符留在输入队列中。下次调用get_choice()将导致get_first()返回这个换行符,从而导致我们不希望出现的行为。

  • 重写get_first(),使其返回下一个非空白字符而不仅仅是下一个字符,即可修复这个问题。

  • 另一种方法是,在count()函数中清理换行符,如下所示:

    void count(void)
    {
          
       int n, i;
       printf("Count how far? Enter an integer:\n");
       n = get_int();
       for (i = 1; i <= n; i++)
         printf("%d\n", i);
       while (getchar() != '\n')
         continue;
    }
    
    /* menuette.c -- 菜单程序 */
    #include <stdio.h>
    char get_choice(void);
    char get_first(void);
    int get_int(void);
    void count(void);
    int main(void)
    {
          
       int choice;
       while ((choice = get_choice()) != 'q')
       {
          
         switch (choice)
         {
          
            case 'a': printf("Buy low, sell high.\n");
                 break;
            case 'b': putchar('\a');  /* ANSI */
                 break;
            case 'c': count();
                 break;
            default:  printf("Program error!\n");
                 break;
         }
       }
       printf("Bye.\n");
       return 0;
    }
    void count(void)
    {
          
       int n, i;
       printf("Count how far? Enter an integer:\n");
       n = get_int();
       for (i = 1; i <= n; i++)
         printf("%d\n", i);
       while (getchar() != '\n')
         continue;
    }
    char get_choice(void)
    {
          
       int ch;
       printf("Enter the letter of your choice:\n");
       printf("a. advice      b. bell\n");
       printf("c. count      q. quit\n");
       ch = get_first();
       while ((ch < 'a' || ch > 'c') && ch != 'q')
       {
          
         printf("Please respond with a, b, c, or q.\n");
         ch = get_first();
       }
       return ch;
    }
    char get_first(void)
    {
          
       int ch;
       ch = getchar();
       while (getchar() != '\n')
         continue;
       return ch;
    }
    int get_int(void)
    {
          
       int input;
       char ch;
       while (scanf("%d", &input) != 1)
       {
          
         while ((ch = getchar()) != '\n')
            putchar(ch); // 处理错误输出
         printf(" is not an integer.\nPlease enter an ");
         printf("integer value, such as 25, -178, or 3: ");
       }
       return input;
    }
    

复习题

  1. putchar(getchar())是一个有效表达式,它实现什么功能?getchar(putchar())是否也是有效表达式?
  2. 下面的语句分别完成什么任务?
    a.putchar(‘H’);
    b.putchar(’\007’);
    c.putchar(’\n’);
    d.putchar(’\b’);
  3. 假设有一个名为count的可执行程序,用于统计输入的字符数。设计一个使用count程序统计essay文件中字符数的命令行,并把统计结果保存在essayct文件中。
  4. 给定复习题3中的程序和文件,下面哪一条是有效的命令?
    a.essayct <essay
    b.count essay
    c.essay >count
  5. EOF是什么?
  6. 对于给定的输入(ch是int类型,而且是缓冲输入),下面各程序段的输出分别是什么?
    a.输入如下:
    If you quit, I will.[enter]
    程序段如下:
    while ((ch = getchar()) != ‘i’)
    putchar(ch);
    b.输入如下:
    Harhar[enter]
    程序段如下:
    while ((ch = getchar()) != ‘\n’)
    {
    putchar(ch++);
    putchar(++ch);
    }
  7. C如何处理不同计算机系统中的不同文件和换行约定?
  8. 在使用缓冲输入的系统中,把数值和字符混合输入会遇到什么潜在的问题?

  1. 表达式putchar(getchar())使程序读取下一个输入字符并打印出来。getchar()的返回值是putchar()的参数。但getchar(putchar())是无效的表达式,因为getchar()不需要参数,而putchar()需要一个参数。
  2. a.显示字符H。
    b.如果系统使用ASCII,则发出一声警报。
    c.把光标移至下一行的开始。
    d.退后一格。
  3. count <essay >essayct或者count >essayct <essay
  4. 都不是有效的命令。
  5. EOF是由getchar()和scanf()返回的信号(一个特殊值),表明函数检测到文件结尾。
  6. a.输出是:If you qu
    注意,字符I与字符i不同。还要注意,没有打印i,因为循环在检测到i之后就退出了。
    b.如果系统使用ASCII,输出是:HJacrthjacrt
    while的第1轮迭代中,为ch读取的值是H。第1个putchar()语句使用的ch的值是H,打印完毕后,ch的值加1(现在是ch的值是I)。然后到第2个putchar()语句,因为是++ch,所以先递增ch(现在ch的值是J)再打印它的值。然后进入下一轮迭代,读取输入序列中的下一个字符(a),重复以上步骤。需要注意的是,两个递增运算符只在ch被赋值后影响它的值,不会让程序在输入序列中移动。
  7. C的标准I/O库把不同的文件映射为统一的流来统一处理。
  8. 数值输入会跳过空格和换行符,但是字符输入不会。假设有下面的代码:
    int score;
    char grade;
    printf(“Enter the score.\n”);
    scanf("%s", &score);
    printf(“Enter the letter grade.\n”);
    grade = getchar();
    如果输入分数98,然后按下Enter键把分数发送给程序,其实还发送了一个换行符。这个换行符会留在输入序列中,成为下一个读取的值(grade)。如果在字符输入之前输入了数字,就应该在处理字符输入之前添加删除换行符的代码。

编程练习

  1. 设计一个程序,统计在读到文件结尾之前读取的字符数。

  2. 编写一个程序,在遇到EOF之前,把输入作为字符流读取。程序要打印每个输入的字符及其相应的ASCII十进制值。注意,在ASCII序列中,空格字符前面的字符都是非打印字符,要特殊处理这些字符。如果非打印字符是换行符或制表符,则分别打印\n或\t。否则,使用控制字符表示法。例如,ASCII的1是Ctrl+A,可显示为^A。注意,A的ASCII值是Ctrl+A的值加上64。其他非打印字符也有类似的关系。除每次遇到换行符打印新的一行之外,每行打印10对值。(注意:不同的操作系统其控制字符可能不同。)

  3. 编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告输入中的大写字母和小写字母的个数。假设大小写字母数值是连续的。或者使用ctype.h库中合适的分类函数更方便。

  4. 编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告平均每个单词的字母数。不要把空白统计为单词的字母。实际上,标点符号也不应该统计,但是现在暂时不同考虑这么多(如果你比较在意这点,考虑使用ctype.h系列中的ispunct()函数)。

  5. 修改程序清单8.4的猜数字程序,使用更智能的猜测策略。例如,程序最初猜50,询问用户是猜大了、猜小了还是猜对了。如果猜小了,那么下一次猜测的值应是50和100中值,也就是75。如果这次猜大了,那么下一次猜测的值应是50和75的中值,等等。使用二分查找(binary search)策略,如果用户没有欺骗程序,那么程序很快就会猜到正确的答案。

  6. 修改程序清单8.8中的get_first()函数,让该函数返回读取的第1个非空白字符,并在一个简单的程序中测试。

  7. 修改第7章的编程练习8,用字符代替数字标记菜单的选项。用q代替5作为结束输入的标记。

  8. 编写一个程序,显示一个提供加法、减法、乘法、除法的菜单。获得用户选择的选项后,程序提示用户输入两个数字,然后执行用户刚才选择的操作。该程序只接受菜单提供的选项。程序使用float类型的变量存储用户输入的数字,如果用户输入失败,则允许再次输入。进行除法运算时,如果用户输入0作为第2个数(除数),程序应提示用户重新输入一个新值。该程序的一个运行示例如下:

    Enter the operation of your choice:

    a. add s. subtract

    m. multiply d. divide

    q. quita

    Enter first number: 22 .4

    Enter second number: one

    one is not an number.

    Please enter a number, such as 2.5, -1.78E8, or 3: 1

    22.4 + 1 = 23.4

    Enter the operation of your choice:

    a. add s. subtract

    m. multiply d. divide

    q. quitd

    Enter first number: 18.4

    Enter second number: 0

    Enter a number other than 0: 0.2

    18.4 / 0.2 = 92

    Enter the operation of your choice:

    a. add s. subtract

    m. multiply d. divide

    q. quitq

    Bye.


   #include <stdio.h>
   int main(void)
   {
    
       unsigned count = 0u;
       printf("Enter characters.The program is to count the numeber of characters!\n");
       while (getchar() != EOF)
       {
    
           count++;
       }
       printf("%u character(s).\n",count);
       return 0;
   }
  1. #include <iostream>
    #include <string>
    using namespace std;
    int main(void)
    {
          
    	string str = "";
    	char ch;
    	while ((ch = getchar()) != EOF)
    	{
          
    		/* code */
    		str.push_back(ch);
    	}
    	unsigned int count = 0u;
    	for (int i = 0; i < str.length(); i++)
    	{
          
    		cout << str[i] << " " << (int)str[i] << "\t";
    		count++;
    		if (str[i] == '\n')
    		{
          
    			count = 0u;
    		}
    		if (count == 10u)
    		{
          
    			cout << endl;
    			count = 0u;
    		}
    		
    	}
    	return 0;
    }
    
  2. #include <stdio.h>
    #include <ctype.h>
    int main(void)
    {
          
        unsigned upper_num = 0u, lower_num = 0u;
        char ch;
        while ((ch = getchar()) != EOF)
        {
          
            if (islower(ch))
            {
          
                lower_num++;
            }
            else if (isupper(ch))
            {
          
                upper_num++;
            }
        }
        printf("upper character: %u lower characters: %u\n", upper_num, lower_num);
        return 0;
    }
    
  3. #include <stdio.h>
    #include <ctype.h>
    int main(void)
    {
          
        char ch;
        unsigned int char_num = 0u, word_num = 0u;
        while ((ch = getchar()) != EOF)
        {
          
            if (ch != ' ' && !ispunct(ch) && isprint(ch))
            {
          
                char_num++;
            }
            if (ch == ' ' || ispunct(ch))
            {
          
                word_num++;
            }
        }
        printf("%u %u %g\n", char_num, word_num, (float)char_num / word_num);
        return 0;
    }
    
  4. #include <stdio.h>
    #include <ctype.h>
    int main(void)
    {
          
    	int guess = (0 + 100) / 2;
    	char ch;
    	int up = 100, down = 0;
    	printf("Pick an integer from 1 to 100. I will try to guess ");
    	printf("it.\nRespond with a d if my guess is big and with");
    	printf("\na x if it is small.\n");
    	printf("and with a y if my guess is right.\n");
    
    	printf("Uh...is your number %d?\n", guess);
    	while ((ch = getchar()) != 'y') /* 获取响应,与 y 做对比 */
    	{
          
    		if (isalpha(ch))
    		{
          
    			ch = tolower(ch);
    		}
    		switch (ch)
    		{
          
    		case 'd':
    			up = guess;
    			guess = (guess + down) / 2;
    			
    			printf("Well, then, is it %d?\n", guess);
    			break;
    		case 'x':
    			down = guess;
    			guess = (guess + up) / 2;
    			
    			printf("Well, then, is it %d?\n", guess);
    			break;
    		default:
    			printf("Error!\n");
    			break;
    		}
    		while (getchar() != '\n')
    		{
          
    			continue;
    		}
    	}
    	printf("I knew I could do it!\n");
    	return 0;
    }
    
  5. #include <stdio.h>
    #include <ctype.h>
    char get_first(void)
    {
          
        int ch;
        // ch = getchar(); /* 读取下一个字符 */
        while (ch = getchar())
        {
          
            if (!isblank(ch))
                break;
        }
        while (getchar() != '\n')
            continue; /* 跳过该行剩下的内容 */
        return ch;
    }
    int main(void)
    {
          
        char ch = get_first();
        printf("%c\n",ch);
        return 0;
    }
    
  6. #include <stdio.h>
    #include <stdbool.h>
    #include<ctype.h>
    #define BASETIME 40
    #define BI 1.5
    
    #define SHUIBASE 300
    #define SHUIBASE1 450
    
    #define SHUIRATE 0.15
    #define SHUIRATE2 0.2
    #define SHUIRATE3 0.25
    char get_first(void);
    int main(void)
    {
          
        float base;
        while (true)
        {
          
            printf("*****************************************************************\n");
            printf("Enter the number corresponding to the desired pay rate or action:\n");
            printf("a) $8.75/hr\n");
            printf("b) $9.33/hr\n");
            printf("c) $10.00/hr\n");
            printf("d) $11.20/hr\n");
            printf("q) quit\n");
            printf("*****************************************************************\n");
            printf("please enter option number: ");
            char option = get_first();
            switch (option)
            {
          
            case 'a':
                base = 8.75;
                break;
            case 'b':
                base = 9.33;
                break;
            case 'c':
                base = 10.00;
                break;
            case 'd':
                base = 11.20;
            case 'q':
                printf("Exit!\n");
                return 0;
                break;
            default:
                printf("Error!Repeat!\n");
                continue;
                break;
            }
            unsigned time;
            printf("enter the number of work time: ");
            scanf("%u", &time);
            float money;
            money = time >= BASETIME ? (BASETIME * base + (time - BASETIME) * BI * base) : (base * time);
            float rate;
            if (money <= SHUIBASE)
            {
          
                rate = money * SHUIRATE;
            }
            else if (money <= SHUIBASE1)
            {
          
                rate = SHUIBASE * SHUIRATE + (money - SHUIBASE) * SHUIRATE2;
            }
            else
            {
          
                rate = SHUIBASE * SHUIRATE + (SHUIBASE1 - SHUIBASE) * SHUIRATE2 + (money - SHUIBASE1) * SHUIRATE3;
            }
            printf("shuiqian: %g,shui: %g,shuihou: %g\n", money, rate, money - rate);
        }
    }
    char get_first(void)
    {
          
        int ch;
        // ch = getchar(); /* 读取下一个字符 */
        while (ch = getchar())
        {
          
            if (!isblank(ch))
                break;
        }
        while (getchar() != '\n')
            continue; /* 跳过该行剩下的内容 */
        return ch;
    }
    
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>

char get_first(void);
char get_choice(void);
float add(float num1, float num2);
float sub(float num1, float num2);
float mul(float num1, float num2);
float div(float num1, float num2);
float get_float(void);
int main(void)
{
    
	char option;
	float num1, num2;
	while (true)
	{
    
		option = get_choice();
		if (option == '0')
		{
    
			printf("Error!Repeat!\n");
			continue;
		}
		if (option != 'q')
		{
    
			printf("Enter first number: \n");
			num1 = get_float();
			printf("Enter second number: \n");
			num2 = get_float();
		}
		switch (option)
		{
    
		case 'a':
			printf("%g + %g = %g\n", num1, num2, add(num1, num2));
			break;
		case 's':
			printf("%g - %g = %g\n", num1, num2, sub(num1, num2));
			break;
		case 'm':
			printf("%g * %g = %g\n", num1, num2, mul(num1, num2));
			break;
		case 'd':
			while (num2 == 0)
			{
    
				printf("input error! Repeat!\n");
				printf("Enter second number: \n");
				num2 = get_float();
			}
			printf("%g / %g = %g\n", num1, num2, div(num1, num2));
			break;
		case 'q':
			printf("Bye!\n");
			return 0;
		default:
			printf("Program error!\n");
			return 0;
			break;
		}
	}
	return 0;
}
char get_first(void)
{
    
	int ch;
	// ch = getchar(); /* 读取下一个字符 */
	while (ch = getchar())
	{
    
		if (!isblank(ch))
			break;
	}
	while (getchar() != '\n')
		continue; /* 跳过该行剩下的内容 */
	return ch;
}
char get_choice(void)
{
    
	printf("=====================================\n");
	printf("a. add   s. subtract\n");
	printf("m. multiply  d. divide\n");
	printf("q. quit\n");
	printf("=====================================\n");
	printf("Enter the operation of your choice:\n");
	char ch = get_first();
	if (ch != 'a' && ch != 's' && ch != 'm' && ch != 'd' && ch != 'q')
	{
    
		return '0';
	}
	return ch;
}
float add(float num1, float num2)
{
    
	return num1 + num2;
}
float sub(float num1, float num2)
{
    
	return num1 - num2;
}
float mul(float num1, float num2)
{
    
	return num1 * num2;
}
float div(float num1, float num2)
{
    
	return num1 / num2;
}
float get_float(void)
{
    
	float num;
	while (scanf("%f", &num) != 1)
	{
    
		printf("input error! Repeat!\n");
		while (getchar() != '\n')
		{
    
			continue;
		}
	}
	while (getchar() != '\n')
	{
    
		continue;
	}
	return num;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_46698891/article/details/113958430

智能推荐

opencv(十):单目相机“三维重建”_opencv单目三维重建-程序员宅基地

文章浏览阅读3.7k次,点赞4次,收藏30次。import cv2import numpy as npimport globdef draw(img, corners, imgpts): corner = tuple(corners[0].ravel()) img = cv2.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5) img = cv2...._opencv单目三维重建

新浪云PHP7.0连接MySQL_新浪云 php连接mysql数据库-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏2次。最近需要部署项目到新浪云服务器上,后台语言使用的是PHP,在连接MySQL时遇到了小小的问题。首先我按照新浪云上的共享型数据库连接指南(http://www.sinacloud.com/doc/sae/php/mysql.html#api-shi-yong-shou-ce)来编写PHP连接代码,是可以连接的,另外搜索了一下相关信息,发现有一种更便捷的连接方式:$mysql = new Sa_新浪云 php连接mysql数据库

VulnRecap 24 年 1 月 22 日 – 观看 Chrome、Ivanti、Citrix 问题-程序员宅基地

文章浏览阅读18次,点赞4次,收藏4次。本周的漏洞新闻包括 GitHub 凭证访问、新的 Chrome 修复程序以及来自中国网站托管的盗版应用程序的隐藏恶意软件。 随着 Netscaler 和 Endpoint Manager Mobile 中出现更多漏洞,Citrix 和 Ivanti 也遇到了更多问题.确保您的安全团队定期检查供应商的软件和硬件更新是否有任何补丁,并特别关注网络设备。 如果您有 GitHub 实例,请导入所有必要的新密钥.

SpringCloud学习笔记(十二、断路器监控),springcloud视频教程-程序员宅基地

文章浏览阅读515次,点赞25次,收藏10次。if(!System.err.printf(“端口%d被占用了,无法启动%n”, port );

vim常用快捷键_vim 常用快捷键大全-程序员宅基地

文章浏览阅读424次。一.文本模式下常用的快捷键:(一)一般模式切换到编辑模式: 1. i 在光标所在处插入 2. a 在光标下一字符处插入 3. o 在光标所在行下一行插入新一行 4. O 在光标所在行上一行插入新一行 5. [Esc] 退出编辑模式(二)一般模式: 移动光标(n为数字): 1. G 移动到文件的最后一行 _vim 常用快捷键大全

Linux服务器每次ssh登入时,conda无效_ssh conda-程序员宅基地

文章浏览阅读95次。Linux服务器每次ssh登入时,conda无效_ssh conda

随便推点

异构网络中基于元图的推荐——FMG-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏15次。主要参考论文:《Meta-Graph Based Recommendation Fusion over Heterogeneous Information Networks》概述基于异构信息网络的推荐系统面临两个问题:1.如何表示高阶推荐语义;2.如何融合异构信息用于推荐。针对第一个问题,本文采用了元图(和元路径)来表示高阶推荐语义;对于第二个问题,本文先用标准矩阵分解技术(MF)分解每个元图(元路径)得到的相似度矩阵,生成用户和物品的隐式表征,对不同元图(元路径)得到的表征使用带有group las_基于元图的

Android WebView设置cookie_android webview cookie httponly-程序员宅基地

文章浏览阅读2k次。场景: Android原生和H5的混合开发中,登录维护在原生,某些详情页面嵌套Webview。 问题:原生登录之后将cookie存起来,在 WebView.loadUrl("");之前设置cookie,针对每个项目cookie它的格式都不相同。 如果你不知道你们url的cookie格式的话,你可以将url复制到pc端的浏览器中,打开开发者模式如..._android webview cookie httponly

NLP实战:Pytorch实现6大经典深度学习中文短文本分类-bert+ERNIE+bert_CNN+bert_RNN+bert_RCNN+bert_DPCNN_pytorch nlp-程序员宅基地

文章浏览阅读4.5k次,点赞10次,收藏62次。本文基于PyTorch框架,实现了6种经典的深度学习中文文本分类模型,这些模型包括基于Transformer模型的Bert和ERNIE,以及结合卷积神经网络、循环神经网络和深度金字塔卷积神经网络的bert_CNN、bert_RNN、bert_RCNN和bert_DPCNN,并对各模型进行了训练与结果比较。_pytorch nlp

Ubuntu 下安装 QQ_ubuntu安装qq-程序员宅基地

文章浏览阅读2.8w次,点赞27次,收藏130次。Linux for QQ 在Ubuntu系统下安装_ubuntu安装qq

消防应急照明和疏散指示系统在建筑行业的应用_集中控制型和非集中控制型消防应急照明和疏散指示系统谁的应用广泛-程序员宅基地

文章浏览阅读811次,点赞20次,收藏17次。有的建筑虽然也需要消防控制室,但是不在太建筑内,例如一个建筑群内仅在一个单体内设置了消防控制室,其余单体仅设置报警控制器并将相关报警线路引至消防控制室,这种情况也认为是设置消防控制室的,消防应急照明需要采用集中控制型,可以将系统的主机设置在消防控制室中,单体设置区域分机线路汇总到主机。基于此保证在火灾发生时,能够准确改变消防应急标志灯具的指示方向,点亮消防应急照明灯,帮助建筑内的人群选择逃生疏散路线,指引安全的逃生方向,保障群众的人身安全,为各类用户担心的安全问题解决了后顾之忧。5.4.4权限管理界面。_集中控制型和非集中控制型消防应急照明和疏散指示系统谁的应用广泛

ESXI物理机安装提示No Network Adapters-程序员宅基地

文章浏览阅读3k次。下载好ESXI并且刻录了U盘,准备在物理PC机安装ESXI时,提示No Network Adapters找不到网卡驱动。解决办法:需要自行打包驱动到对应的ESXI版本中ESXi-Customizer定制ESXI网卡驱动:https://blog.whsir.com/post-3359.htmlESXi6.7物理机安装之网卡驱动封装:https://blog.whsir.com/post-3377.html怎么查询当前网卡,就不用过多说明了吧,如果当前机器是一台linux主机可以lspci_no network adapters