【在主画面加入捷径】
       
【选择语系】
繁中 简中

[C 语言] 程序设计教学:如何使用 C 字串 (String)

【赞助商连结】

    学完数组和指针后,就有足够的预备知识学习 C 字串。C 语言没有独立的字串类型,而 C 字串是以 char 或其他字符 (character) 为基础类型的数组,所以要有先前文章的铺陈才容易学习 C 字串。

    (影音教学) 字串的基本处理

    在以下影片中,我们会将数个常见的 C 字串处理的流程拆解,让读者易于了解:

    本影片的内容是选择性的,不影响本文的阅读。

    C 语言的字串方案

    在 C 语言中,常见的字串方案有以下数种:

    • 固定宽度字符数组
      • 字符数组 (character array)
      • 宽字符数组 (wide character array):使用 wchar.h函数库
    • 多字节编码 (multibyte encodings),像是 Big5 (大五码) 或 GB2312 等
    • 统一码 (Unicode):包括 UTF-8、UTF-16、UTF-32 等

    有些方案为等宽字符,有些方案则采不等宽字符编码。原本的字符数组仅能处理英文文字,其他方案则是为了处理多国语文文字而产生的。

    例如,在支援 Unicode 的终端机环境,可以透过 wchar 印出中文字串:

    #include <locale.h>
    #include <wchar.h>
    
    int main(void)
    {
        // Trick to print multibyte strings.
        setlocale(LC_CTYPE, "");
    
        wchar_t *s = L"你好,世界";
        printf("%ls\n", s);
    
        return 0;
    }
    

    由于本文的目的是了解字串的基本操作,我们仍然是以原先的字符数组为准,本文不考虑多国语言的情境。

    C 字串微观

    我们由 "Hello World" 字串来看 C 字串的组成:

    C 语言字串示意图

    由上图可知,C 字串除了依序储存每个字符外,在尾端还会额外加上一个 '\0' 字符,代表字串结束。由于 C 字串需要尾端的 '\0' 字符来判断字串结束,我们在处理字串时,别忘了在字串尾端加上该字符。

    接下来,我们会介绍数个字串操作的情境。由于 C 标准函式库已经有 string.h函数库,在采作字串时应优先使用该函式库,而非重造轮子;本文展示的程序仅供参考。

    计算 C 字串长度

    计算字串长度时,不包含尾端的结束字尾,所以 "happy" 的字串长度为 5。可参考以下的范例程序代码:

    #include <assert.h>
    #include <stddef.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "Hello";
        
        size_t sz = 0;
        
        for (size_t i = 0; s[i]; i++) {
            sz++;
        }
        
        assert(sz == strlen(s));
        
        return 0;
    }
    

    当字串走到尾端的零值时,s[i] 会回传零,这时循环会结束。藉由走访字串数组一轮就可以知道字串长度。

    复制 C 字串

    一般使用 strcpy函数的范例,都是预先从 stack memory 配置某个长度的字符数组;本例略加修改,先动态计算来源字串的长度,再由 heap memory 动态配置一块新的字符数组,将原本的字符逐一复制到目标字串即完成。参考以下程序代码:

    #include <assert.h>
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "Hello World";
        
        size_t sz_s = strlen(s);
        size_t sz = sz_s + 1;  // Add trailing zero.
        
        char *out = malloc(sz * sizeof(char));
    
        for (size_t i = 0; i < sz_s; i++) {
            out[i] = s[i];
        }
        
        out[sz-1] = '\0';
        
        assert(strcmp(out, "Hello World") == 0);
        
        free(out);
        
        return 0;
    }
    

    在本例中,由于 out 是由 heap 配置内存,使用完要记得手动释放。

    相接两个 C 字串

    原本的 strcat函数需预先估计目标字串的长度,笔者略为修改,采用动态计算字串长度后生成所需长度的字符数组,最后将原本的字串逐一复制过去。范例程序代码如下:

    #include <assert.h>
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void) {
        char s_a[] = "Hello ";
        char s_b[] = "World";
        
        size_t sz_a = strlen(s_a);
        size_t sz_b = strlen(s_b);
        size_t sz = sz_a + sz_b + 1;  // Include tailing zero.
        
        char *out = malloc(sz * sizeof(char));
    
        for (size_t i = 0; i < sz_a; i++) {
            out[i] = s_a[i];
        }
        
        for (size_t i = 0; i < sz_b; i++) {
            out[i+sz_a] = s_b[i];
        }
        
        out[sz-1] = '\0';  // Add tailing zero.
        
        assert(strcmp(out, "Hello World") == 0);
        
        free(out);
        
        return 0;
    }
    

    在本例中,由于 out 是由 heap 配置内存,使用完要记得手动释放。

    检查两个 C 字串是否相等

    检查字串相等的流程很简单:

    • 检查两字串是否等长
    • 逐一扫描两字串的元素,确认每个元素是否相等

    可参考以下范例程序代码:

    #include <assert.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <string.h>
    
    int main(void)
    {
        char s_a[] = "happy";
        char s_b[] = "hurry";
        
        bool is_equal = true;
        
        size_t sz_a = strlen(s_a);
        size_t sz_b = strlen(s_b);
        
        if (sz_a != sz_b) {
            is_equal = false;
            goto END;
        }
        
        for (size_t i = 0; i < sz_a; i++) {
            if (s_a[i] != s_b[i]) {
                is_equal = false;
                goto END;
            }
        }
        
    END:
        assert(is_equal == false);
    
        return 0;
    }
    

    寻找子字串

    寻找子字串的示意图如下:

    在 C 字串中找寻子字串

    本例的想法相当简单,我们逐一走访原字串,在每个位置检查是否符合子字串。

    将以上想法写成伪代码如下:

    s is the original string.
    ss is the substring.
    
    is_found <- false
    
    for (i from 0 to Len(s) - 1) do
        if i + Len(ss) >= Len(s) then
            break
        end if
        
        flag <- true
        for (j from 0 to Len(ss) - 1) do
            if s[i+j] != s[j] then
                flag <- false
                break
            end if
        end for
        
        if flag == true then
            is_found <- true
            break
        end if
    end for
    
    check whether is_found is true or not
    

    最后展示 C 语言的实现:

    #include <assert.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <string.h>
    
    int main(void)
    {
        // Original string.
        char s[] = "The quick brown fox jumps over the lazy dog";
        // Substring.
        char ss[] = "lazy";
        
        bool is_found = false;
    
        size_t sz_s = strlen(s);
        size_t sz_ss = strlen(ss);
        
        bool temp;
        for (size_t i = 0; i < sz_s; i++) {
            if (i + sz_ss >= sz_s) {
                break;
            }
            
            temp = true;
            for (size_t j = 0; j < sz_ss; j++) {
                if (s[i+j] != ss[j]) {
                    temp = false;
                    break;
                }
            }
            
            if (temp) {
                is_found = true;
                break;
            }
        }
    
        assert(is_found);
    
        return 0;
    }
    
    【赞助商连结】