学完C语言之后,我就去阅读《C Primer Plus》这本经典的C语言书籍,对每一章的编程练习题都做了相关的解答,仅仅代表着我个人的解答思路,如有错误,请各位大佬帮忙点出!
由于使用的是命令行参数常用于linux系统或者vscode,但此代码是运行于vs2022的,测试截图就不弄了。
1.修改程序清单13.1中的程序,要求提示用户输入文件名,并读取用户 输入的信息,不使用命令行参数。
#include <stdio.h>
#include <stdlib.h>
#define LEN 50
int main(int argc, char* argv[])
{
int ch;
FILE* fp;
int count = 0;
char filename[LEN];
printf("Please enter a filename: ");
scanf("%s", filename);
fp = fopen(filename, "r");
if (fp == NULL)
{
printf("Can't open %s\n", filename);
exit(0);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch, stdout);
count++;
}
fclose(fp);
printf("\nFile %s has %d characters\n", filename, count);
return 0;
}
2.编写一个文件拷贝程序,该程序通过命令行获取原始文件名和拷贝文 件名。尽量使用标准I/O和二进制模式。
#include <stdio.h>
#include <stdlib.h>
#define BUF 512
int main(int argc, char* argv[])
{
int bytes;
FILE* source;
FILE* target;
char temp[BUF];
if ((source = fopen("test.txt", "rb")) == NULL)
{
printf("Can't open %s\n", "test.txt");
exit(0);
}
if ((target = fopen("copy.txt", "wb")) == NULL)
{
printf("Can't open %s\n", "copy.txt");
exit(0);
}
while ((bytes = fread(temp, sizeof(char), BUF, source)) > 0)
{
fwrite(temp, sizeof(char), bytes, target);
}
fclose(source);
fclose(target);
return 0;
}
3.编写一个文件拷贝程序,提示用户输入文本文件名,并以该文件名作 为原始文件名和输出文件名。该程序要使用 ctype.h 中的 toupper()函数,在 写入到输出文件时把所有文本转换成大写。使用标准I/O和文本模式。
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#define LEN 50
int main(int argc, char* argv[])
{
int ch;
FILE* fp;
char filename[LEN];
printf("Please enter a filename: ");
scanf("%s", filename);
fp = fopen(filename, "r+");
if (fp == NULL)
{
printf("Can't open %s\n", filename);
exit(0);
}
while ((ch = getc(fp)) != EOF)
{
fseek(fp, -1L, SEEK_CUR);
putc(toupper(ch), fp);
fseek(fp, 0L, SEEK_CUR);
rewind(fp);
printf("File %s:\n", filename);
while ((ch = getc(fp)) != EOF)
{
putchar(ch);
}
printf("\n");
fclose(fp);
return 0;
}
4.编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。使用 argc控制循环。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int i, temp;
FILE* source;
if (argc == 1)
{
printf("Usage: %s filename[s]\n", argv[0]);
exit(0);
}
for (i = 1; i < argc; i++)
{
source = fopen(argv[i], "r");
if (source == NULL)
{
printf("Can't open file %s\n", argv[i]);
continue;
}
printf("File %s:\n", argv[i]);
while ((temp = getc(source)) != EOF)
{
putchar(temp);
}
printf("\n");
fclose(source);
}
return 0;
}
5.修改程序清单13.5中的程序,用命令行界面代替交互式界面。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE* source, FILE* dest)
{
size_t bytes;
static char temp[BUFSIZE];
while ((bytes = fread(temp, sizeof(char), BUFSIZE, source)) > 0)
{
fwrite(temp, sizeof(char), bytes, dest);
}
return;
}
int main(int argc, char* argv[])
{
int i, ch;
int files = 0;
FILE* fa;
FILE* fs;
if (argc < 3)
{
printf("Usage: %s appendfile sourcefile.\n", argv[0]);
exit(0);
}
if ((fa = fopen(argv[1], "a+")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(0);
}
if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create output buffer\n", stderr);
exit(0);
}
for (i = 2; i < argc; i++)
{
if (strcmp(argv[i], argv[1]) == 0)
{
fputs("Can't append file to itself\n", stderr);
}
else if ((fs = fopen(argv[i], "r")) == NULL)
{
printf("Can't open %s\n", argv[i]);
}
else
{
if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create input buffer\n", stderr);
continue;
}
append(fs, fa);
if (ferror(fs) != 0)
{
printf("Error in reading file %s.\n", argv[i]);
}
if (ferror(fa) != 0)
{
printf("Error in writing file %s.\n", argv[1]);
}
fclose(fs);
files++;
printf("File %s appended.\n", argv[i]);
}
}
printf("Done appending. %d files appended.\n", files);
rewind(fa);
printf("%s contents:\n", argv[1]);
while ((ch = getc(fa)) != EOF)
{
putchar(ch);
}
printf("\n");
printf("Done displaying.");
fclose(fa);
return 0;
}
6.使用命令行参数的程序依赖于用户的内存如何正确地使用它们。重写 程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需信 息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 40
int main(int argc, char* argv[])
{
FILE* in, * out;
int ch;
char name[LEN];
int count = 0;
char temp[LEN];
printf("Please enter a filename:\n");
scanf("%s", temp);
in = fopen(temp, "r");
if (in == NULL)
{
printf("I could't open the file \"%s\"\n", temp);
exit(0);
}
strncpy(name, temp, LEN - 5);
name[LEN - 5] = '\0';
strcat(name, ".red");
out = fopen(name, "w");
if (out == NULL)
{
printf("Can't create output file.\n");
exit(1);
}
while ((ch = getc(in)) != EOF)
{
if (count++ % 3 == 0)
{
putc(ch, out);
}
}
fclose(in);
fclose(out);
return 0;
}
7.编写一个程序打开两个文件。可以使用命令行参数或提示用户输入文 件名。
a.该程序以这样的顺序打印:打印第1个文件的第1行,第2个文件的第1 行,第1个文件的第2行,第2个文件的第2行,以此类推,打印到行数较多文 件的最后一行。
b.修改该程序,把行号相同的行打印成一行。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN 256
int main(int argc, char* argv[])
{
FILE* f1;
FILE* f2;
char str1[LEN], str2[LEN];
printf("请输入两个文件的名字:");
scanf("%s %s", str1, str2);
f1 = fopen(str1, "r");
if (f1 == NULL)
{
printf("Can't open file %s\n", str1);
exit(0);
}
f2 = fopen(str2, "r");
if (f2 == NULL)
{
printf("Can't open file %s\n", str2);
exit(0);
}
char* s1 = fgets(str1, LEN, f1);
char* s2 = fgets(str2, LEN, f2);
while (s1 || s2)
{
int len1 = strlen(str1), len2 = strlen(str2);
if (str1[len1 - 1] == '\n')
{
str1[len1 - 1] = '\0';
}
if (str2[len2 - 1] == '\n')
{
str2[len2 - 1] = '\0';
}
if (s1)
{
printf("%s\n", str1);
}
if (s2)
{
printf("%s\n", str2);
}
s1 = fgets(str1, LEN, f1), s2 = fgets(str2, LEN, f2);
}
fclose(f1);
fclose(f2);
return 0;
}
8.编写一个程序,以一个字符和任意文件名作为命令行参数。如果字符 后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件并报告 每个文件中该字符出现的次数。文件名和字符本身也要一同报告。程序应包 含错误检查,以确定参数数量是否正确和是否能打开文件。如果无法打开文件,程序应报告这一情况,然后继续处理下一个文件。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int search(int ch, FILE* fp)
{
int find;
int n = 0;
while ((find = getc(fp)) != EOF)
{
if (ch == find)
{
++n;
}
}
return n;
}
int main(int argc, char* argv[])
{
FILE* fp;
int i, ch, ct;
int count = 0;
if (argc < 2)
{
printf("Usage: %s character filename[s]\n", argv[0]);
exit(-1);
}
if (strlen(argv[1]) != 1)
{
printf("The second argument must be a character!\n");
exit(-1);
}
if (argc == 2)
{
fp = stdin;
ch = argv[1][0];
printf("Please enter a string (EOF to quit): ");
count = search(ch, fp);
printf("%c appeared %d times.\n", ch, count);
}
else if (argc > 2)
{
ch = argv[1][0];
for (i = 2; i < argc; i++)
{
if ((fp = fopen(argv[i], "r")) == NULL)
{
printf("Can't open file %s\n", argv[i]);
continue;
}
printf("File %s:\n", argv[i]);
while ((ct = getc(fp)) != EOF)
{
putchar(ct);
}
rewind(fp);
count = search(ch, fp);
printf("%c appeared %d times in file %s\n", ch, count, argv[i]);
fclose(fp);
}
}
return 0;
}
9.修改程序清单 13.3 中的程序,从 1 开始,根据加入列表的顺序为每个 单词编号。当程序下次运行时,确保新的单词编号接着上次的编号开始。
#include <stdio.h>
#include <stdlib.h>
#define MAX 41
int main(void)
{
FILE* fp;
int ct = 0;
char words[MAX];
fp = fopen("wordy.txt", "a+");
if ( fp == NULL)
{
printf("Can't open \"wordy\" file.\n");
exit(-1);
}
while (fgets(words, MAX, fp) != NULL)
{
ct++;
}
rewind(fp);
printf("Enter words to add to the file; press the #\n");
printf("key at the beginning of a line to terminate.\n");
while ((fscanf(stdin, "%s", words) == 1) && (words[0] != '#'))
{
fprintf(fp, "%-2d : %s\n", ++ct, words);
}
printf("File contents:");
rewind(fp);
while (fgets(words, MAX, fp) != NULL)
{
fputs(words, stdout);
}
printf("Done!\n");
fclose(fp);
return 0;
}
10.编写一个程序打开一个文本文件,通过交互方式获得文件名。通过 一个循环,提示用户输入一个文件位置。然后该程序打印从该位置开始到下 一个换行符之前的内容。用户输入负数或非数值字符可以结束输入循环。
#include <stdio.h>
#include <stdlib.h>
#define LEN 50
int main(int argc, char* argv[])
{
int ch;
FILE* fp;
int number;
char filename[LEN];
printf("Please enter a filename: ");
scanf("%49s", filename);
fp = fopen(filename, "r");
if ( fp == NULL)
{
printf("Can't open file %s\n", filename);
exit(-1);
}
printf("Please enter a file position (<0 or q to quit): ");
while (scanf("%d", &number) == 1 && number >= 0)
{
fseek(fp, number, SEEK_SET);
while ((ch = getc(fp)) != '\n' && ch != EOF)
{
putchar(ch);
}
if (ch == EOF)
{
printf("The position reaches EOF.\n");
break;
}
printf("\nYou can enter again (<0 or q to quit): ");
}
printf("Done.\n");
fclose(fp);
return 0;
}
11.编写一个程序,接受两个命令行参数。第1个参数是一个字符串,第 2个参数是一个文件名。然后该程序查找该文件,打印文件中包含该字符串 的所有行。因为该任务是面向行而不是面向字符的,所以要使用fgets()而不 是getc()。使用标准C库函数strstr()(11.5.7节简要介绍过)在每一行中查找 指定字符串。假设文件中的所有行都不超过255个字符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SLEN 256
int main(int argc, char* argv[])
{
FILE* fp;
char line[SLEN];
if (argc != 3)
{
printf("Usage: %s string filename\n", argv[0]);
exit(-1);
}
if ((fp = fopen(argv[2], "r")) == NULL)
{
printf("Can't open file %s", argv[2]);
exit(-1);
}
while (fgets(line, SLEN, fp) != NULL)
{
if (strstr(line, argv[1]) != NULL)
{
fputs(line, stdout);
}
}
fclose(fp);
return 0;
}
12.创建一个文本文件,内含20行,每行30个整数。这些整数都在0~9 之间,用空格分开。该文件是用数字表示一张图片,0~9表示逐渐增加的灰 度。编写一个程序,把文件中的内容读入一个20×30的int数组中。一种把这 些数字转换为图片的粗略方法是:该程序使用数组中的值初始化一个20×31 的字符数组,用值0 对应空格字符,1 对应点字符,以此类推。数字越大表 示字符所占的空间越大。例如,用#表示9。每行的最后一个字符(第31个) 是空字符,这样该数组包含了20个字符串。最后,程序显示最终的图片 (即,打印所有的字符串),并将结果储存在文本文件中。例如,下面是开 始的数据:
#include <stdio.h>
#include <stdlib.h>
#define ROWS 20
#define COLS 30
#define LEVELS 10
#define LEN 50
const char trans[LEVELS + 1] = " .':~*=&%#";
void change(int(*a)[COLS], char(*b)[COLS], int rows)
{
int row, col;
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
b[row][col] = trans[a[row][col]];
}
}
}
int main(void)
{
int row, col;
FILE* infile;
char filename[LEN];
int figure[ROWS][COLS];
char picture[ROWS][COLS];
printf("Please enter a filename: ");
scanf("%s", filename);
infile = fopen(filename, "r+");
if ( infile == NULL)
{
printf("Can't open file %s\n", filename);
exit(-1);
}
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fscanf(infile, "%d", &figure[row][col]);
}
}
change(figure, picture, ROWS);
printf("File %s have been transformed pictures:\n", filename);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
putchar(picture[row][col]);
}
printf("\n");
}
fseek(infile, 0L, SEEK_END);
putc('\n', infile);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(infile, "%c", picture[row][col]);
}
putc('\n', infile);
}
fclose(infile);
return 0;
}
13.用变长数组(VLA)代替标准数组,完成编程练习12。
#include <stdio.h>
#include <stdlib.h>
#define ROWS 20
#define COLS 30
#define LEVELS 10
#define LEN 50
const char trans[LEVELS + 1] = " .':~*=&%#";
void change(int rows, int cols, int a[rows][cols], char b[rows][cols])
{
int row, col;
for (row = 0; row < rows; row++)
{
for (col = 0; col < cols; col++)
{
b[row][col] = trans[a[row][col]];
}
}
return;
}
int main(void)
{
int row, col;
FILE* infile;
char filename[LEN];
int figure[ROWS][COLS];
char picture[ROWS][COLS];
printf("Please enter a filename: ");
scanf("%s", filename);
infile = fopen(filename, "r+");
if ( infile == NULL)
{
printf("Can't open file %s\n", filename);
exit(-1);
}
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fscanf(infile, "%d", &figure[row][col]);
}
}
change(ROWS, COLS, figure, picture);
printf("File %s have been transformed pictures:\n", filename);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
putchar(picture[row][col]);
}
printf("\n");
}
fseek(infile, 0L, SEEK_END);
putc('\n', infile);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(infile, "%c", picture[row][col]);
}
putc('\n', infile);
}
fclose(infile);
return 0;
}
14.数字图像,尤其是从宇宙飞船发回的数字图像,可能会包含一些失 真。为编程练习12添加消除失真的函数。该函数把每个值与它上下左右相邻 的值作比较,如果该值与其周围相邻值的差都大于1,则用所有相邻值的平 均值(四舍五入为整数)代替该值。注意,与边界上的点相邻的点少于4 个,所以做特殊处理。
#include <stdio.h>
#include <stdlib.h>
#define ROWS 20
#define COLS 30
#define LEVELS 10
#define LEN 50
const char trans[LEVELS + 1] = " .':~*=&%#";
int clean_distortion(int(*a)[COLS], int rows, int x, int y)
{
int i = 0, average = 0, cnt = 0, sur = 0;
int d[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
for (i = 0; i < 4; ++i)
{
int dx = x + d[i][0], dy = y + d[i][1];
if (dx < 0 || dx >= ROWS || dy < 0 || dy >= COLS) continue;
++sur;
if (a[x][y] - a[dx][dy] > 1)
{
++cnt, average += a[dx][dy];
}
}
if (cnt != sur) return a[x][y];
return 1.0 * average / cnt + 0.5;
}
void change(int(*a)[COLS], char(*b)[COLS], int rows)
{
int row, col;
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
b[row][col] = trans[a[row][col]];
}
}
}
int main(void)
{
int row, col;
FILE* infile;
char filename[LEN];
int figure[ROWS][COLS];
char picture[ROWS][COLS];
printf("Please enter a filename: ");
scanf("%s", filename);
infile = fopen(filename, "r+");
if ( infile == NULL)
{
printf("Can't open file %s\n", filename);
exit(-1);
}
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fscanf(infile, "%d", &figure[row][col]);
}
}
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
figure[row][col] = clean_distortion(figure, ROWS, row, col);
}
}
change(figure, picture, ROWS);
printf("File %s have been transformed pictures:\n", filename);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
putchar(picture[row][col]);
}
printf("\n");
}
fseek(infile, 0L, SEEK_END);
putc('\n', infile);
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(infile, "%c", picture[row][col]);
}
putc('\n', infile);
}
fclose(infile);
return 0;
}