目 录CONTENT

文章目录

C语言讲义-字符与字符串

smallkun
2023-12-24 / 0 评论 / 0 点赞 / 127 阅读 / 9,802 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我删除。

第1节 字符的妙用

img

在第2章中我们就已经知道,存储整数可以用int,存储小数可以用float,存储单个字符可以用char。

我们用char a;定义一个字符变量a,用scanf(“%c”, &a);来从键盘读取一个字符并存放在变量a中,用printf(“%c”, a);来输出变量a中的字符。下面这段代码的作用就是从键盘读入一个字符并将其输出:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char a;
        scanf("%c", &a);
        printf("你刚才输入的字符是%c", a);
        system("pause");
        return 0;
    }

如何给一个字符变量赋值呢?很简单:

    char a;
    a='x';

请注意,x的两边是单引号,千万不要输错了。或者可以简写为:

    char a='x';

下面的内容要注意啦!

    a='1';

是把字符1赋值给字符变量a。请注意’1’、1、"1"是不同的。第1个是字符,所以两边是单引号;第2个是整数1;第3个是字符串,所以两边是用双引号,只不过"1"这个字符串看起来里面只有1个字符罢了(请注意,这里看起来只有一个字符,其实并不是,后面会讲到)。

好了,了解上面有关字符的知识后,我们可以做一个稍微复杂的计算器啦。用户可以输入a+b、a-b、a*b、a/b(这里的a和b指的是任意整数)的任意一种形式,程序便可以自动识别此时是要进行加法运算、减法运算、乘法运算还是除法运算。

由于输入的格式是“整数 运算符 整数”,所以我们需要3个变量:两个整数变量用来存储两个整数,一个字符变量用来存储运算符。

    int a, b;
    char c;

接下来就是读入部分了,输入的顺序是“整数 运算符 整数”,所以我们scanf的顺序就是"%d%c%d"。注意双引号中没有空格。

    scanf("%d%c%d", &a, &c, &b);

img

读入之后,第1个整数存储在整型变量a中,第2个整数存储在整型变量b中,运算符存储在字符变量c中。接下来就要对字符变量c所存储的内容进行分情况讨论,如果是加号则进行加法运算,如果是减号则进行减法运算,如果是乘号则进行乘法运算,如果是除号则进行除法运算。代码如下:

    if(c=='+')
      printf("%d", a+b);
    if(c=='-')
      printf("%d", a-b);
    if(c=='*')
      printf("%d", a*b);
    if(c=='/')
      printf("%d", a/b);

至此,我们已经完成了整个程序。完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          int a, b;
          char c;
          scanf("%d%c%d", &a, &c, &b);
          if(c=='+')
              printf("%d", a+b);
          if(c=='-')
              printf("%d", a-b);
          if(c=='*')
              printf("%d", a*b);
          if(c=='/')
              printf("%d", a/b);
          system("pause");
          return 0;
    }

怎么样,请输入5-6或5+6试一试吧,看看计算机能否计算出正确答案呢?

第2节 字符的本质

你猜字符1和1是什么关系?字符a和97又是什么关系呢?我们先来看一段代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
      int i;
      for(i=0; i<=127; i++)
      {
              printf("%d %c\n", i, i);
      }
      system("pause");
      return 0;
    }

在上面的代码中,循环变量i从1开始循环,一直循环到128。在输出时,将整型变量i的值输出了两次:第一次以“%d”的方式输出,第二次以“%c”的方式输出。显然,变量i是整型变量,我们用“%d”的方式来输出变量i的值是没有任何问题的。关键问题是:我们用“%c”来输出整型变量i的值时计算机会输出什么呢?“%c”不是用来输出字符的吗?我们来看看运行效果,如图7-4所示。

img

图7-4 ASCⅡ字符

经过观察发现,“%d”确实输出了整数(1~128),这个没有问题,但是“%c”却输出了一些“乱七八糟”的字符。例如:i的值为1时,通过“%c”竟然输出了一个“笑脸”;当i为3、4、5、6时,输出了扑克牌中的一些符号。

