C Primer Plus 第十三章 课后答案

目录

 

复习题

1.下面的程序有什么问题

2.下面的程序完成什么任务?(假设在命令行环境中运行)

3.假设程序中有下列语句:

另外,假设成功打开了两个文件。补全下面函数调用中缺少的参数:

a.ch = getc();

b.fprintf( ,"%c\n", );

c.putc( , );

d.fclose(); /* 关闭terky文件 */

4.编写一个程序,不接受任何命令行参数或接受一个命令行参数。如果有一个参数,将其解释为文件名;如果没有参数,使用标准输入(stdin)作为输入。假设输入完全是浮点数。该程序要计算和报告输入数字的算术平均值

5.编写一个程序,接受两个命令行参数。第1个参数是字符,第2个参数是文件名。要求该程序只打印文件中包含给定字符的那些行。

注意

C程序根据'\n'识别文件中的行。假设所有行都不超过256个字符,你可能会想到用fgets()

6.二进制文件和文本文件有何区别?二进制流和文本流有何区别

7.

a.分别用fprintf()和fwrite()储存8238201有何区别?

b.分别用putc()和fwrite()储存字符S有何区别?

8.下面语句的区别是什么?

printf("Hello, %s\n", name);

fprintf(stdout, "Hello, %s\n", name);

fprintf(stderr, "Hello, %s\n", name);

9."a+"、"r+"和"w+"模式打开的文件都是可读写的。哪种模式更适合用来更改文件中已有的内容?

编程题

1.修改程序清单13.1中的程序,要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数

2.编写一个文件拷贝程序,该程序通过命令行获取原始文件名和拷贝文件名。尽量使用标准I/O和二进制模式

3.编写一个文件拷贝程序,提示用户输入文本文件名,并以该文件名作为原始文件名和输出文件名。该程序要使用 ctype.h 中的 toupper()函数,在写入到输出文件时把所有文本转换成大写。使用标准I/O和文本模式

4.编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。使用argc控制循环

5.修改程序清单13.5中的程序,用命令行界面代替交互式界面

6.使用命令行参数的程序依赖于用户的内存如何正确地使用它们。重写程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需信息。

7.编写一个程序打开两个文件。可以使用命令行参数或提示用户输入文件名。

a.该程序以这样的顺序打印:打印第1个文件的第1行,第2个文件的第1行,第1个文件的第2行,第2个文件的第2行,以此类推,打印到行数较多文件的最后一行。

b.修改该程序,把行号相同的行打印成一行。

8.编写一个程序,以一个字符和任意文件名作为命令行参数。如果字符后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件并报告每个文件中该字符出现的次数。文件名和字符本身也要一同报告。程序应包含错误检查,以确定参数数量是否正确和是否能打开文件。如果无法打开文件,程序应报告这一情况,然后继续处理下一个文件。

9.修改程序清单 13.3 中的程序,从 1 开始,根据加入列表的顺序为每个单词编号。当程序下次运行时,确保新的单词编号接着上次的编号开始。

10.编写一个程序打开一个文本文件,通过交互方式获得文件名。通过一个循环,提示用户输入一个文件位置。然后该程序打印从该位置开始到下一个换行符之前的内容。用户输入负数或非数值字符可以结束输入循环。

11.编写一个程序,接受两个命令行参数。第1个参数是一个字符串,第2个参数是一个文件名。然后该程序查找该文件,打印文件中包含该字符串的所有行。因为该任务是面向行而不是面向字符的,所以要使用fgets()而不是getc()。使用标准C库函数strstr()(11.5.7节简要介绍过)在每一行中查找指定字符串。假设文件中的所有行都不超过255个字符。

12.创建一个文本文件,内含20行,每行30个整数。这些整数都在0~9之间,用空格分开。该文件是用数字表示一张图片,0~9表示逐渐增加的灰度。编写一个程序,把文件中的内容读入一个20×30的int数组中。一种把这些数字转换为图片的粗略方法是:该程序使用数组中的值初始化一个20×31的字符数组,用值0 对应空格字符,1 对应点字符,以此类推。数字越大表示字符所占的空间越大。例如,用#表示9。每行的最后一个字符(第31个)是空字符,这样该数组包含了20个字符串。最后,程序显示最终的图片(即,打印所有的字符串),并将结果储存在文本文件中。例如,下面是开始的数据:

