字符与字符串

150 阅读6分钟

在计算机中,二进制数字与二进制数字串构成了各种数据。而在C语言中,字符与字符串涵盖了各种形式的字符数据。

字符

字符就是各种符号与各种文字,而在C语言中,由于字符就是数字的一种形式,所以我们可以通过数字运算循环,打印出这种“字符-数字"对应关系。

在程序设计中,验证观点的最好办法就是动手制作程序,下面我们将会向您展示 C语言 中的“字符-数字"对应关系。

C语言:使用 Ascil 字符集

/*
    ascil.c
    Print the ascil table
    BeginnerC
*/
#include <stdio.h>
int main()
{
    for (int i = 0; i < 256 ;i++)
    {
        printf("%d => %c \t", i, i);
        if (i % 9 == 0)
        {
            printf("\n");
        }
    }
    printf("\n");
    return 0;
}

下面我们分析其中的知识点

  1. for 循环语句
    在阅读过《入门:C语言中的逻辑与循环》之后,我们相信你一定已经了解到了 while 循环的使用方法。
    事实上,C语言中的循环,殊途同归,只不过是同一样逻辑的不同表达形式。
    for 循环与 while 循环的区别在于,for 循环为循环的初始化与每一次循环的收尾语句提供了位置
  2. % 运算
    求余运算可以计算出每一次整数除法之后的余数
    A % B 如果结果为 0,代表 A 可以被 B 整除
  3. printf 中的 %d %c
    在 printf 函数中,%d 用于打印整数,%c 用于打印字符
  4. ‘\t’ 字符
    制表符,反映到我们此处的程序,可以视作“8个空格"

而除了上面四条的知识点之外,最重要的莫过于整数与具体字符的对应关系,我们将这种关系称呼为“字符集"

(C语言使用 Ascil 字符集,转换关系由上面我们制作的程序所体现)

putchar & getchar 函数

在 C语言 中,putchar 函数可以用于打印字符,如您所见

/*
    putchar.c
    Use the putchar function
    BeginnerC
*/
#include <stdio.h>
int main()
{
    putchar('c');
    return 0;
}

而 getchar 函数可以用来读取字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEgr04zN-1683077265704)(foruda.gitee.com/images/1677… “1672720001605.png”)]

/*
    getchar.c
    Use the getchar function
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char c;
    c = getchar();
    putchar(c);
    return 0;
}

字符的识别

C语言提供了有用的组件来帮助我们处理各种字符,这一组有用的组件包含在 ctype.h 标准库中。

比如,我们可以制作一个简单的字符识别小程序,它可以判断输入字符的类型。

/*
    ctype.c
    Judge the type of char
    BeginnerC
*/
#include <ctype.h>
#include <stdio.h>
int main()
{
    char c;
    c = getchar();
    if (isalpha(c))
    {
        printf("%c is a letter\n", c);
        if (isupper(c))
        {
            printf("Upper Letter\n");
        }
        else if (islower(c))
        {
            printf("Lower Letter\n");
        }
    }
    else if (isdigit(c))
    {
        printf("%c is a number\n", c);
    }
    else
    {
        printf("Unknow\n");
    }
    return 0;
}

字符的处理

在对字符的类型进行判断之外,我们还可以对字符本身进行转换。

ctype.h 也为我们提供了这方面的组件。

tolowertoupper 能够帮助我们将字母的大小写进行转换

/*
    alpha.c
    Use toupper & tolower
    BeginnerC
*/
#include <stdio.h>
#include <ctype.h>
int main()
{
    char c;
    c = getchar();
    if (isalpha(c))
    {
        if (islower(c))
        {
            putchar(toupper(c));
        }
        else
        {
            putchar(tolower(c));
        }
    }
    else
    {
        printf("Not a alpha.\n");
    }
    return 0;
}

拓展:中文的表示

早期的计算机,因为硬件条件与软件设计的限制,最初的字符集只能够用于表示字母与简单的符号,而对于其它形式的文字则无能为力。

而伴随着计算机硬件与软件的发展,有许多其它的字符集得以提出,用于展现其它的文字。

其中,GBK字库用于表示中文,为了兼容 Ascil 字符集合,GBK 字库使用 “单双字节变长编码,单字节编码完全兼容ASCII字符编码,而中文部分采用双字节编码。"

这种单双字节变长编码的做法,我们可以采用如下的形式进行验证:

/*
    char_chinese.c
    Test the chinese in C language
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char c = '好';
    printf("%d => %c\n", c, c);
    return 0;
}

程序的执行没能符合我们的期待,原因显而易见,汉字占用两个字节,而一般的 Ascil 字符仅仅占用 1 个字节,因此自然不能用于表示汉字。

这种情况称之为“溢出",解决的办法在于拿出足够承载的空间。

在 C语言 中,标准库 wchar.h 提供了用于处理诸如汉字之类占用多字节的字符的函数与类型,我们称之为“宽字符函数库"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOlexdem-1683077265708)(foruda.gitee.com/images/1677… “1672716017927.png”)]

C语言参考材料 (由全球C/C++ 社区维护)上提供了与之有关的信息,在这里,由于宽字符相关的知识过于繁琐与复杂,我们选择不继续深入阐述。

值得注意的是,当下流行的字符集被称为 UTF-8 字符集,较之于 GBK,它具有更好的通用性。

字符串

字符串就是字符数组

由于字符串的本质就是字符的集合,从C语言的语法层面而言,是一个数组。

数组就是对相同类型变量的一个集合

如下面所展示

/*
    string_array.c
    Print the string
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string[] = "C Language";
    for (int i = 0;i < sizeof(string);i++)
    {
        putchar(string[i]);
    }
    return 0;
}

字符数组就是数字流

在前文中我们发现,字符就是C语言中数字的另一种表示形式,因此对于字符串,我们当然也可以将他们视作为多个数字。

事实上,上面的程序可以改写为:

/*
    string_array_int.c
    Print the string
    BeginnerC
*/
#include <stdio.h>
int main()
{
    int string[] = {'C', ' ', 'L', 'a', 'n', 'g', 'u', 'a', 'g', 'e', '\0'};
    for (int i = 0;i < sizeof(string) / sizeof(string[0]);i++)
    {
        putchar(string[i]);
    }
    return 0;
}

我们可以很明显地发现与第一个程序相比,这个程序变得明显地复杂,具体体现为如下:

  1. 我们没有使用 “C Language” 进行赋值
    这是因为,int 类型数组终究代表整数类型数组,它并不是 C语言中内置用于处理字符串类型数据的类型,因此我们不能使用字符类型的语法糖进行处理(语法糖就是将某一项问题的解决用更简单的方式表示)
    只能老老实实使用数组元素赋值的语法
  2. “C Language” 之外,多了一个 ‘\0’
    在 C语言 中,所有的可以正常使用的字符串都需要以 ‘\0’ 字符作为收尾,换而言之,我们字符串的储存空间实际上“比我们见到的要大一点"
    我们可以编写程序验证这一点
  3. for 循环语句的条件中,sizeof(string) 需要除 sizeof(string[0])
    这是因为,在我们使用 char 数据类型的时候,每一个元素只占用一个字节,这就意味着,sizeof(string)是多少,就有多少个元素。
    而第二个程序之中,因为 int 数据类型往往占用多个字节空间(往往是4个),如果我们还要使用之前的思路进行处理,势必出现问题。
    所以我们必须要将 sizeof(string) 计算出的总储存空间,除以单个元素占用的储存空间,计算出元素的个数(元素个数 * 单个元素所用空间 = 总储存空间)

作为对 2 的验证,我们可以将 程序一 改写为如下

/*
    string_array_ascil_code.c
    Print the array ascil code
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string[] = "C Language";
    for (int i = 0;i < sizeof(string);i++)
    {
        printf("%d => %c\n", string[i], string[i]);
    }
    return 0;
}

很明显发现,最后的元素就是 ‘\0’,也就是数字0

用数字流的思路,解决一些字符串问题

字符串的比较

我们可以用数字流的思路解决一些实际的问题,在这里,我们以字符串比较为案例

字符串的比较实际上就是对每一个元素所代表的数字进行比较,据此我们写出如下程序

/*
    string_compare.c
    Compare the string
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string_1[1024] = {};
    char string_2[1024] = {};
    int flag = 0;
    scanf("%s", string_1);
    scanf("%s", string_2);
    for (int i = 0;i < 1024;i++)
    {
        if (string_1[i] != string_2[i])
        {
            flag = 1;
            break;
        }
    }
    if (flag)
    {
        printf("Not Equal\n");
    }
    else
    {
        printf("Equal\n");
    }
    return 0;
}

可以发现,尽管这个程序能够顺利地进行字符串的比较,但还是存在着许多缺陷,其中,最重要的就有两个

  1. 对字符串的比较,存在长度限制
  2. 忽略了参与比较的字符串可能存在比较的空间问题,容易造成溢出

因此,有比较对这一问题进行改进,由此我们提出如下方案

/*
    string_compare_final.c
    Compare the string
    (Final Version)
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string_1[1024] = {};
    char string_2[12] = {};
    int flag = 0;
    scanf("%s", string_1);
    scanf("%s", string_2);
    for (int i = 0;string_1[i] && string_2[i];i++)
    {
        if (string_1[i] != string_2[i])
        {
            flag = 1;
            break;
        }
    }
    if (flag)
    {
        printf("Not Equal\n");
    }
    else
    {
        printf("Equal\n");
    }
    return 0;
}

在这套方案之中,我们使用 字符串结束标志 作为结束的条件(&& 逻辑运算中,参与运算的两个变量中的一个为 0 时,结果为 0)

由此解决了长度的限制.

计算字符串的长度

用同样的方式,我们可以制作一个字符串长度统计程序

/*
    string_length.c
    Calc the length of string
    BeginnerC
*/
#include <stdio.h>
int main()
{
    char string[1024] = {};
    int length = 0;
    scanf("%s", string);
    for (length = 0;string[length];length++)
        ;
    printf("The length is %d\n", length);
    return 0;
}

C语言中的字符串组件库

C语言 string.h 标准库提供了许多有用的字符串函数,我们可以使用他们解决各种有用的问题。

比如,字符串的比较与字符串的长度计算,就可以使用 strcmp 与 strlen 函数进行解决。