再通过仔细观察可以发现,i为48时输出字符0, i为49时输出字符1, i为50时输出字符2……i为57时输出字符9。

i为65时输出大写字母A, i为66时输出大写字母B, i为67时输出大写字母C……i为90时输出大写字母Z。

i为97时输出小写字母a, i为98时输出小写字母b, i为99时输出小写字母c……i为122时输出小写字母z。

你可以看看“+”、“-”、“*”、“/”、“>”、“<”等所对应的数字分别是多少。

你肯定会觉得很奇怪,为什么1~128每一个整数在计算机中都对应1个字符呢(7~10你可能看不到)?其实计算机本质上只能存储0和1,任意整数都可以通过进制转换的方式变化成0和1的序列。所以表示字符最简单的方法就是把字符用整数来代替。例如,字符a就用97来表示,此处的97就是字符a的ASCII码。有关ASCII码的详细信息,有兴趣的同学可以自己在课下学习。

从某种角度来说,97有两层含义,第1层含义是整数97,第2层含义是字符a。当你需要以整数的形式打印出来时就用“%d”,当你需要以字符的形式打印出来时就用“%c”。

好了,你应该知道1和字符1的区别有多大了。1就是整数1,而字符1换算成整数却是49。

第3节 人名怎么存储呢

到目前为止,学了这么久,我们竟然还不知道如何存储人名!先来解决如何存储“英文人名”的问题。人名说白了就是一串字符,单个字符我们已经知道如何存储了,代码如下:

    char a;

这样就定义了一个字符变量a来存储单个字符。那么如何存储多个字符呢?我们又想到了第6章学到的数组。没错,字符也有数组形式,叫作字符数组,也叫作字符串,形式如下:

    char a[10];

这样就定义了一个字符数组a,或者叫字符串a。它有10个小空间,即a[0]~a[9]。

这里需要注意的是:虽然有10个小空间,但实际上只能存储9个字符,因为最后一个小空间需要用来存储字符串的结束标记‘\0’,用来表示字符串的结尾。不要小看这个结束标记,很多地方都需要利用它。

那么如何读取一行字符串呢?有很多种方法。

    scanf("%s", a);

请注意,a前面没有取址符“&”。这里确实很特殊,在用scanf进行读入时,只有与“%s”配合使用来读取一行字符串时,才不需要在变量前加取址符“&”。至于为什么以后再说吧,有兴趣的同学去问问“谷哥”或者“度娘”!

输出一行字符串同样很简单:

    printf("%s", a);

请看下面一段代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          char a[10];
          scanf("%s", a);
          printf("%s", a);
          system("pause");
          return 0;
    }

上面代码的功能是,从键盘输入一行字符串,然后原封不动地将输入的字符串再次输出。假如你输入的是hello,那么也会输出hello,如图7-5所示。

img

图7-5 输出一个字符串

强调一下,此处的字符数组a(或者称作字符串a)只申请了10个空间,但只能存9个有效字符,因为最后一个需要用来存储字符串的结束标记‘\0’。

img

img

好了,我们来看1个题目:第1行先输入1个人的名字,空1格后输入这个人的分数,第2行还是先输入1个人的名字,空1格后输入这个人的分数。代码如下:

    Jack 90
    Tom 99

然后请输出分数较高的这个人的名字,对于上面的输入,我们应该输出Tom。想一想应该怎么做呢?

我们的程序需要接收4个信息,分别是第1个人的名字和分数,以及第2个人的名字和分数。人名我们可以用字符数组来存储,分数可以用整型来存储。因此我们需要两个字符数组和两个整型变量。

    char a[101], b[101];
    int x, y;

这里字符数组a和b的大小都是101,因为一般人的名字应该不会超过100个字符吧!

