第十一章 字符串和字符串函数  

编程练习

  1. 设计并测试一个函数,可以输入读取n个字符(包括空格、制表符、换行符),把结果存储在一个数组中,这个数组的地址通过参数来传递。

#include <stdio.h>
#define LEN 80

char * getch(char a[], int n);

int main(void)
{
	char a[LEN];

	if (getch(a, 4))
		puts(a);
	else
		printf("没有读取\n");

	return 0;
}
char * getch(char a[], int n)
{
	int i;
	for (i = 0; i < n; i++)
	{
		a[i] = getchar();
		if (EOF == a[i])
		{
			a[i] = '\0';
			break;
		}		
	}
	a[i] = '\0';
	if (!a[0])
		return NULL;
	
	return a;
}


2.修改编程练习1中的函数,使得可以在N个字符后,或第一个空格、制表、换行符后停止读取输入,由上述情况中最先满足的那个终止读取(不能用scanf()函数)。

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

#define LEN 80

char * getch(char a[], int n);

int main(void)
{
	char a[LEN];

	if (getch(a, 6))
		puts(a);
	else
		printf("没有读取\n");

	return 0;
}
char* getch(char a[], int n)
{
	int i;
	for (i = 0; i < n; i++)
	{
		a[i] = getchar();
		if (EOF == a[i] || isspace(a[i]))
		{
			a[i] = '\0';
			break;
		}
	}
	a[i] = '\0';
	if ('\0' == a[0])
		return NULL;

	return a;
}


3.设计并测试一个函数,其功能是读取输入行里的第一个单词到数组,并丟掉该行中其它的字符。

一个单词的定义是一个字符串,其中不含空格、制表符和换行符

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>	//不支持bool,可以返回int类型 0 或 1 

bool getword(char * );

int main(void)
{
	char array[20];
	if (getword(array))
		puts(array);
	else
		puts("没有读取到单词。");

	return 0;
}

bool getword(char * array)
{
	char ch;
	int a = 0;
	char * p = array;
	while ((ch = getchar()) != '\n')
	{
		//怱略一行开始处的空格。
		if (a == 0 && isspace(ch))
			continue;
		else
			a = 1;

		if (!isspace(ch))
			*array++ = ch;
		else
			break;
	}
	*array = '\0';
	if (*p == '\0')
		return false;
	return true;
}


4.设计并测试一个函数,其功能是搜索由第一个参数指定的字符串,在其中查找由函数的第二个参数指定的字符的第一次出现的位置。如果找到,返回指向这个字符的指针;如果没找返回空字符(这种方式和strchr()函数的功能一样)。在一个使用循环语句为这个函数提供输入的完整程序中进行测试。

#include <stdio.h>
#define LEN 80

char * search(char *str, char ch);

int main(void)
{
	char ch;
	char target[LEN];
	char * pstr;
	puts("请输入字符串(在一行的开始处输入回车结束):");
	while (gets(target) && target[0] != '\0')
	{
		puts("请输入字符:");
		ch = getchar();
		while (getchar() != '\n')
			;
		pstr = search(target, ch);
		if (pstr == NULL)
			printf("字符%c不在字符串中\n", ch);
		else
			printf("找到字符%c,它的地址为%p\n", ch, pstr);
		
		puts("请输入下一个字符串(在一行的开始处输入回车结束:");
	}

	return 0;
}

char * search(char *str, char ch)
{
	while (*str != '\0')
	{
		if (*str++ == ch)
			return str;
	}

	return NULL;
}


5.编写一个函数is_within(),它接受两个参数,一个是字符,另一个是字符串指针。其功能是如果字符在字符串中,就返回一个非0值(真);如果字符不在字符串中,就返回0值(假)。在一个使用循环语句为这个函数提供输入的完整程序中进行测试。

#include <stdio.h>
#define LEN 80

int search(char *str, char ch);

int main(void)
{
	char ch;
	char target[LEN];

	puts("请输入源字符串(在一行的开始处输入回车结束):");
	while (gets(target) && target[0] != '\0')
	{
		puts("请输入目标字符:");
		ch = getchar();
		while (getchar() != '\n')
			;
		if (search(target, ch))
			printf("字符%c在字符串中\n", ch);
		else
			printf("字符%c不在字符串中\n", ch);
		
		puts("请输入下一个源字符串(在一行的开始处输入回车结束:");
	}

	return 0;
}

int search(char *str, char ch)
{
	while (*str != '\0' &&  *str != ch)
		str++;
	return *str;
}


6.strncpy(s1, s2, n)函数从s2复制n个字符给s1,并在必要时截断s2或为其填充额外的空字符。如果s2的长度等于或大于n,目标字符串没有标志结束的空字符。函数返回s1。自己编写这个函数,并在一个使用循环语句为这个函数提供输入的完整程序中进行测试。