0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 2 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 5 2 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 1 9 8 5 4 5 2 0 0 0 0 0 0 0 0 0

0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 0 4 5 2 0 0 0 0 0 0 0 0

0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 4 5 2 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 1 8 5 0 0 0 4 5 2 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 4 5 2 0 0 0 0 0

5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5

8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8

9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9 9 9 9 9

8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8

5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0

0 0 0 0 2 2 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0

0 0 0 0 3 3 0 0 0 0 0 0 5 8 9 9 8 5 0 5 6 1 1 1 1 6 5 0 0 0

0 0 0 0 4 4 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0

0 0 0 0 5 5 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

根据以上描述选择特定的输出字符,最终输出如下:

13.用变长数组(VLA)代替标准数组,完成编程练习12。

14.数字图像,尤其是从宇宙飞船发回的数字图像,可能会包含一些失真。为编程练习12添加消除失真的函数。该函数把每个值与它上下左右相邻的值作比较,如果该值与其周围相邻值的差都大于1,则用所有相邻值的平均值(四舍五入为整数)代替该值。注意,与边界上的点相邻的点少于4个,所以做特殊处理


复习题

1.下面的程序有什么问题

int main(void) 
{ 
    int * fp; 
    int k; 
    fp = fopen("gelatin"); 
    for (k = 0; k < 30; k++) 
        fputs(fp, "Nanette eats gelatin."); 
    fclose("gelatin");
     return 0; 
}
#include <stdio.h>

int main(void) 
{ 
    FILE *fp; 
    int k; 
    fp = fopen("gelatin", "w"); 
    for (k = 0; k < 30; k++) 
        fputs("Nanette eats gelatin.\n", fp); 
    fclose(fp);
    return 0; 
}

2.下面的程序完成什么任务?(假设在命令行环境中运行)

#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h> 
int main(int argc, char *argv []) 
{ 
    int ch; 
    FILE *fp; 
    if (argc < 2) 
        exit(EXIT_FAILURE); 
    if ((fp = fopen(argv[1], "r")) == NULL) 
        exit(EXIT_FAILURE); 
    while ((ch = getc(fp)) != EOF) 
        if (isdigit(ch)) 
            putchar(ch); 
    fclose(fp); 
    return 0; 
}

如果有第二个参数,尝试打开一个和该参数名同名的文件,如果该文件可以打开,在屏幕上输出一个其中的所有数字

3.假设程序中有下列语句:

#include <stdio.h> 
FILE * fp1,* fp2; 
char ch; 
fp1 = fopen("terky", "r"); 
fp2 = fopen("jerky", "w");

另外,假设成功打开了两个文件。补全下面函数调用中缺少的参数:

a.ch = getc();

b.fprintf( ,"%c\n", );

c.putc( , );

d.fclose(); /* 关闭terky文件 */

a. fp1

b. fp2        ch

c. ch        fp2

d. fp1