然后需要解决的就是如何输入了。根据输入的规则,先是1个人名和1个整数,接下来还是1个人名和1个整数。

    scanf("%s", a);
    scanf("%d", &x);
    scanf("%s", b);
    scanf("%d", &y);

请注意,在输入时,变量a和b前面没有取址符“&”,而x和y前面有取址符“&”。第1个人的名字存储在字符数组a中,第1个人的分数存储在整型变量x中。第2个人的名字存储在字符数组b中,第2个人的分数存储在整型变量y中。接下来就是判断大小了:

    if(x>y)
    {
      printf("%s", a);
    }
    else
    {
      if(x<y)
      {
          printf("%s", b);
      }
      else
      {
          printf("%s和%s的分数相同", a, b);
      }
    }

好了,完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          char a[101], b[101];
          int x, y;
          scanf("%s", a);
          scanf("%x", &x);
          scanf("%s", b);
          scanf("%x", &y);
          if(x>y)
          {
              printf("%s", a);
          }
          else
          {
              if(x<y)
              {
                  printf("%s", b);
              }
              else
              {
                  printf("%s和%s的分数相同", a, b);
              }
          }
          system("pause");
          return 0;
    }

其实,读取字符串除了用scanf外还可以用gets,用法如下:

    char a[101];
    gets(a);

它们有细微的区别,请分别运行代码1和代码2,运行时请输入:

    Tom Smith

代码1如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          char a[10];
          scanf("%s", a);
          printf("%s", a);
          system("pause");
          return 0;
    }

代码2如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          char a[10];
          gets(a);
          printf("%s", a);
          system("pause");
          return 0;
    }

分别运行后,你会发现代码1输出了:

    Tom

但是代码2却输出了:

    Tom Smith

由此可见,用scanf进行字符串读入时,遇到空格就提前终止了,但是用gets进行读入时却可以读入一整行。

同样,输出字符串除了用printf以外还可以用puts,用法如下:

    puts(a);

使用puts(a)输出时,会在末尾自动换到下一行,相当于printf(“%s\n”, a)。

给单个字符赋初始值很简单,但是如何给一个字符数组赋初始值呢?也很简单,在字符串的两边加上双引号和花括号就可以。例如:

    char a[10]={"hello"};

第4节 字母的排序

前面,我们已经学习过如何对整数进行排序。本节我们将学习如何对1行字母进行排序。即读入1行小写字母,然后将这行字母从a到z进行排序。

例如,如果输入:

    dzapytrbtc

则需要输出:

    abcdprttyz

之前已经讨论过字符的本质是整数。字符的排序和整数的排序是完全一样的。

首先申请一个字符数组a,然后用gets( )进行读入。

    char a[101]; //假设读入的字符不超过100个
    gets(a);

接下来要知道读入的字符串有多长,可以用strlen( )来获取字符串的长度。定义一个整型变量len(你可以改为你喜欢的名字)来存储字符串的长度:

    int len;
    len = strlen(a);

需要说明的一点就是,如果你用了strlen( )函数,就需要在程序的最开始(第一行),增加一条语句:

    #include <string.h>

最后,添加已经学习过的“选择排序”的代码,完整的代码如下:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char a[101], t;
        int len, i, j;
        gets(a);
        len=strlen(a);
        for(i=0; i<=len-2; i++)
        {
          for(j=i+1; j<=len-1; j++)
          {
              if(a[i]>a[j])
              {
                  t=a[i];
                  a[i]=a[j];
                  a[j]=t;
              }
          }
      }
      puts(a);
      system("pause");
      return 0;
    }

第5节 字典序

我们刚刚已经知道如何对单个字符进行排序了,那如果是一个字符串呢?比如apple和pear哪一个排在前面呢?当然是apple排在pear的前面。因为apple在英语字典中就排在pear的前面。我们在翻字典时,从第1页开始翻,会先看到apple这个单词,然后再看到pear,这个就是字典序。

我们来完成这样一个例子:输入两个单词,然后按照字典序输出这两个单词。