#include <stdio.h>
#define LEN 80

char * strncpy1(char *, char *, int);

int main(void)
{
	char str1[LEN], str2[LEN];
	int n;

	printf("请输入要复制的字符串:");
	while (gets(str2))
	{
		printf("请输入要复制的字符个数:");
		scanf("%d", &n);
		strncpy1(str1, str2, n);
		printf("复制的字符串:");
		puts(str1);
		printf("请输入要复制的字符串:");
		while (getchar() != '\n')
			;
	}

	return 0;
}

#include <string.h>
char * strncpy1(char *str1, char *str2, int n)
{
	int l = strlen(str2);
	int i;
	
	if (l > n)
		n = l;
	for (i = 0; i < n; i++)
		str1[i] = str2[i];
	str1[i] = '\0';

	return str1;
}


7.编写一个函数string_in(),它接受两个字符串指针参数。如果第二个字符串被包含在第一个字符串中,函数就返回被包含的字符串开始的地址。例如,string_in("hats", "at")返回hats中a的地址,否则,函数返回空指针。在一个使用循环语句为这个函数提供输入的完整程序中进行测试。

#include <stdio.h>
#define LEN 80

char * string_in(char *, char *);

int main(void)
{
	char str1[LEN], str2[LEN];
	char * pstr;

	printf("请输入被查找的字符串:");
	while (gets(str1))
	{
		printf("请输入要查找的字符串:");
		gets(str2);
		pstr = string_in(str1, str2);
		if (pstr != NULL)
		{
			printf("字符串已找到在内存中的地址为%p\n", pstr);
		}
		else	
		{
			printf("未找到字符串。\n");
		}
		printf("请输入被查找的字符串:");
	}
	printf("结束!");

	return 0;
}

#include <string.h>
char * string_in(char * str1, char * str2)
{
	int len2 = strlen(str2);
	int match;
	int len1 = strlen(str1);
	int i = len1 - len2 + 1;
	if (i > 0)
	{
		while ((match = strncmp(str1, str2, len2)) && i-- )
		{
			str1++;
		}
	}
	if(match)
		return NULL;
	else
		return str1;	
}


8.编写一个函数,其功能是使输入字符串反序。在一个使用循环语句为这个函数提供输入的完整程序中进行测试。

#include <stdio.h>
#define LEN 80

char * antitone(char * pstr);

int main(void)
{
	char str[LEN];

	printf("请输入要反序的字符串:");
	while (gets(str))
	{	
		printf("反转后的字符串:");
		puts(antitone(str));
		printf("请输入要反序的字符串:");
	}
	printf("结束!\n");

	return 0;
}

#include <string.h>
char * antitone(char * pstr)
{
	int len = strlen(pstr) - 1;
	int i;
	char temp;

	for (i=0,  i<len - i; i++)
	{
		temp = pstr[len - i];
		pstr[len - i] = pstr[i];
		pstr[i] = temp;
	}

	return pstr;
}


9.编写一个函数,其参数为一个字符串,函数删除字符串中的空格。在一个可以循环读取的程序中进

行测试,直到用户输入空行。对于任何输入字符串,函数都应该适用并可以显示结果

#include <stdio.h>
#define LEN 80

char * del_space(char * pstr);

int main(void)
{
	char str[LEN];

	printf("请输入要删空格的字符串:");
	while (gets(str) && str[0] != '\0')
	{	
		printf("删掉空格后的字符串:");
		puts(del_space(str));
		printf("请输入要删空格的字符串:");
	}
	printf("结束!\n");

	return 0;
}

#include <ctype.h>
char * del_space(char * pstr)
{
	char * temp = pstr;
	char * temp1;

	while (*temp)
	{
		if (isspace(*temp))
		{
			temp1 = temp;
			do
			{
				*temp1 = *(temp1 + 1);
			}	
			while(*++temp1)	;
		}
		else 
		{
			temp++;
		}
	}
	
	return pstr;
}


10,编写一个程序,读取输入,直到读入了10个字符串或遇到EOF,由二者中最先被满足的那个终止读取过程。这个程序可以为用户提供一个有5个选项的菜单:输出字符串列表、按ASCII顺序输出字符串、长度递增顺序输出字符串、按字符串中第一个单词的长度输出字符串、退出。菜单可以循环直到用户输入退出请求。当然,程序要能真正完成菜单中的各项功能。

