使用 string


C++ 标准函数库提供string,可以使用这个类来创建字符串,便于进行高阶的字符串操作,像是字符串指定、连接等,若要表现字符串,C++ 建议使用string,这要先包含string标头文件:

#include <string>

可以使用以下方式来创建string实例,例如:

string str1;                 // 内容为空字符串
string str2("caterpillar");  // 内容为指定的字符串常量
string str3(str2);           // 以 str1 实例创建字符串
string str4 = "Justin";      // 内容为指定的字符串常量

第一个方式创建了空字符串,长度为 0;第二个方式会以字面常量内容来创建string实例;第三个方法会复制str2,并创建一个新的string实例,第四个方式也是以字面常量内容来创建string实例。

可以使用size()length()来获取字符串长度,使用empty()测试字符串是否为空,使用==比较两个字符串的内容是否相同,例如:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string str1; 
    string str2 = "caterpillar"; 
    string str3(str2); 

    cout << "str1 是否为空:" << str1.empty() << endl;
    cout << "str1 长度: " << str1.size() << endl;
    cout << "str2 长度: " << str2.size() << endl;
    cout << "str3 长度: " << str3.size() << endl;

    cout << "str1 与 str2 内容是否相同:" << (str1 == str2) << endl;
    cout << "str2 与 str3 内容是否相同:" << (str3 == str3) << endl;

    return 0; 
}

执行结果:

str1 是否为空:1
str1 长度: 0
str2 长度: 11
str3 长度: 11
str1 与 str2 内容是否相同:0
str2 与 str3 内容是否相同:1

可以将字符串指定给另一个字符串,例如:

string str1 = "text1";
string str2 = "text2";
....
str1 = str2;

以上指定会将str1原本的字符串内存空间释放,并重新配置足够容纳str2的内存空间,然后将str2的内容逐一复制至str1;也可以将一个 C 风格的字符串指定给string,例如:

string name = "caterpillar";
char str[] = "Justin";
name = str;

然而不能将一个string实例指定给字符数组,例如:

char str[] = "Justin";
string name = "caterpillar";
str = name; // error

可以使用+运算符来连接字符串,例如:

str1 = str1 + str2;
str1 = str1 + "\n";

string实例可以使用[]指定索引来访问相对应位置的char,就有如字符数组的操作一般,例如:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string name = "caterpillar";

    for(int i = 0; i < name.length(); i++) {
        cout << name[i] << endl;
    }

    return 0; 
}

对于顺序遍历的需求,可以使用 for range 语法:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string name = "caterpillar";

    for(auto ch : name) {
        cout << ch << endl;
    }

    return 0; 
}

那么是该面对问题的时候了,对于"caterpillar",以上范例是会逐一显示各个字母没错,若是中文呢?这就要回忆一下〈字符数组与字符串〉的内容了,例如以上会显示什么呢?

string name = "良葛格";
cout << name.length() << endl;

类似地,这要看你的源码编码是什么,以及编译时下的参数是什么,如果是这个的话:

string name = u8"良葛格";
cout << name.length() << endl;

就是显示 9 了,因为一个中文本以 UTF-8 编码的话,会有三个字节,也就是说,对于 C++ 来说,这是个多字节字符组成的字符串,每个中文要使用三个char,若要正确显示中文的话,你的文本模式要是 UTF-8,然后如下编写程序:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string name = u8"良葛格";
    for(int i = 0; i < name.length(); i += 3) {
        cout << name.substr(i, 3) << endl;
    }

    return 0; 
}

stringsubstr方法可以指定索引与char长度来获取子字符串,因为是 UTF-8 编码,每次要取三个char,然后索引加 3。

这当然是蛮麻烦的一件事,而且 UTF-8 编码的字符串中若有中英、数字夹杂的话就很麻烦了,〈字符数组与字符串〉中谈过wchar_tchar16_tchar32_tchar8_tstring标头中对应的版本是wstringu16stringu32stringu8string

因此,若直接搭配〈字符数组与字符串〉中的toUTF8函数,可以直接使用cout来显示中文,以wstring为例的话:

#include <iostream>
#include <string>

using namespace std;
string toUTF8(int cp);

int main(int argc, char *argv[]) {
    // 在 UTF-8 终端下会显示正确中文
    wstring name = L"良葛格(Justin)";
    for(int i = 0; i < name.length(); i++) {
        cout <<  toUTF8(name[i]) << endl;
    }

    return 0;
}

string toUTF8(int cp) {
    char ch[5] = {0x00};
    if(cp <= 0x7F) { 
        ch[0] = cp; 
    }
    else if(cp <= 0x7FF) { 
        ch[0] = (cp >> 6) + 192; 
        ch[1] = (cp & 63) + 128; 
    }
    else if(0xd800 <= cp && cp <= 0xdfff) {} // 无效区块
    else if(cp <= 0xFFFF) { 
        ch[0] = (cp >> 12) + 224; 
        ch[1]= ((cp >> 6) & 63) + 128; 
        ch[2]= (cp & 63) + 128; 
    }
    else if(cp <= 0x10FFFF) { 
        ch[0] = (cp >> 18) + 240; 
        ch[1] = ((cp >> 12) & 63) + 128; 
        ch[2] = ((cp >> 6) & 63) + 128; 
        ch[3]= (cp & 63) + 128; 
    }
    return string(ch);
}

C++ 11 提供了wstring_convert,便于在stringwstring间转换,例如,若要将wstring转换为 UTF-8 编码的string,可以如下:

#include <locale>
#include <codecvt>
#include <string>
#include <iostream>
using namespace std;

int main() {
    wstring_convert<codecvt_utf8<wchar_t>> utf8;
    wstring ws = L"良葛格";
    string s = utf8.to_bytes(ws);
    cout << s << endl; // 在 UTF-8 编码终端下可显示中文
}

若只是要在标准输出中显示wstring,可以使用wcout,不过必须设定正确的locale,才能显示正确的字符,在许多文件中都会写到可以这么使用:

#include <iostream>
#include <locale>
using namespace std;

int main( void ) {
    locale loc("cht");
    wcout.imbue(loc);

    wcout << L"良葛格" << endl;

    return 0;
}

不过我使用MinGW-w64,GNU 编译器版本 8.1.0 编译后,执行时会发生底下的问题:

terminate called after throwing an instance of 'std::runtime_error'
  what():  locale::facet::_S_create_c_locale name not valid

这似乎是 Windows 上 MinGW-w64 的问题,我找到以下的方式解决:

#include <iostream>
#include <locale>
#include <string>
#include <codecvt>
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    locale utf8(std::locale(), new codecvt_utf8<wchar_t>);
    wcout.imbue(utf8);

    wcout << L"良葛格" << endl; // 在 UTF-8 编码终端下可显示中文

    return 0;
}




展开阅读全文