例如,我们输入:

    pear
    apple

需要输出:

    apple
    pear

读入和输出都很简单,关键是如何比较两个字符串。字符的比较可以用“>”、“<”、“<=”、“<=”或者“==”,但是字符串却不可以。两个字符串的比较可以用函数strcmp( )。strcmp(a, b)就是比较字符串a和字符串b在字典中的顺序。

如果字符串a和字符串b完全相同,那么返回值为0。

如果字符串a在字典中比字符串b先出现,那么返回值小于0。

如果字符串a在字典中比字符串b后出现,那么返回值大于0。

举一个例子:假设a和b是两个字符数组,分别存储两个字符串,然后把a和b按照字典序输出。

    if ( strcmp(a, b) < 0)  // a在b前面
    {
      puts(a);
      puts(b);
    }
    if ( strcmp(a, b) > 0)  // a在b后面
    {
      puts(b);
      puts(a);
    }
    if ( strcmp(a, b) == 0) // a和b 是同一个字符串
    {
      puts(a);
      puts("一样的");
    }

好了,回到本节的题目:输入任意两个字符串,将其按字典序输出。

还有,如果你用了strcmp( )函数,也需要在程序的第一行增加一条语句:

    #include <string.h>

完整的代码如下:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          char a[101], b[101];
          gets(a);
          gets(b);
          if ( strcmp(a, b) <= 0)
          {
                puts(a);
                puts(b);
          }
          else
          {
                puts(b);
                puts(a);
          }
          system("pause");
          return 0;
    }

第6节 多行字符

之前我们学习了如何存储一行字符,但如果要存储多行字符该怎么办?例如,我们需要存储5个人或者5 000个人的名字该怎么办呢。

这里我们需要使用二维字符数组,其实与我们在第6章学习的普通二维数组差不多:

    char a[5][11];

上面的语句就定义了一个二维的字符数组,这个字符数组有5行,每行有11列,也就是说可以存储5个长度不超过10的字符串(想一想为什么不是长度不超过11的字符串)。其实你可以这样理解:二维字符数组a有5行,每行都可以用来存储1行字符串。

img

下面的代码就是读入5行字符串,然后将这5行字符串原封不动地输出。请注意在输入时每行字符串不要超过10个字符:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char a[5][11];
        int i;
        for(i=0; i<=4; i++)
        {
            gets(a[i]);
        }
        for(i=0; i<=4; i++)
        {
            puts(a[i]);
        }
        system("pause");
        return 0;
    }

现在来解决这样一个问题:输入5个单词,然后把这些单词按照字典序输出。

例如,输入:

    book
    books
    car
    zoo
    apple

需要输出:

    apple
    book
    books
    car
    zoo

首先需要定义一个5行11列的二维字符数组(这里定义11列是因为常见的英文单词都在10个字母以内):

    char a[5][11];

接下来读入这5个单词:

    for(i=0; i<=4; i++)
          gets(a[i]);

再接下来就是排序,下面的代码就是我们熟悉的选择排序:

    for(i=0; i<=3; i++)
    {
        for(j=i+1; j<=4; j++)
        {
                if(a[i]>a[j])
              {
                  t=a[i];
                  a[i]=a[j];
                  a[j]=t;
              }

        }
    }

原来在对整数或者字符进行排序时,a[i]、a[j]和t都是整数或字符。但是现在a[i]、a[j]和t都是一行字符串。如果想把整个字符串a[j]赋值给a[i],是不能写成a[i]=a[j]的,需要用到字符串复制函数strcpy( )。strcpy(a[i], a[j]);的意思就是把字符串a[j]的内容原封不动地复制到字符串a[i]中,从而替换掉字符串a[i]中原来的内容。

使用strcpy( )函数,也需要在程序的第一行加上:

    #include <string.h>