/*
*	10,编写一个程序,读取输入,直到读入了10个字符串或遇到EOF,由二者中最先被满足的那个终止读取过程。
*	这个程序可以为用户提供一个有5个选项的菜单:输出字符串列表、按ASCII顺序输出字符串、
*	长度递增顺序输出字符串、
*	按字符串中第一个单词的长度输出字符串、退出。菜单可以循环直到用户输入退出请求。
*	当然,程序要能真正完成菜单中的各项功能。
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define ROWS 10
#define COLS 81

void menu(void);			//显示菜单
char get_char(void);		//读取用户输入的正确选项
int in_str(char str[][COLS]);					//输入字符串,返回字符串的个数
void show_str(const char * str[], int n);		//输出参数传递过来字符串
void show_ascii(char * str[], int n);			//按ASCII顺序输出
void show_strlen(char * str[], int n);			//按字符串长度输出
void show_wordlen(char  * str[], int n);		//按字符串第一个单词的长度输出
int wordlen(char * str);	//求字符串第一个单词找度

int main(void)
{
	char str[ROWS][COLS];		//保存输入的原始字符串,值不做改变,方便用户循环选择时,任何时候都能输出原始值。
	char str_copy[ROWS][COLS];	//原始数据的备份可改变
	char * pstr[ROWS];			//声明指针做为函数参数传送
	char * pcopy[ROWS];
	char ch;					//菜单选项
	int i;			//字符串的个数
	int j;			//用于数组赋值

	i = in_str(str);
	for (j = 0; j < i; j++)
	{	//数组赋值
		pstr[j] = str[j];
		pcopy[j] = str_copy[j];
		strcpy(pcopy[j],pstr[j]);
	}

	menu();
	while ((ch = get_char()) != 'q')
	{
		switch (ch)
		{
		case 'a': show_str(pstr, i);
			break;
		case 'b': show_ascii(pcopy, i);
			break;
		case 'c': show_strlen(pcopy, i);
			break;
		case 'd': show_wordlen(pcopy, i);
			break;
		default: printf("Error!");
		}
		menu();
	}

	return 0;
}
int wordlen(char * str)
{	//首单词字母个数
	int count = 0;
	char ch;

	while (ch = *str++)
	{
		if (isspace(ch) && count == 0) continue;
		if (isspace(ch) && count != 0) break;
		if (!isspace(ch)) count++;
	}

	return count;
}
void show_wordlen(char *str[], int n)
{	//按照首单词字母长度排序后输出
	char * temp;
	int i, j;

	for (i = 0; i < n - 1; i++)
	for (j = i; j < n; j++)
	if (wordlen(str[i]) > wordlen(str[j]))
	{
		temp = str[i];
		str[i] = str[j];
		str[j] = temp;
	}
	show_str(str, n);
}
void show_strlen(char *str[], int n)
{	//按照字符串长度排序后输出
	char * temp;
	int i, j;
	for (i = 0; i < n - 1; i++)
	for (j = i; j < n; j++)
	if (strlen(str[i]) > strlen(str[j]))
	{
		temp = str[i];
		str[i] = str[j];
		str[j] = temp;
	}
	show_str(str, n);
}
void show_ascii(char *str[], int n)
{	//按照字符ASCII编码顺序排序后输出
	char * temp;
	int i, j;
	for (i = 0; i < n - 1; i++)
	for (j = i; j < n; j++)
	if (strcmp(str[i], str[j]) > 0)
	{
		temp = str[i];
		str[i] = str[j];
		str[j] = temp;
	}
	show_str(str, n);
}
void show_str(const char *str[], int n)
{	//输出字符串数组
	int i = 0;
	while (i < n)
		puts(str[i++]);
}
int in_str(char(*str)[COLS])
{	//输入字符串
	int i = 0;

	do
	{
		printf("请输入字符串%d(输入EOF结束): ", i + 1);
	} while (gets(str[i]) && ++i < ROWS);
	puts("输入结束。");

	return i;
}
void menu(void)
{	//显示菜单
	puts("请选择字符串输出方式:");
	puts("  a)输出字符串列表");
	puts("  b)按ASCII顺序输出字符串");
	puts("  c)长度递增顺序输出字符串");
	puts("  d)按字符串中第一个单词的长度输出字符串");
	puts("  q)退出");

}
char get_char(void)
{	//确认输入符合要求
	char ch;
	while (ch = getchar())
	{
		while (getchar() != '\n')
			;
		if (!strchr("abcdq", ch))
			puts("请输入正确的选项,(如:a b c d q):");
		else
			break;
	}
	return ch;
}



11.编写一个程序。功能是读取输入,直到遇到EOF,并报告单词数、大写字母数、小写字母数、标点符号数和数字字符数。使用ctype.h系列函数。

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
int main()
{
	int count_word = 0;
	int count_lower = 0;
	int count_upper = 0;
	int count_digit = 0;
	int count_punct = 0;
	bool sign = false;
	char ch;
	while ((ch = getchar()) != EOF)
	{
		if (islower(ch)) count_lower++;
		else if (isupper(ch)) count_upper++;
		else if (isdigit(ch)) count_digit++;
		else if (ispunct(ch)) count_punct++;
		if (!isspace(ch) && !sign)
		{	//单词的界定为单个非空格字符和不包含空格字符的字符串,
			count_word++;
			sign = true;
		}
		else if (isspace(ch) && sign)
		{
			sign = false;
		}
	}
	puts("统计结果:");
	printf("单词数:%d\n", count_word);
	printf("小写字母数:%d\n", count_lower);
	printf("大写字母数:%d\n", count_upper);
	printf("标点符号数:%d\n", count_punct);
	printf("数字个数:%d\n", count_digit);

	return 0;
}


12.编写一个程序,按照相反的单词顺序显示命令行参数。即,如果命令行参数是see you later,程序的显示应该为later you see。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 80
int main()
{
	char str[LEN] = "see you later";
	char temp;
	int len = strlen(str) - 1;
	int i, j, wordlen;
	puts(str);
	for (i = 0; i < len - i; i++)
	{	
		temp = str[i];
		str[i] = str[len - i];
		str[len - i] = temp;
	}
	puts(str);

	wordlen = 0;
	i = 0;
	do 
	{
		if (!isspace(str[i]) && str[i] != '\0')
		//isspace()不能识别'\0'条件中要加上str[i] != '\0'
			wordlen++;	
		else
		{
			if (wordlen > 0)
			{
				for (j = 0; j < wordlen - j; j++)
				{
					temp = str[i -1 - j];	//i-1 位置在单词的最后一个了符
					str[i - 1 -j] = str[i - wordlen+j];	//[i - wordlen]位置在单词的第一个字符。
					str[i - wordlen+j] = temp;
				}
				wordlen = 0;
			}

		}
	} while (str[i++] != '\0');
		
	puts(str);

	return 0;
}


13.编写一个计算乘幂的基于命令行和程序。第一个命令行参数为double类型数,作为幂的底数;第二个参数为整数,作为幂的指数。

#include <stdio.h>
#include <stdlib.h>	//提供atof() 
#include <math.h>	//提供pow()

int main(int argc, char *argv[])
{
	double num, exp;

	if (argc != 3)
		printf("参数不符\n");
	else
	{
		num = atof(argv[1]);
		exp = atof(argv[2]);
		printf("%.2lf的%.0lf次幂为:%lf", num, exp, pow(num, exp));
	}
	return 0;
}


14.使用字符分类函数实现atoi()函数。

#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
	char str[20];
	while(gets(str))
		printf("%d\n", my_atoi(str));
	
	return 0;
}

int my_atoi(char * pstr)
{
	int sign = 1;
	int num = 0;

	//字符串不能为空
	if (pstr == NULL)
		return 0;
	//去掉空格
	while (isspace(*pstr))
		pstr++;
	//判数首位是否有符号
	if (*pstr == '-')
		sign = -1;
	if (*pstr == '-' || *pstr == '+')
		pstr++;
	//转换直到遇到非数字时结束。
	while (isdigit(*pstr))
	{
		num = (num * 10) + (*pstr - '0');
		pstr++;
	}
	//增加符号位
	num *= sign;

	return num;
}


15.编写一个程序,其功能是读取输入,直到遇到文件结尾,并把文件显示出来,要求程序可以识别并执行下面的命令行参数:

-p    按原样显示输入

-u    把输入全部转换为大写

-l    把输入全部转换为小写

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

int main(int argc, char *argv[])
{
	int ch;

	if (argc > 2)
	{
		printf("%s不是正确的指令,请输入正确的指禽 -p -u 或-l\n", argv[1]);
		printf("例如: %s [-p | -u | -l]\n", argv[0]);
	}
	else if (argc == 2)
	{
		if (argv[1][0] != '-')
		{
			printf("%s不是正确的指令,请输入正确的指禽 -p -u 或-l\n", argv[1]);
			printf("例如: %s [-p | -u | -l]\n", argv[0]);
		}
		else
			switch (argv[1][1])
		{
			case 'p':while ((ch = getchar()) != EOF)
				putchar(ch);
				break;
			case 'u':while ((ch = getchar()) != EOF)
				putchar(toupper(ch));
				break;
			case 'l': while ((ch = getchar()) != EOF)
				putchar(tolower(ch));
				break;
			default: printf("%s不是正确的指令,请输入正确的指禽 -p -u 或-l\n", argv[1]);
				printf("例如: %s [-p | -u | -l]\n", argv[0]);
		}
	}

	return 0;
}