技术标签: 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
检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。
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;
}
注意:
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
注意:
/* 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!
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;
}
// 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;
}
设计一个程序,统计在读到文件结尾之前读取的字符数。
编写一个程序,在遇到EOF之前,把输入作为字符流读取。程序要打印每个输入的字符及其相应的ASCII十进制值。注意,在ASCII序列中,空格字符前面的字符都是非打印字符,要特殊处理这些字符。如果非打印字符是换行符或制表符,则分别打印\n或\t。否则,使用控制字符表示法。例如,ASCII的1是Ctrl+A,可显示为^A。注意,A的ASCII值是Ctrl+A的值加上64。其他非打印字符也有类似的关系。除每次遇到换行符打印新的一行之外,每行打印10对值。(注意:不同的操作系统其控制字符可能不同。)
编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告输入中的大写字母和小写字母的个数。假设大小写字母数值是连续的。或者使用ctype.h库中合适的分类函数更方便。
编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告平均每个单词的字母数。不要把空白统计为单词的字母。实际上,标点符号也不应该统计,但是现在暂时不同考虑这么多(如果你比较在意这点,考虑使用ctype.h系列中的ispunct()函数)。
修改程序清单8.4的猜数字程序,使用更智能的猜测策略。例如,程序最初猜50,询问用户是猜大了、猜小了还是猜对了。如果猜小了,那么下一次猜测的值应是50和100中值,也就是75。如果这次猜大了,那么下一次猜测的值应是50和75的中值,等等。使用二分查找(binary search)策略,如果用户没有欺骗程序,那么程序很快就会猜到正确的答案。
修改程序清单8.8中的get_first()函数,让该函数返回读取的第1个非空白字符,并在一个简单的程序中测试。
修改第7章的编程练习8,用字符代替数字标记菜单的选项。用q代替5作为结束输入的标记。
编写一个程序,显示一个提供加法、减法、乘法、除法的菜单。获得用户选择的选项后,程序提示用户输入两个数字,然后执行用户刚才选择的操作。该程序只接受菜单提供的选项。程序使用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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
文章浏览阅读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单目三维重建
文章浏览阅读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数据库
文章浏览阅读18次,点赞4次,收藏4次。本周的漏洞新闻包括 GitHub 凭证访问、新的 Chrome 修复程序以及来自中国网站托管的盗版应用程序的隐藏恶意软件。 随着 Netscaler 和 Endpoint Manager Mobile 中出现更多漏洞,Citrix 和 Ivanti 也遇到了更多问题.确保您的安全团队定期检查供应商的软件和硬件更新是否有任何补丁,并特别关注网络设备。 如果您有 GitHub 实例,请导入所有必要的新密钥.
文章浏览阅读515次,点赞25次,收藏10次。if(!System.err.printf(“端口%d被占用了,无法启动%n”, port );
文章浏览阅读424次。一.文本模式下常用的快捷键:(一)一般模式切换到编辑模式: 1. i 在光标所在处插入 2. a 在光标下一字符处插入 3. o 在光标所在行下一行插入新一行 4. O 在光标所在行上一行插入新一行 5. [Esc] 退出编辑模式(二)一般模式: 移动光标(n为数字): 1. G 移动到文件的最后一行 _vim 常用快捷键大全
文章浏览阅读95次。Linux服务器每次ssh登入时,conda无效_ssh conda
文章浏览阅读2.2k次,点赞2次,收藏15次。主要参考论文:《Meta-Graph Based Recommendation Fusion over Heterogeneous Information Networks》概述基于异构信息网络的推荐系统面临两个问题:1.如何表示高阶推荐语义;2.如何融合异构信息用于推荐。针对第一个问题,本文采用了元图(和元路径)来表示高阶推荐语义;对于第二个问题,本文先用标准矩阵分解技术(MF)分解每个元图(元路径)得到的相似度矩阵,生成用户和物品的隐式表征,对不同元图(元路径)得到的表征使用带有group las_基于元图的
文章浏览阅读2k次。场景: Android原生和H5的混合开发中,登录维护在原生,某些详情页面嵌套Webview。 问题:原生登录之后将cookie存起来,在 WebView.loadUrl("");之前设置cookie,针对每个项目cookie它的格式都不相同。 如果你不知道你们url的cookie格式的话,你可以将url复制到pc端的浏览器中,打开开发者模式如..._android webview cookie httponly
文章浏览阅读4.5k次,点赞10次,收藏62次。本文基于PyTorch框架,实现了6种经典的深度学习中文文本分类模型,这些模型包括基于Transformer模型的Bert和ERNIE,以及结合卷积神经网络、循环神经网络和深度金字塔卷积神经网络的bert_CNN、bert_RNN、bert_RCNN和bert_DPCNN,并对各模型进行了训练与结果比较。_pytorch nlp
文章浏览阅读2.8w次,点赞27次,收藏130次。Linux for QQ 在Ubuntu系统下安装_ubuntu安装qq
文章浏览阅读811次,点赞20次,收藏17次。有的建筑虽然也需要消防控制室,但是不在太建筑内,例如一个建筑群内仅在一个单体内设置了消防控制室,其余单体仅设置报警控制器并将相关报警线路引至消防控制室,这种情况也认为是设置消防控制室的,消防应急照明需要采用集中控制型,可以将系统的主机设置在消防控制室中,单体设置区域分机线路汇总到主机。基于此保证在火灾发生时,能够准确改变消防应急标志灯具的指示方向,点亮消防应急照明灯,帮助建筑内的人群选择逃生疏散路线,指引安全的逃生方向,保障群众的人身安全,为各类用户担心的安全问题解决了后顾之忧。5.4.4权限管理界面。_集中控制型和非集中控制型消防应急照明和疏散指示系统谁的应用广泛
文章浏览阅读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