4.编写一个程序,不接受任何命令行参数或接受一个命令行参数。如果有一个参数,将其解释为文件名;如果没有参数,使用标准输入(stdin)作为输入。假设输入完全是浮点数。该程序要计算和报告输入数字的算术平均值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void mktxt()
{
    srand((unsigned long)time(0));
    FILE *fp;
    fp = fopen("321.txt", "w");
    for (int j = 0; j < 100; ++j)
    {
        for (int i = 0; i < 7; ++i)
        {
            if(i == 3)
            {
                putc('.', fp);
            }
            putc(rand() % 10 + '0', fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char** argv)
{
    mktxt();
    FILE *fp;
    double n, sum = 0.0;
    int num = 0;
    if(argc == 1)
    {
        fp = stdin;
    }
    else if(argc == 2)
    {
        if((fp = fopen(argv[1], "r")) == NULL)
        {
            fprintf(stderr, "Open %s failed\n", argv[1]);
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        fprintf(stderr, "Usage:%s [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    while(fscanf(fp, "%lf", &n) == 1)
    {
        num++;
        sum += n;
    }
    if(num)
    {
        printf("%lf\n", sum / n);
    }
    else
    {
        puts("There is no number");
    }
    return 0;
}

5.编写一个程序,接受两个命令行参数。第1个参数是字符,第2个参数是文件名。要求该程序只打印文件中包含给定字符的那些行。

注意

C程序根据'\n'识别文件中的行。假设所有行都不超过256个字符,你可能会想到用fgets()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void mktxt()
{
    srand((unsigned long)time(0));
    FILE *fp;
    fp = fopen("321.txt", "w");
    for (int j = 0; j < 100; ++j)
    {
        int n = rand() % 256;
        for (int i = 0; i < n; ++i)
        {
            putc(32 + rand() % 95, fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char** argv)
{
    mktxt();
    FILE *fp;
    char ch;
    if(argc != 3)
    {
        fprintf(stderr, "Usage:%s [character] [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        if(strlen(argv[1]) != 1)
        {
            fprintf(stderr, "The second parameter should be a character\n");
            exit(EXIT_FAILURE);
        }
        if((fp = fopen(argv[2], "r")) == NULL)
        {
            fprintf(stderr, "Open %s failed\n", argv[1]);
            exit(EXIT_FAILURE);
        }
    }
    ch = argv[1][0];
    char a[256];
    while(fgets(a, 256, fp) != NULL)
    {
        if(strchr(a, ch));
        {
            puts(a);
        }
    }
    return 0;
}

6.二进制文件和文本文件有何区别?二进制流和文本流有何区别

这两种文件格式对系统的依赖性不同:二进制流和文本流的区别包括是在读写流时程序执行的转换(二进制流不转换,文本流可能要转换换行符和其他字符)

7.

a.分别用fprintf()和fwrite()储存8238201有何区别?

b.分别用putc()和fwrite()储存字符S有何区别?

a. 用fprintf()时把8238201当做7个字符存储,用fwrite()时把8238201当做一个4字节整形存储

b. 没有区别,都将其视为一个单字节二进制码

8.下面语句的区别是什么?

printf("Hello, %s\n", name);

fprintf(stdout, "Hello, %s\n", name);

fprintf(stderr, "Hello, %s\n", name);

第一句和第二句没有区别,第三条语句把消息写到标准错误上。

通常,标准错误被定向到与标准输出相同的位置。但标准错误不受标准输出重定向的影响

9."a+"、"r+"和"w+"模式打开的文件都是可读写的。哪种模式更适合用来更改文件中已有的内容?

r+

a+只允许在文件的末尾添加,w+会丢弃文件原有内容

编程题

1.修改程序清单13.1中的程序,要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数

#include <stdio.h> 
#include <stdlib.h>  // 提供 exit()的原型
int main()
{ 
    int ch;     // 读取文件时,储存每个字符的地方
    FILE *fp;   // “文件指针”
    unsigned long count = 0;
    char a[50];
    printf("Input the filename:");
    gets(a);
    if ((fp = fopen(a, "r")) == NULL)
    {
        printf("Can't open %s\n", a);
        exit(EXIT_FAILURE);
    }
    while ((ch = getc(fp)) != EOF)
    {
        putc(ch, stdout); // 与 putchar(ch);相同
        count++;
    }
    fclose(fp);
    printf("File %s has %lu characters\n", a, count);
    return 0;
}

2.编写一个文件拷贝程序,该程序通过命令行获取原始文件名和拷贝文件名。尽量使用标准I/O和二进制模式

#include <stdio.h> 
#include <stdlib.h>
#include <time.h>

void mktxt(char* a)
{
    srand((unsigned long)time(0));
    FILE *fp;
    fp = fopen(a, "w");
    for (int j = 0; j < 100; ++j)
    {
        int n = rand() % 256;
        for (int i = 0; i < n; ++i)
        {
            putc(32 + rand() % 95, fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char** argv)
{
    mktxt(argv[1]);
    FILE *fp1, *fp2;
    if(argc != 3)
    {
        fprintf(stderr, "Usage:%s [filename] [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        if((fp1 = fopen(argv[1], "rb")) == NULL)
        {
            fprintf(stderr, "Open %s failed\n", argv[1]);
            exit(EXIT_FAILURE);
        }
        if((fp2 = fopen(argv[2], "wb")) == NULL)
        {
            fprintf(stderr, "Open %s failed\n", argv[2]);
            exit(EXIT_FAILURE);
        }
    }
    char ch;
    while((ch = getc(fp1)) != EOF)
    {
        putc(ch, fp2);
    }
    return 0;
}

3.编写一个文件拷贝程序,提示用户输入文本文件名,并以该文件名作为原始文件名和输出文件名。该程序要使用 ctype.h 中的 toupper()函数,在写入到输出文件时把所有文本转换成大写。使用标准I/O和文本模式

#include <stdio.h> 
#include <stdlib.h>
#include <time.h>
#include <ctype.h>

//在网上看好像C语言处理文本只能复制出来再读进去

void mktxt(char* a)
{
    srand((unsigned long)time(0));
    FILE *fp;
    fp = fopen(a, "w");
    for (int j = 0; j < 100; ++j)
    {
        int n = rand() % 256;
        for (int i = 0; i < n; ++i)
        {
            putc(32 + rand() % 95, fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char** argv)
{
    mktxt(argv[1]);
    FILE *fp1, *fp2;
    if(argc != 2)
    {
        fprintf(stderr, "Usage:%s [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        if((fp1 = fopen(argv[1], "r")) == NULL)
        {
            fprintf(stderr, "Open %s failed\n", argv[1]);
            exit(EXIT_FAILURE);
        }
        if((fp2 = fopen("midfile.txt", "w")) == NULL)
        {
            fprintf(stderr, "FAIL\n");
            exit(EXIT_FAILURE);
        }
    }
    char ch;
    while((ch = getc(fp1)) != EOF)
    {
        if(islower(ch))
        {
            ch = toupper(ch);
        }
        putc(ch, fp2);
    }
    fclose(fp1);
    fclose(fp2);
    if((fp1 = fopen(argv[1], "w")) == NULL)
    {
        fprintf(stderr, "FAIL\n");
        exit(EXIT_FAILURE);
    }
    if((fp2 = fopen("midfile.txt", "r")) == NULL)
    {
        fprintf(stderr, "FAIL\n");
        exit(EXIT_FAILURE);
    }
    while((ch = getc(fp2)) != EOF)
    {
        putc(ch, fp1);
    }
    remove("midfile.txt");
    return 0;
}

4.编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。使用argc控制循环

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

void mktxt(char* a)
{
    srand((unsigned long)clock());
    //用time()有个问题,因为只设置一次种子,每过两个文件文件内容就会重复,所以用clock
    FILE *fp;
    fp = fopen(a, "w");
    for (int j = 0; j < 10; ++j)
    {
        int n = rand() % 256;
        for (int i = 0; i < n; ++i)
        {
            putc(32 + rand() % 95, fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char** argv)
{
    FILE* fp;
    char ch;
    for (int i = 1; i < argc; ++i)
    {
        mktxt(argv[i]);
        if((fp = fopen(argv[i], "r")) != NULL)
        {
            while((ch = getc(fp)) != EOF)
            {
                putchar(ch);
            }
            putchar('\n');
            putchar('\n');
            fclose(fp);
        }
        else
        {
            fprintf(stderr, "Open %s failed\n", argv[i]);
            exit(EXIT_FAILURE);
        }
    }
    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); 
char * s_gets(char * st, int n); 
int main(int argc, char** argv)
{
    FILE *fa, *fs;  // fa 指向目标文件,fs 指向源文件
    int files = 0;         // 附加的文件数量
    char file_app[SLEN];  // 目标文件名
    char file_src[SLEN];  // 源文件名
    int ch;
    if(argc < 2)
    {
        fprintf(stderr, "Usage:%s [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if ((fa = fopen(argv[1], "a+")) == NULL)
    {
        fprintf(stderr, "Can't open %s\n", argv[2]);
        exit(EXIT_FAILURE);
    }
    if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
    {
        fputs("Can't create output buffer\n", stderr);
        exit(EXIT_FAILURE);
    }
    puts("Enter name of first source file (empty line to quit):");
    while (s_gets(file_src, SLEN) && file_src[0] != '\0')
    {
        if (strcmp(argv[1], file_src) == 0)
            fputs("Can't append file to itself\n", stderr);
        else if ((fs = fopen(file_src, "r")) == NULL)
            fprintf(stderr, "Can't open %s\n", file_src);
        else
        {
            if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
            {
                fputs("Can't create input buffer\n", stderr);
                continue;
            }
            append(fs, fa);
            if (ferror(fs) != 0)
                fprintf(stderr, "Error in reading file %s.\n", file_src);
            if (ferror(fa) != 0)
                fprintf(stderr, "Error in writing file %s.\n", file_app);
            fclose(fs);
            files++;
            printf("File %s appended.\n", file_src);
            puts("Next file (empty line to quit):");
        }
    }
    printf("Done appending.%d files appended.\n", files);
    rewind(fa); printf("%s contents:\n", file_app);
    while ((ch = getc(fa)) != EOF)
        putchar(ch);
    puts("Done displaying.");
    fclose(fa);
    return 0;
}

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);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find; ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 查找换行符
        if (find)                  // 如果地址不是NULL,
            *find = '\0';       // 在此处放置一个空字符
        else while (getchar() != '\n')
            continue;
    }
    return ret_val;
}

6.使用命令行参数的程序依赖于用户的内存如何正确地使用它们。重写程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需信息。

#include <stdio.h> 
#include <stdlib.h>  // 提供 exit()的原型
#include <string.h>  // 提供 strcpy()、strcat()的原型
#define LEN 40
int main(int argc, char *argv [])
{
    FILE *in, *out;  // 声明两个指向 FILE 的指针
    int ch;
    char name[LEN];  // 储存输出文件名
    int count = 0; // 检查命令行参数
    char a[LEN];
    printf("Input the file name:");
    gets(a);
    if ((in = fopen(a, "r")) == NULL)
    {
        fprintf(stderr, "I couldn't open the file \"%s\"\n", argv[1]);
        exit(EXIT_FAILURE);
    } // 设置输出
    strncpy(name, a, LEN - 5);  // 拷贝文件名
    name[LEN - 5] = '\0';
    strcat(name, ".red");        // 在文件名后添加.red
    if ((out = fopen(name, "w")) == NULL)
    {          // 以写模式打开文件
        fprintf(stderr, "Can't create output file.\n");
        exit(3);
    } // 拷贝数据
    while ((ch = getc(in)) != EOF)
        if (count++ % 3 == 0)
            putc(ch, out);// 打印3个字符中的第1个字符
    // 收尾工作
    if (fclose(in) != 0 || fclose(out) != 0)
        fprintf(stderr, "Error in closing files\n");
    return 0;
}

7.编写一个程序打开两个文件。可以使用命令行参数或提示用户输入文件名。

a.该程序以这样的顺序打印:打印第1个文件的第1行,第2个文件的第1行,第1个文件的第2行,第2个文件的第2行,以此类推,打印到行数较多文件的最后一行。

b.修改该程序,把行号相同的行打印成一行。

#include <stdio.h> 
#include <stdlib.h>  
#include <string.h>  

void afunc(FILE* a, FILE* b)
{
    char aa[256], bb[256];
    while(fgets(aa, 256, a) && fgets(bb, 256, b))
    {
        puts(aa);
        puts(bb);
    }
    while(fgets(aa, 256, a))
    {
        puts(aa);
    }
    while(fgets(bb, 256, b))
    {
        puts(bb);
    }
}

void bfunc(FILE* a, FILE* b)
{
    char aa[256], bb[256];
    while(fgets(aa, 256, a) && fgets(bb, 256, b))
    {
        printf("%s%s\n", aa, bb);
    }
    while(fgets(aa, 256, a))
    {
        puts(aa);
    }
    while(fgets(bb, 256, b))
    {
        puts(bb);
    }
}

int main(int argc, char *argv [])
{
    if(argc != 3)
    {
        fprintf(stderr, "Usage:%s [filename] [filename]", argv[0]);
        exit(EXIT_FAILURE);
    }
    FILE *fp1, *fp2;
    if((fp1 = fopen(argv[1], "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    if((fp2 = fopen(argv[2], "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", argv[2]);
        exit(EXIT_FAILURE);
    }
    bfunc(fp1, fp2);//或afunc()
    return 0;
}

8.编写一个程序,以一个字符和任意文件名作为命令行参数。如果字符后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件并报告每个文件中该字符出现的次数。文件名和字符本身也要一同报告。程序应包含错误检查,以确定参数数量是否正确和是否能打开文件。如果无法打开文件,程序应报告这一情况,然后继续处理下一个文件。

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>

void mktxt(char* a)
{
    srand((unsigned long)clock());
    //用time()有个问题,因为只设置一次种子,每过两个文件文件内容就会重复,所以用clock
    FILE *fp;
    fp = fopen(a, "w");
    for (int j = 0; j < 10; ++j)
    {
        int n = rand() % 256;
        for (int i = 0; i < n; ++i)
        {
            putc(32 + rand() % 95, fp);
        }
        putc('\n', fp);
    }
    fclose(fp);
}

int main(int argc, char *argv [])
{
    FILE *fp;
    int num;
    char ch;
    if(argc < 2)
    {
        fprintf(stderr, "Usage:%s [character] / [character] [filename] ... \n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if(strlen(argv[1]) != 1)
    {
        fprintf(stderr, "The second parameter should be a character\n");
        exit(EXIT_FAILURE);
    }
    if(argc == 2)
    {
        num = 0;
        char a[256];
        for (int j = 0; j < 256 ; ++j) {
            a[j] = getchar();
        }
        for (int i = 0; i < 256; ++i)
        {
            if(a[i] == argv[1][0])
            {
                num++;
            }
        }
        printf("There are %d %c in the input\n", num, argv[1][0]);
    }
    else
    {
        for (int i = 2; i < argc; ++i)
        {
            num = 0;
            mktxt(argv[i]);
            if((fp = fopen(argv[i], "r")) == NULL)
            {
                fprintf(stderr, "Open %s failed\n", argv[i]);
                continue;
            }
            while((ch = getc(fp)) != EOF)
            {
                if(ch == argv[1][0])
                {
                    num++;
                }
            }
            printf("There are %3d %c in %5s\n", num, argv[1][0], argv[i]);
        }
    }
    return 0;
}

9.修改程序清单 13.3 中的程序,从 1 开始,根据加入列表的顺序为每个单词编号。当程序下次运行时,确保新的单词编号接着上次的编号开始。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <ctype.h>
#define MAX 41 

int main(void) 
{
    int n = 0;
    FILE *fp; 
    char words[MAX];
    long count = -1;
    char ch;
    if ((fp = fopen("wordy", "a+")) == NULL)
    { 
        fprintf(stdout, "Can't open \"wordy\" file.\n"); 
        exit(EXIT_FAILURE); 
    }
    if(getc(fp))
    {
        do
        {
            fseek(fp, count--, SEEK_END);
            ch = getc(fp);
        }while(!isdigit(ch));
        fseek(fp, -4, SEEK_CUR);
        for (int i = 0; i < 4; ++i)
        {
            n *= 10;
            if(isdigit(ch = getc(fp)))
            {
                n += ch - '0';
            }
        }
        printf("%d\n", n);
    }
    fseek(fp, 0, SEEK_END);//把指针重新定位到结尾,不然无法向文件输出
    puts("Enter words to add to the file; press the #"); 
    puts("key at the beginning of a line to terminate."); 
    while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#')) 
        fprintf(fp, "%04d%s\n", ++n, words);
    puts("File contents:");
    rewind(fp);    /* 返回到文件开始处 */
    while (fscanf(fp, "%s", words) == 1)
        puts(words);
    puts("Done!");
    if (fclose(fp) != 0)
        fprintf(stderr, "Error closing file\n");
    return 0;
}

10.编写一个程序打开一个文本文件,通过交互方式获得文件名。通过一个循环,提示用户输入一个文件位置。然后该程序打印从该位置开始到下一个换行符之前的内容。用户输入负数或非数值字符可以结束输入循环。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <ctype.h>
#define MAX 41 

int main(void) 
{
    FILE *fp;
    char a[40];
    long n;
    char ch;
    printf("Input a filename:");
    scanf("%s", a);
    if((fp = fopen(a, "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", a);
        exit(EXIT_FAILURE);
    }
    printf("Input the index(q or -1 to quit):");
    while(scanf("%ld", &n) == 1 && n > 0)
    {
        fseek(fp, n, SEEK_SET);
        while((ch = getc(fp)) != EOF && ch != '\n')
        {
            putchar(ch);
        }
        putchar('\n');
        printf("Input the index(q or -1 to quit):");
    }
    return 0;
}

11.编写一个程序,接受两个命令行参数。第1个参数是一个字符串,第2个参数是一个文件名。然后该程序查找该文件,打印文件中包含该字符串的所有行。因为该任务是面向行而不是面向字符的,所以要使用fgets()而不是getc()。使用标准C库函数strstr()(11.5.7节简要介绍过)在每一行中查找指定字符串。假设文件中的所有行都不超过255个字符。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <ctype.h>
#define MAX 41 

int main(void) 
{
    FILE *fp;
    char a[40];
    char c[256];
    char s[256];
    printf("Input a filename:");
    scanf("%s", a);
    if((fp = fopen(a, "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", a);
        exit(EXIT_FAILURE);
    }
    printf("Input a String:");
    scanf("%s", c);
    while(fgets(s, 256, fp))
    {
        if(strstr(s, c))
        {
            puts(s);
        }
    }
    return 0;
}

12.创建一个文本文件,内含20行,每行30个整数。这些整数都在0~9之间,用空格分开。该文件是用数字表示一张图片,0~9表示逐渐增加的灰度。编写一个程序,把文件中的内容读入一个20×30的int数组中。一种把这些数字转换为图片的粗略方法是:该程序使用数组中的值初始化一个20×31的字符数组,用值0 对应空格字符,1 对应点字符,以此类推。数字越大表示字符所占的空间越大。例如,用#表示9。每行的最后一个字符(第31个)是空字符,这样该数组包含了20个字符串。最后,程序显示最终的图片(即,打印所有的字符串),并将结果储存在文本文件中。例如,下面是开始的数据:

0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 2 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 5 2 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 1 9 8 5 4 5 2 0 0 0 0 0 0 0 0 0

0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 0 4 5 2 0 0 0 0 0 0 0 0

0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 4 5 2 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 1 8 5 0 0 0 4 5 2 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 4 5 2 0 0 0 0 0

5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5

8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8

9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9 9 9 9 9

8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8

5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0

0 0 0 0 2 2 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0

0 0 0 0 3 3 0 0 0 0 0 0 5 8 9 9 8 5 0 5 6 1 1 1 1 6 5 0 0 0

0 0 0 0 4 4 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0

0 0 0 0 5 5 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0

根据以上描述选择特定的输出字符,最终输出如下:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <ctype.h>


int main(void) 
{
    FILE *fp;
    int n[20][30];
    char ch[20][31];
    char s[40];
    int in = 0;
    char l[] = {' ','.','\'','\"','^','*','%','$','@','#'};
    if((fp = fopen("123.txt", "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", "123.txt");
        exit(EXIT_FAILURE);
    }
    while (fgets(s, 40, fp))
    {
        for (int i = 0; i < 30; ++i) {
            n[in][i] = s[i] - '0';
        }
        in++;
    }
    for (int j = 0; j < 20; ++j) {
        for (int i = 0; i < 30; ++i) {
            ch[j][i] = l[n[j][i]];
        }
        ch[j][30] = '\0';
    }
    for (int k = 0; k < 20; ++k)
    {
        puts(ch[k]);
    }
    return 0;
}

13.用变长数组(VLA)代替标准数组,完成编程练习12。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


int main(void)
{
    FILE *fp;
    int a, b;
    printf("Input a and b:");
    scanf("%d %d", &a, &b);
    int n[a][b];
    char ch[a][b + 1];
    char s[b + 2];
    int in = 0;
    char l[] = {' ','.','\'','\"','^','*','%','$','@','#'};
    if((fp = fopen("123.txt", "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", "123.txt");
        exit(EXIT_FAILURE);
    }
    while (fgets(s, 40, fp) && in < a)
    {
        for (int i = 0; i < b; ++i) {
            n[in][i] = s[i] - '0';
        }
        in++;
    }
    for (int j = 0; j < a; ++j) {
        for (int i = 0; i < b; ++i) {
            ch[j][i] = l[n[j][i]];
        }
        ch[j][b] = '\0';
    }
    for (int k = 0; k < a; ++k)
    {
        puts(ch[k]);
    }
    return 0;
}

14.数字图像,尤其是从宇宙飞船发回的数字图像,可能会包含一些失真。为编程练习12添加消除失真的函数。该函数把每个值与它上下左右相邻的值作比较,如果该值与其周围相邻值的差都大于1,则用所有相邻值的平均值(四舍五入为整数)代替该值。注意,与边界上的点相邻的点少于4个,所以做特殊处理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
    FILE *fp;
    int n[20][30];
    char ch[20][31];
    char s[40];
    int in = 0;
    char l[] = {' ','.','\'','\"','^','*','%','$','@','#'};
    if((fp = fopen("123.txt", "r")) == NULL)
    {
        fprintf(stderr, "Open %s failed\n", "123.txt");
        exit(EXIT_FAILURE);
    }
    while (fgets(s, 40, fp))
    {
        for (int i = 0; i < 30; ++i) {
            n[in][i] = s[i] - '0';
        }
        in++;
    }
    if(abs(n[0][0] - n[0][1]) > 1 && abs(n[0][0] - n[1][0]) > 0)
    {
        n[0][0] = (n[0][1] + n[1][0] + 1) / 2;
    }
    if(abs(n[0][29] - n[0][28]) > 1 && abs(n[0][29] - n[1][29]) > 0)
    {
        n[0][29] = (n[0][28] + n[1][29] + 1) / 2;
    }
    if(abs(n[20][0] - n[20][1]) > 1 && abs(n[20][0] - n[19][0]) > 0)
    {
        n[20][0] = (n[20][1] + n[19][0] + 1) / 2;
    }
    if(abs(n[20][29] - n[20][28]) > 1 && abs(n[20][29] - n[19][29]) > 0)
    {
        n[20][29] = (n[20][28] + n[19][29] + 1) / 2;
    }

    for (int i = 1; i < 29; ++i) {
        if(abs(n[0][i] - n[0][i - 1]) > 1 && abs(n[0][i] - n[0][i + 1]) > 1 && abs(n[0][i] - n[1][i]) > 1)
        {
            n[0][i] = (n[0][i - 1] + n[0][i + 1] + n[1][i] + 1) / 3;
        }
    }

    for (int i = 1; i < 29; ++i) {
        if(abs(n[19][i] - n[19][i - 1]) > 1 && abs(n[19][i] - n[19][i + 1]) > 1 && abs(n[19][i] - n[18][i]) > 1)
        {
            n[19][i] = (n[19][i - 1] + n[19][i + 1] + n[18][i] + 1) / 3;
        }
    }
    for (int i = 1; i < 20; ++i) {
        if(abs(n[i][0] - n[i + 1][0]) > 1 && abs(n[i][0] - n[i - 1][0]) > 1 && abs(n[i][0] - n[i][1]) > 1)
        {
            n[i][0] = (n[i + 1][0] + n[i - 1][0] + n[i][1] + 1) / 3;
        }
    }
    for (int i = 1; i < 20; ++i) {
        if(abs(n[i][29] - n[i + 1][29]) > 1 && abs(n[i][29] - n[i - 1][29]) > 1 && abs(n[i][29] - n[i][28]) > 1)
        {
            n[i][29] = (n[i + 1][29] + n[i - 1][29] + n[i][28] + 1) / 3;
        }
    }

    for (int i = 1; i < 19; ++i) {
        for (int j = 1; j < 29; ++j) {
            if(abs(n[i][j] - n[i + 1][j]) > 1 && abs(n[i][j] - n[i - 1][j]) > 1 && abs(n[i][j] - n[i][j - 1]) > 1 && abs(n[i][j] - n[i][j + 1]) > 1)
            {
                n[i][j] = (n[i + 1][j] + n[i - 1][j] + n[i][j - 1] + n[i][j + 1] + 2) / 4;
            }
        }
    }
    for (int j = 0; j < 20; ++j) {
        for (int i = 0; i < 30; ++i) {
            ch[j][i] = l[n[j][i]];
        }
        ch[j][30] = '\0';
    }
    for (int k = 0; k < 20; ++k)
    {
        puts(ch[k]);
    }
    return 0;
}

 

展开阅读全文

没有更多推荐了,返回首页