引言:
本文根据上课所学和自主总结,分享了对字符分类函数和字符转换函数理解,简单介绍了11种字符分类函数和两种字符转换函数(tolower,toupper),同时记录了使用分类函数中的isdigit,isspace,ispunct等模拟实现库函数atoi的过程。其次进行tolower,toupper这两个函数自主模拟实现,并且由tolower,toupper函数加文件操作,枚举体,函数转移表的相关知识实现了一个可以根据需要将输入字符串转化成大写或小写,并写入到某个指定文件中的程序。
目录
字符分类函数
作为最经典的计算机语言之一,C语言提供了一系列的字符分类函数,它们被放置在<ctype.h>的头文件中,用于判断字符的类型,以下是对11个字符分类函数的简单介绍,有图有真相(只包含后7种)。
- iscntrl:为真的条件是任何控制字符。控制字符包括ASCII表中的0~31和第95的DEL。点此进入cpusplus.com了解更多
- isspace:为真的条件是所有空白字符,包括' '空格符 ' \f '换页符 ' \n '换行符 ' \r' 回车符 ' \t'制表符 '\v'垂直制表符。点此进入cpusplus.com了解更多
- isdigit:为真条件0~9的十进制数点此进入cpusplus.com了解更多。
- isxdigit:十六进制数,包括十进制数和大小写a~f,点此进入cpusplus.com了解更多。
- islower:小写字母a~z为真,点此进入cpusplus.com了解更多。
- isupper:大写字母A~Z为真,点此进入cpusplus.com了解更多。
- isalpha:大小写字母点此进入cpusplus.com了解更多。
- isalnum:大小写字母和十进制数字点此进入cpusplus.com了解更多。
- ispunct: 所有标点符号,任何不包含数字或者字母的图形字符 点此进入cpusplus.com了解更多。
- isgraph:任何图形字符点此进入cpusplus.com了解更多。
- isprint:任何可打印字符点此进入cpusplus.com了解更多。
模拟实现库函数atoi
atoi函数根据cplusplus.com查找文献可知,简单地说,该函数可利用传入的字符指针找到所指向字符串中第一个数字,将它转换成int类型,并且返回这个数字,如果失败返回0。
官方翻译资料:解析c字符串str,将其内容解释为整数,并返回int类型的值。该函数首先根据需要丢弃尽可能多的空白字符(如在isspace中),直到找到第一个非空白字符。然后,从这个字符开始,接受一个可选的初始加号或减号,后面跟着尽可能多的以10为基数的数字,并将它们解释为数值。
字符串可以在组成整数的字符之后包含其他字符,这些字符将被忽略,并且对此函数的行为没有影响
本节将利用到之前介绍的isspace,ispunct,isalpha,iscntrl,isdigit函数进行atoi函数的模拟实现,在思路分享中,会将函数拆成几块,逐一解释,以下是模拟实现的思路分享,本节最后给出完整模拟代码。
思路分享
atoi函数的返回类型是int数字,对于参数我们不希望p所指向的内容被修改所以用const修饰。当进入函数后,我们最核心的部分应该是将char类型的数字转化成int类型的数字,但在这之前根据文献,应尽可能的丢弃多的空白字符,确保找到要转化的字符数字,所以我们首先要关注的是p指针不为空指针,其次传入的内容也可能是空,所以我们判断其不为斜杠零。然后利用while循环推进,筛选掉空白字符,符号字符,大小写字母,控制字符(这个出现的概率比较小,应本人知识有限,加上更保险),但有可能所传字符串中没有数字,从而造成越界访问,所以增设结束标志。
先讨论特殊情况,上段中如果遍历所有字符后,仍未找到数字,会在这一段中走else的路线。在上一段中,我们筛选掉了所有非数字的符号,但是如果碰见目标是负数时,负号就会丢失,所以我们设置一个专门记录正负的变量flg,在161行乘上这个变量,加以解决。之后,就是最核心的部分,将字符数字转化成int类型的数字。我们在脑海中想象一组字符串,此时函数所处理的字符来到了非数字字符和数字字符的交界处,但我们读到的是目标数字的最高位,而我们每往后读一个字符,目标数字就在最小位上增加一次,可知,既要有一个数来储存上一次的结果,又要有一个值来接受增加的量。所以,我们很自然的联想到要使用两个变量。再回到代码,对每次增加我们都要在上一次的基础上,把个位腾出来给增加的量,所以需要乘以十倍,而' 0 '的ASCII码值是48(当然这里直接减去' 0 '也行),所以减去48,并强制类型转化成int类型。因为可能有多个字符数字,所以用while循环,针对条件,我们要避免遍历数字时,把其中夹的字母,或者其它类型的字符,遍历进去如(wew213ew12),我们可以再次使用isdigit函数也可以用图中的写法,将函数停在3的位置,并且在结束后返回result。
完整代码:
//正式模拟
#include<stdio.h>
#include<assert.h>
#include<ctype.h>
int my_atoi(const char* p)
{
assert(p);
if (*p == '\0')
return 0;
while (isspace(*p)||ispunct(*p)||isalpha(*p)||iscntrl(*p))
{
p++;
if (*p == '\0');
break;
}
if (isdigit(*p))
{
int flg = 1;
if (*(p - 1) == '-')
flg = -1;
int num = 0;
int result = 0;
while (48 <= *p && *p <= 57)
{
result = (num * 10 + (int)(*p - 48))*flg;
num = result;
p++;
}
return result;
}
else
{
return 0;
}
}
int main()
{
char arr[256] = { 0 };
gets(arr);
int output = my_atoi(arr);
printf("输入的数字是%d,它的平方是%d\n", output, output * output);
return 0;
}
最后结果可以看到,output可以进行乘法运算。
反思总结:
值得注意的是,有时候需要处理的字符类型数字可能很大,超出了当前操作系统所能储存的最大数字。这时,可以人为的规定一个最大上限,如果数字过小同理。
字符转换函数
C语言在ctype.h的头文件中,提供了tolower和toupper两个字符转换函数,和字符分类函数一样,返回类型和参数都是int类型。本节简单介绍了这两个函数的实现,重点通过实现一个将输入字符串根据需要转化成大写或小写,并写入到指定文件中的程序来巩固上课所学的的文件操作和字符转换函数的相关知识。
tolower和toupper函数的模拟实现
值得注意的是,正是因为这两个函数的参数都是int类型,属于传值调用,所以不会改变原数据,要得到改变的值需重新接收。同时,实现时应该进行两次筛选,第一次确保是字母,这里使用isalpha函数解决,当判定为否时,根据标准返回EOF,第二次确保是大写或者小写,使用islower或isupper,最后在返回值加上32,结束。因为比较简单,不做过多赘述,代码如下
#include<stdio.h>
#include<ctype.h>
int MY_tolower(int i)
{
if (isalpha(i))
{
if (isupper(i))
{
return (i + 32);
}
else
{
return i;
}
}
else
{
return EOF;
}
}
int main()
{
char arr[100] = { 0 };
gets(arr);
int i = 0;
while (*(arr + i))
{
if (!islower(*(arr + i)))
{
*(arr + i) = MY_tolower(*(arr + i));
}
putchar(*(arr + i));
i++;
}
return 0;
}
将输入转化成大写或小写,并写入到文件中的程序
本节借助前文,并结合C语言文件操作,枚举体,函数转移表,相关知识实现一种可以将输入的英文字符串,根据需要转化成大写或者小写,并写入到指定文件中,因为内容较简单,解释见具体代码的注释。
运行结果:
![](https://i-blog.csdnimg.cn/blog_migrate/5b40454da5d1d44f38713d7454b6b80f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7b6318e84e810940e243d471b330d49e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f7682cac600ded936c3aecd34e65ea9e.png)
代码实现:
//将输入转化成大写或小写,并写入到文件中的程序
#include<stdio.h>
#include<ctype.h>
void menu1()
{
printf("*****************\n");
printf("*** UPPER 0 ***\n");
printf("*** LOWER 1 ***\n");
printf("*****************\n");
}
void menu0()
{
printf("*****************\n");
printf("*** start 1 ***\n");
printf("*** end 0 ***\n");
printf("*****************\n");
}
void fuc(int p)
{
int (*fun1[2])(int) = { toupper,tolower };
int (*fun2[2])(int) = { islower,isupper};
printf("请输入:");
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[100] = { 0 };
scanf("%s", arr);
int i = 0;
//
while (*(arr + i))
{
if ((*fun2[p])(*(arr + i)))
{
*(arr + i) = (*fun1[p])(*(arr + i));
}
fputc(*(arr + i),pf);//写入文件
putchar(*(arr + i));//展示
i++;
}
printf("\n");
fclose(pf);
pf = NULL;
}
int main()
{
//创造文件
FILE* pf = fopen("test.txt", "w");
if (pf==NULL)
{
perror("fopen");
return 1;
}
fclose(pf);
pf = NULL;
enum OPTION
{
c1 = 0,
c2 = 1,
};
enum OPTION a = c1;
enum OPTION b = c2;
int input = 0;
menu0();//菜单打印
printf("请输入你的选择:>");
scanf("%d", &input);
do
{
int input1 = 0;
menu1();
printf("请输入你的选择:>");
scanf("%d", &input1);
if (input1 == a)
fuc(0);
if (input1==b)
fuc(1);
} while (input);
return 0;
}