1、编写一个程序,从标准输入读取一些字符,并统计下列各类字符所占的百分比。
控制字符/空白字符/数字/小写字母/大写字母/标点符号/不可打印的字符
请使用在ctype.h头文件中定义的字符分类函数。
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int i;
int ch;
float sum = 0;
float count[7];
printf("please input some character: ");
while ((ch = getchar()) != '\n') {
sum++;
if (iscntrl(ch))
count[0]++;
if (isspace(ch))
count[1]++;
if (isdigit(ch))
count[2]++;
if (islower(ch))
count[3]++;
if (isupper(ch))
count[4]++;
if (ispunct(ch))
count[5]++;
if (!isprint(ch))
count[6]++;
}
printf("control character: %2.2f%%\n", (count[0] / sum) * 100);
printf("space character: %2.2f%%\n", (count[1] / sum) * 100);
printf("digit character: %2.2f%%\n", (count[2] / sum) * 100);
printf("lower character: %2.2f%%\n", (count[3] / sum) * 100);
printf("upper character: %2.2f%%\n", (count[4] / sum) * 100);
printf("punctuation character: %2.2f%%\n", (count[5] / sum) * 100);
printf("can't print character: %2.2f%%\n", (count[6] / sum) * 100);
return 0;
}
2、编写一个名叫my_strlen的函数,它类似与strlen函数,但它能够处理由于使用strn–函数而创建的未以NUL字节结尾的字符串。你需要向函数传递一个参数,它的值就是保存了需要进行长度测试的字符串的数组的长度。
#include <stdio.h>
size_t my_strlen(char *string, int n);
int main(void)
{
char string[10] = {'a', 'b', 'c'};
printf("my strlen: %d\n", (int)my_strlen(string, 10));
}
size_t my_strlen(char *string, int n)
{
register size_t count = 0;
while(*string++ != '\0' && n-- > 0)
count++;
return count;
}
3、编写一个名为my_strcpy的函数,它类似与strcpy函数,但它不会溢出目标数组。复制的结果必须是一个真正的字符串。
#include <stdio.h>
char *my_strcpy(char *dest, const char *src, int n);
void main(void)
{
char string1[5];
char string2[10] = "abcdefghi";
my_strcpy(string1, string2, sizeof(string1) - 1);
printf("my strcpy: %s\n", string1);
}
char *my_strcpy(char *dest, const char *src, int n)
{
char *des = dest;
while (*src && n > 0) {
*dest++ = *src++;
n--;
}
*dest = '\0';
return des;
}
4、编写一个名叫my_strcat的函数,它类似于strcat函数,但它不会溢出目标数组,它的结果必须是一个真正的字符串。
#include <stdio.h>
#include <string.h>
char *my_strcat(char *dest, const char *src, int size);
void main(void)
{
char string1[10] = "abc";
char string2[10] = "abcdefghi";
my_strcat(string1, string2, sizeof(string1));
printf("my strcat: %s\n", string1);
}
char *my_strcat(char *dest, const char *src, int size)
{
int len = size - 1 - strlen(dest);
if (len > 0) {
strncat(dest, src, len);
*(dest + size - 1) = '\0';
}
return dest;
}
5、编写函数
void my_strncat(char *dest, char *src, int dest_len);
它用于把src中的字符串连接到dest中原有的字符串末尾,但它保证不会溢出长度为dest_len的dest数组,和strncat函数不同,这个函数也考虑原先存在于dest数组的字符串长度,因此能够保证不会超越数组边界。
#include <stdio.h>
#include <string.h>
void my_strncat(char *dest, char *src, int dest_len);
void main(void)
{
char string1[10] = "abc";
char string2[10] = "abcdefghi";
my_strncat(string1, string2, sizeof(string1));
printf("my strncat: %s\n", string1);
}
void my_strncat(char *dest, char *src, int dest_len)
{
int len = dest_len - strlen(dest);
if (len > 0)
strncat(dest, src, len);
}
6、编写一个名叫my_strcpy_end的函数取代strcpy,它返回一个指向目标字符串末尾的指针(也就是说,指向NUL字节的指针),而不是返回一个指向目标字符串起始位置的指针。
#include <stdio.h>
#include <string.h>
char *my_strcpy_end(char *dest, char *src);
void main(void)
{
char string1[10];
char string2[5] = {'a', 'b', 'c', 'd', 'e'};
char *p;
p = my_strcpy_end(string1, string2);
printf("my strcpy end: %c\n", *p);
}
char *my_strcpy_end(char *dest, char *src)
{
while (*src != '\0')
*dest++ = *src++;
return --dest;
}
7、编写一个名叫my_strrchr的函数,它的原型如下:
char *my_strrchr(char const *str,int ch);
这个函数类似与strchr函数,只是它返回的是一个指向ch字符在str字符串中最后一次出现的位置的指针。
#include <stdio.h>
#include <string.h>
char *my_strrchr(char const *str, int ch);
void main(void)
{
char *p;
char string[10] = "abcabcabc";
p = my_strrchr(string, 'a');
printf("my strrchr: %s\n", p);
}
char *my_strrchr(char const *str, int ch)
{
char *last;
while (*str != '\0') {
if (*str == ch)
last = (char *)str;
str++;
}
return last;
}
8、编写一个名叫my_strnchr的函数,它的原型如下:
char *my_strnchr(char comst *str, int ch, int which);
这个函数类似于strchr,但它的第三个参数指定ch字符在str字符串中第几次出现,例如,第三个参数为1,这个函数的功能就和strchr一样,如果参数为2,这个函数就返回一个指向ch字符在str字符串中第二次出现的位置的指针。
#include <stdio.h>
#include <string.h>
char *my_strnchr(char const *str, int ch, int which);
void main(void)
{
char *p;
char string[10] = "abcabcabc";
p = my_strnchr(string, 'a', 2);
printf("my strnchr: %s\n", p);
}
char *my_strnchr(char const *str, int ch, int which)
{
int num = 0;
char *last;
while (*str != '\0') {
if (*str == ch) {
last = (char *)str;
if (++num == which)
return last;
}
str++;
}
return NULL;
}
9、编写一个函数,它的原型如下:
int count_chars(char const *str,char const *chars);
函数应该在第一个参数中进行查找,并返回匹配第二个参数所包含的字符的数量。
#include <stdio.h>
#include <string.h>
int count_chars(char const *str, char const *chars);
void main(void)
{
int count;
char string[10] = "abcabcabc";
char chars[10] = "ab";
count = count_chars(string, chars);
printf("count chars: %d\n", count);
}
int count_chars(char const *str, char const *chars)
{
int num = 0;
while (*str != '\0') {
if (!strncmp(str, chars, strlen(chars)))
num++;
str++;
}
return num;
}
10、编写函数:
int palindrome(char *string);
如果参数字符串是个回文,函数就返回真,否则返回假。回文就是指一个字符串从左向右和从右向左读是一样的。函数应该忽略所有的非字母字符,而且在进行字符比较时不用区分大小写。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int palindrome(char *string);
void main(void)
{
char *string = "Madam, I'm Adam";
if (palindrome(string))
printf("string: \"%s\" is palindrome\n", string);
else
printf("string: \"%s\" is not palindrome\n", string);
}
int palindrome(char *string)
{
char *p, *q;
p = string;
q = string;
while (*q++ != '\0')
;
q--;
while (p < q) {
if (!isalpha(*p)) {
p++;
continue;
}
if (!isalpha(*q)) {
q--;
continue;
}
if (tolower(*p) != tolower(*q))
return 0;
else {
p++;
q--;
}
}
return 1;
}
11、编写一个程序,对标准输入进行扫描,并对单词“the”出现的次数进行计数,进行比较时应该区分大小写,所以“The”和“THE”并不计算在内。你可以认为各单词由一个或多个空格字符分隔,而且输入行在长度上不超过100个字符,计数结果应该写到标准输出上。
#include <stdio.h>
#include <string.h>
int count_of_the(char const *string);
void main(void)
{
char count;
char str[100];
printf("pleast input charaters: ");
gets(str);
count = count_of_the(str);
printf("count of the: %d\n", count);
}
int count_of_the(char const *string)
{
int count = 0;
while (*string != '\0') {
if (!strncmp(string, "the ", 4))
count++;
string++;
}
return count;
}
12、有一种技巧可以对数据进行加密,并使用一个单词作为它的密钥。下面是它的工作原理:首先,选择一个单词作为密钥,如TRAILBLAZERS。如果单词中包含有重复的字母,只保留第一个,其余几个丢弃。现在,修改过那个单词列于字母表的下面,如下所示:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
T R A I L B Z E S
最后,底下那行字母表中剩余的字母填充完整:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
T R A I L B Z E S C D F G H J K M N O P Q U V W X Y
在对信息进行加密时,信息中的每个字母被固定于顶上那行,并用下面那行的对应字母一一取代原文的字母。因此,使用这个密钥,ATTACK AT DAWN(黎明时攻击)就会被加密为TPPTAD TP ITVH。
这个题材共有三个程序(包括下面两个练习),在第一个程序中,你需要编写函数:
int prepare_key(char *key);
它接受一个字符串参数,它的内容就是需要使用的密钥单词,函数根据上面描述的方法把它转换成一个包含编好码的字符数组。假定key参数是个字符数组,其长度至少可以容纳27个字符。函数必须把密钥中的所有字符要么转换成大写,要么转换成小写(随你选择),并从单词中去除重复的字母,然后再用字母表中剩余的字母按照你原先所选择的大小写形式填充到key数组中去,如果处理成功,函数将返回一个真值,如果key参数为空或包含任何非字母字符,函数将返回一个假值。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int prepare_key(char *key);
void main(void)
{
char key[27] = "TRAILBLAZERS";
if (prepare_key(key))
printf("prepare key success: %s\n", key);
else
printf("prepare key fail\n");
}
int prepare_key(char *key)
{
char ch;
char *head;
char use_key[27];
if (key == NULL)
return 0;
head = key;
while (*head != '\0') {
if (!isalpha(*head))
return 0;
*head = toupper(*head);
if (!strchr(use_key, *head))
strncat(use_key, head, 1);
head++;
}
for (ch = 'A'; ch <= 'Z'; ch++) {
if (!strchr(use_key, ch))
strncat(use_key, &ch, 1);
}
strcpy(key, use_key);
return 1;
}
13、编写函数:
void encrypt(char *data, char const *key);
它使用前题prepare_key函数所产生的密钥对data中的字符进行加密。data中的非字母字符不作修改,但字母字符则用密钥所提供的编过码的字符一一取代源字符。字符的大小写状态应该保留。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 100
void encrypt(char *data, char const *key);
void main(void)
{
char key[27] = "TRAILBZESCDFGHJKMNOPQUVWXY";
char message[MAX_LEN] = "Hello World!";
encrypt(message, key);
printf("encrypt success: %s\n", message);
}
void encrypt(char *data, char const *key)
{
while (*data != '\0') {
if (isupper(*data))
*data = key[*data - 'A'];
if (islower(*data))
*data = tolower(key[*data - 'a']);
data++;
}
}
14、这个问题的最后部分就是编写函数:
void decrypt(char *data, char const *key);
它接受一个加过密的字符串为参数,它的任务是重现原来的信息,除了它是用于解密之外,它的工作原理应该与encrypt相同。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 100
void decrypt(char *data, char const *key);
void main(void)
{
char key[27] = "TRAILBZESCDFGHJKMNOPQUVWXY";
char message[MAX_LEN] = "Elffj Vjnfi!";
decrypt(message, key);
printf("decrypt success: %s\n", message);
}
void decrypt(char *data, char const *key)
{
int offset;
while (*data != '\0') {
if (isupper(*data)) {
offset = strchr(key, *data) - key;
*data = 'A' + offset;
}
if (islower(*data)) {
offset = strchr(key, toupper(*data)) - key;
*data = 'a' + offset;
}
data++;
}
}
15、标准I/O库并没有提供一种机制,打印大整数时逗号进行分隔。在这个练习中,你需要编写一个程序,为美元数额的打印提供这个功能。函数把一个数字字符串(代表以美分为单位的金额)转换为美元形式,下面是函数的原型:
void dollars(char *dest, char const *src);
src将指向需要被格式化的字符(你可以假定它们都是数字)。函数应该像上面例子所示的那样对字符进行格式化,并把结果字符串保存到dest中。你应该保证你所创建的字符串以一个NUL字节结尾。src的值不应被修改。你应该使用指针而不是下标。
提示:首先找到第2个参数字符串的长度,这个值有助于判断逗号应插入到什么位置,同时,小数点和最后两位数字应该是唯一需要你进行处理的特殊情况。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void dollars(char *dest, char const *src);
void main(int argc, char **argv)
{
char *output;
output = malloc(100);
if (argc == 1) {
dollars(output, "");
printf("format success: %s\n", output);
}
else if (argc == 2) {
dollars(output, argv[1]);
printf("format success: %s\n", output);
}
else {
printf("error! please input 1 parameter\n");
}
free(output);
}
void dollars(char *dest, char const *src)
{
int i;
int len;
len = strlen(src);
*dest++ = '$';
if (len >= 3) {
for (i = len -2; i > 0;) {
*dest++ = *src++;
if ((--i > 0) && (i % 3 == 0))
*dest++ = ',';
}
}
else
*dest++ = '0';
*dest++ = '.';
*dest++ = len < 2 ? '0' : *src++;
*dest++ = len < 1 ? '0' : *src;
}
16、 这个程序与前一个练习相似,但它更为通用。它按照一个指定的格式字符串对一个数字字符串进行格式化,类似许多BASIC编码器所提供的“print using”语句。函数的原型如下:
int format(char *format_string, char const *digit_string);
digit_string中的数字根据一开始在format_string中找到的字符从右到左逐个复制到format_string中。注意被修改后的format_string就是这个处理过程的结果,当你完成时,确定format_string依然是以NUL字节结尾的。根据格式化过程中是否出现错误,函数返回真或假。
格式字符串可以包括下列字符:
字符 | 描述 |
---|---|
# | 在两个字符串中都是从右向左进行操作,格式字符串中的每个#字符都被数字字符串中的下一个数字取代,如果数字字符串用完,格式字符串中所有剩余的#字符由空白代替(但存在例外,请参见下面对小数点的讨论)。 |
, | 如果逗号左边至少有一位数字,那么它就不作修改。否则它由空白取代。 |
. | 小数点始终作为小数点存在。如果小数点左边没有一位数字,那么小数点左边的那个位置以及右边直到有效数字为止的所有位置都由0填充。 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int format(char *format_string, char const *digit_string);
void main(int argc, char **argv)
{
if (argc == 3) {
if (format(argv[1], argv[2]))
printf("format string: %s\n", argv[1]);
else
printf("format fail\n");
}
else
printf("error! please input 2 parameter\n");
}
int format(char *format_string, char const *digit_string)
{
int i;
char *format_strhead = format_string;
char const *digit_strhead = digit_string;
if (digit_string == NULL)
return 0;
format_string += strlen(format_string) - 1;
digit_string += strlen(digit_string) - 1;
for (; format_string >= format_strhead; format_string--) {
if (*format_string == '#') {
if (digit_string >= digit_strhead)
*format_string = *digit_string--;
else
if (*(format_string + 1) == '.')
*format_string = '0';
else
*format_string = ' ';
}
else if (*format_string == ',') {
if (digit_string >= digit_strhead)
continue;
else
*format_string = ' ';
}
else if (*format_string == '.') {
i = 1;
while (*(format_string + i) == ' ') {
*(format_string + i) = '0';
i++;
}
}
}
if (digit_string >= digit_strhead)
return 0;
else
return 1;
}
17、这个程序与前两个练习类似,但更加一般化了,它允许调用程序把逗号放在大数的内部,去除多余的前导0以及提供一个浮动的美元符号等。这个函数的操作类似于IBM370机器上的Edit和Mark指令。它的原型如下:
char *edit(char *pattern, char const *digits);
它的基本思路很简单,模式(pattern)就是一个图样,处理结果看上去应该向它的样子。数字字符串中的字符根据这个图样所提供的方式从左向右复制到模式字符串。数字字符串的第一位有效数字很重要,结果字符串中所有在第一位有效数字之前的字符都由一个“填充”字符代替,函数将返回一个指针,它所指向的位置正是第一位有效数字存储在结果字符串中的位置(调用程序可以根据这个返回指针,把一个浮动美元符号放在这个值左边的毗邻位置)。这个函数的输出结果就像支票上打印的那样–这个值左边所有的空白符由星号或其他字符填充。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *edit(char *pattern, char const *digits);
void main(int argc, char **argv)
{
char *effect_char;
if (argc == 3) {
if (effect_char = edit(argv[1], argv[2])) {
printf("edit string: %s\n", argv[1]);
printf("effect string: %s\n", effect_char);
}
else
printf("edit fail\n");
}
else if (argc == 2) {
edit(argv[1], "");
printf("edit string: %s\n", argv[1]);
}
else
printf("error! please input 2 or 1 parameter\n");
}
char *edit(char *pattern, char const *digits)
{
char fullch;
int signif = 0;
char *sign = NULL;
if (pattern == NULL || digits == NULL)
return NULL;
fullch = *pattern;
while (*pattern != '\0') {
switch (*pattern) {
case '#':
if (*digits == '\0') {
*pattern = *digits;
return sign;
}
else if (signif == 0) {
if (*digits == '0' || *digits == ' ')
*pattern = fullch;
else {
*pattern = (*digits == ' ')? '0' : *digits;
signif = 1;
sign = pattern;
}
}
else {
*pattern = (*digits == ' ')? '0' : *digits;
}
digits++;
break;
case '!':
if (*digits == '\0') {
*pattern = *digits;
return sign;
}
else if (signif == 0) {
*pattern = (*digits == ' ')? '0' : *digits;
signif = 1;
sign = pattern;
}
else {
*pattern = (*digits == ' ')? '0' : *digits;
}
digits++;
break;
default:
if (signif == 0)
*pattern = fullch;
}
pattern++;
}
return sign;
}