另外,在本章第6节中,我们也已经讲过两个字符串的比较也不能直接用“>”、“<”或者“==”,而要用字符串比较函数strcmp( )。strcmp(a[i], a[j])的作用就是比较字符串a[i]和a[j]在字典中的顺序。

好了,完整的代码如下:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char a[5][11], t[11];
        int i, j;
        for(i=0; i<=4; i++)
        {
            gets(a[i]);
        }
        for(i=0; i<=3; i++)
        {
          for(j=i+1; j<=4; j++)
          {
                if( strcmp(a[i], a[j])>0 )
                {
                    strcpy(t, a[i]);
                    strcpy(a[i], a[j]);
                    strcpy(a[j], t);
                }
          }
        }
        for(i=0; i<=4; i++)
        {
            puts(a[i]);
        }
        system("pause");
        return 0;
    }

关于字符串的处理函数还有strcat( )等,有兴趣的同学可以自己去问“谷哥”或者“度娘”。

最后说一下,如果你需要使用二维字符数组中的某一个字符也是可以的。例如,第0行第0列就是a[0][0],第1行第2列就是a[1][2]……。

a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] a[0][5]a[0][6]a[0][7] a[0][8] a[0][9] a[0][10]
a[1][0] a[1][1] a[1][2] a[1][3] a[1][4] a[1][5]a[1][6]a[1][7] a[1][8] a[1][9] a[1][10]
a[2][0] a[2][1] a[2][2] a[2][3] a[2][4] a[2][5]a[2][6]a[2][7] a[2][8] a[2][9] a[2][10]
a[3][0] a[3][1] a[3][2] a[3][3] a[3][4] a[3][5]a[3][6]a[3][7] a[3][8] a[3][9] a[3][10]
a[4][0] a[4][1] a[4][2] a[4][3] a[4][4] a[4][5]a[4][6]a[4][7] a[4][8] a[4][9] a[4][10]

第7节 存储一个迷宫

好了,又到了本章的最后1节。我们已经学习了如何存储多行字符,但是一直忽略了一个问题,就是如何对二维字符数组进行初始化。例如,要存储一个迷宫该怎么办?

    ###########
    #O   #  ###
    # ## ##   #
    #  #    # #
    # #### ## #
    #       #
    ###########

其实二维字符数组的初始化和一维数组的初始化差不多,我们现在回忆一下之前是如何给一维字符数组进行初始化的:

    char a[10]={"hello"};

一维字符数组的初始化很简单,直接在字符串的两边加上双引号和花括号就可以。二维字符数组的初始化代码如下:

    char a[2][10]={"hello", "world"};

或者

    char a[2][10]={"hello",
                    "world"};

写成两行,主要是为了美观,更形象地表现出我们是在对二维字符数组进行初始化。

仔细观察后你会发现,字符数组a有两行,因此在初始化时有两个带双引号的字符串,并用逗号隔开。

如果要初始化本节开头的那个迷宫,我们需要定义足够大的二维字符数组,这个迷宫有7行,每行有11列。因此在定义字符数组时也要有7行,但是每行要有12列(不要忘记每行字符串的结尾要有‘\0’)。

    char a[7][12]={"###########",
                    "#O   #  ###",
                    "# ## ##   #",
                    "#  #    # #",
                    "# #### ## #",
                    "#       #  ",
                    "###########"};

现在我们可以用for循环和puts( )来把这个迷宫输出到屏幕上。因为有7行,但是字符数组是从第0行开始的,所以循环变量i是从0到6。从第0行到第6行,而每1行是一个一维字符串,因此直接用puts(a[i])就可以。代码如下:

    for(i=0; i<=6; i++)
        puts(a[i]);

完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          int i;
          char a[7][12]={"###########",
                          "#O   #  ###",
                          "# ## ##   #",
                          "#  #    # #",
                          "# #### ## #",
                          "#       #  ",
                          "###########"};
          for(i=0; i<=6; i++)
              puts(a[i]);
          system("pause");
          return 0;
    }
0

评论区