1. C++字符串操作核心概念解析
在C++编程中,字符串处理是最基础也是最频繁使用的功能之一。不同于C语言使用字符数组的方式,C++通过string类提供了更安全、更便捷的字符串操作接口。string类封装了底层字符数组的管理,自动处理内存分配和释放,极大降低了开发者的负担。
string类的设计遵循了STL(标准模板库)的惯例,提供了丰富的成员函数和操作符重载。从简单的长度查询到复杂的模式匹配,string类几乎涵盖了所有常见的字符串处理需求。值得注意的是,C++11之后的标准库对字符串处理进行了多项重要增强,比如新增了数值转换、正则表达式支持等。
重要提示:虽然C++保留了C风格的字符串函数(strlen、strcpy等),但在现代C++代码中应当优先使用string类,除非有明确的性能需求或与遗留代码交互的特殊情况。
2. 字符串基础操作详解
2.1 字符串定义与初始化
C++提供了多种字符串初始化方式,每种方式适用于不同的场景:
cpp复制// 空字符串
std::string str1;
// C风格字符串初始化
std::string str2("Hello World");
// 重复字符构造
std::string str3(5, 'A'); // "AAAAA"
// 子串初始化
std::string str4(str2, 6, 5); // "World"
// 移动构造(C++11)
std::string str5(std::move(str2));
初始化时需要注意的几个关键点:
- 直接使用等号是拷贝初始化,而使用括号是直接初始化
- 从C风格字符串构造时,会复制内容而非直接使用指针
- C++11引入的移动构造可以避免不必要的拷贝
2.2 字符串输入输出操作
字符串的读写操作是日常开发中最常用的功能:
cpp复制// 标准输入
std::string input;
std::cin >> input; // 读取到空白符停止
std::getline(std::cin, input); // 读取整行
// 文件操作
std::ifstream file("data.txt");
std::string line;
while(std::getline(file, line)) {
// 处理每行内容
}
// 格式化输出
std::cout << "Length: " << input.length() << std::endl;
实际开发中,处理用户输入时需要注意:
cin >>和getline混用时要注意缓冲区残留的换行符- 文件读取时应当检查文件是否成功打开
- 对于大文件,逐行处理比一次性读取更安全
3. 字符串查询与访问
3.1 基本信息查询
string类提供了多种查询字符串属性的方法:
cpp复制std::string s = "Hello World";
s.empty(); // 是否为空
s.size(); // 字符数量(C++11后推荐)
s.length(); // 同size()
s.capacity(); // 当前分配的存储空间
s.max_size(); // 系统允许的最大长度
性能提示:empty()通常比size()==0更高效,因为前者只需检查内部指针是否为空,而后者需要计算长度。
3.2 元素访问与迭代
访问字符串中的字符有多种方式:
cpp复制std::string s = "Hello";
// 下标访问
char c1 = s[1]; // 'e'
char c2 = s.at(1); // 'e',带边界检查
// 迭代器访问
for(auto it = s.begin(); it != s.end(); ++it) {
std::cout << *it;
}
// C++11范围for
for(char c : s) {
std::cout << c;
}
安全建议:
- 优先使用at()而非[],因为前者会进行边界检查
- 修改字符串时,迭代器可能失效,需要特别注意
- C++17引入了string_view,可以安全地传递字符串视图
4. 字符串修改与操作
4.1 内容修改
string类提供了丰富的修改接口:
cpp复制std::string s = "Hello";
s += " World"; // 追加
s.append("!!!"); // 同+=
s.push_back('!'); // 追加单个字符
s.insert(5, " C++"); // 在位置5插入
s.erase(5, 3); // 从位置5删除3个字符
s.replace(6, 5, "Universe"); // 替换子串
s.clear(); // 清空字符串
性能优化技巧:
- 大量拼接操作时,reserve()预先分配空间可避免多次重分配
- replace()比erase()+insert()组合更高效
- C++11引入的shrink_to_fit()可以释放多余内存
4.2 字符串比较
字符串比较有多种方式:
cpp复制std::string a = "apple";
std::string b = "banana";
// 操作符比较
bool r1 = (a == b); // false
bool r2 = (a < b); // true
// 成员函数比较
int r3 = a.compare(b); // 返回负值(a<b)
// 子串比较
int r4 = a.compare(0, 2, b, 0, 2); // "ap" vs "ba"
比较规则说明:
- 字典序比较,区分大小写
- compare()返回0表示相等,负值表示小于,正值表示大于
- C++20引入了三路比较运算符<=>,可以更高效地比较
5. 字符串搜索与子串操作
5.1 搜索功能
string类提供了多种搜索方法:
cpp复制std::string s = "Hello World, Hello C++";
size_t pos1 = s.find("Hello"); // 0
size_t pos2 = s.rfind("Hello"); // 13
size_t pos3 = s.find_first_of("abc"); // 查找任意匹配字符
size_t pos4 = s.find_last_not_of(" "); // 查找最后一个非空格
// C++17新增的搜索算法
std::boyer_moore_searcher bm_searcher("Hello");
auto it = std::search(s.begin(), s.end(), bm_searcher);
搜索技巧:
- find()返回string::npos表示未找到
- 对于复杂模式匹配,考虑使用正则表达式
- 多次搜索相同模式时,可以预编译搜索器(如C++17的boyer_moore)
5.2 子串操作
提取子串是常见需求:
cpp复制std::string s = "Hello World";
std::string sub = s.substr(6, 5); // "World"
// 分割字符串示例
std::vector<std::string> tokens;
size_t start = 0, end = 0;
while((end = s.find(' ', start)) != std::string::npos) {
tokens.push_back(s.substr(start, end - start));
start = end + 1;
}
tokens.push_back(s.substr(start));
实用建议:
- substr()的第一个参数是起始位置,第二个是长度(不是结束位置)
- 对于复杂的分割需求,可以考虑使用stringstream或正则表达式
- C++20引入了starts_with()和ends_with()方法,简化了常见检查
6. 字符串与数值转换
6.1 数值转字符串
C++11引入了方便的数值转换函数:
cpp复制// 整数转字符串
std::string s1 = std::to_string(42);
std::string s2 = std::to_string(3.14);
// 更灵活的控制(C++17)
char buffer[100];
std::to_chars(buffer, buffer+100, 123.456, std::chars_format::scientific);
格式化技巧:
- 对于需要特定格式的情况,可以使用ostringstream
- to_chars()比sprintf()更安全,不涉及内存分配
- 本地化数字格式可以使用
头文件
6.2 字符串转数值
同样有多种转换方式:
cpp复制std::string s = "123.45";
// 简单转换
int i = std::stoi(s);
double d = std::stod(s);
// 更安全的转换(C++17)
int value;
std::from_chars(s.data(), s.data()+s.size(), value);
错误处理建议:
- stoi/stod等函数在转换失败时会抛出异常
- from_chars()返回错误码而非抛出异常,适合高性能场景
- 总是检查转换结果,特别是处理用户输入时
7. 字符串与容器操作
7.1 与字符数组互转
与C风格字符串的互操作:
cpp复制std::string s = "Hello";
// string转C风格字符串
const char* cstr1 = s.c_str(); // 以null结尾
const char* cstr2 = s.data(); // C++17后不以null结尾
// C风格字符串转string
char buffer[] = "World";
std::string s2(buffer);
安全注意事项:
- c_str()返回的指针在string修改后可能失效
- 不要修改c_str()返回的内容
- C++17后data()返回的指针不一定以null结尾
7.2 与字节容器互转
处理二进制数据时很有用:
cpp复制std::string s = "Hello";
// 获取底层字节
const char* bytes = s.data();
size_t length = s.size();
// 从字节序列构造
std::string s2(bytes, length);
// 与vector互转
std::vector<char> vec(s.begin(), s.end());
std::string s3(vec.begin(), vec.end());
二进制处理技巧:
- 对于非文本数据,最好使用vector<uint8_t>
- C++17引入了string_view,可以避免不必要的拷贝
- 跨平台时要注意字节序问题
8. 高级字符串操作
8.1 正则表达式支持
C++11引入了正则表达式库:
cpp复制#include <regex>
std::string s = "Email: test@example.com";
std::regex email_regex(R"(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b)");
// 检查匹配
bool is_email = std::regex_search(s, email_regex);
// 提取匹配
std::smatch matches;
if(std::regex_search(s, matches, email_regex)) {
std::cout << "Email found: " << matches[0] << std::endl;
}
// 替换
std::string result = std::regex_replace(s, email_regex, "[email redacted]");
正则表达式性能提示:
- 重复使用的正则表达式应当预编译
- 简单匹配优先使用字符串方法
- 复杂模式考虑使用第三方库如PCRE
8.2 本地化与编码转换
处理多语言字符串:
cpp复制#include <locale>
#include <codecvt>
// 宽字符串转换
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wide = L"你好世界";
std::string utf8 = converter.to_bytes(wide);
// 本地化比较
std::locale loc("en_US.UTF-8");
bool result = std::use_facet<std::collate<char>>(loc).compare(
"apple", "apple"+5, "Apple", "Apple"+5) == 0;
编码处理建议:
- C++11提供了
,但在C++17中被标记为废弃 - 对于复杂的编码转换,考虑使用ICU等专业库
- 内部处理最好统一使用UTF-8编码
9. 性能优化与最佳实践
9.1 内存管理优化
string的内存分配策略:
cpp复制std::string s;
s.reserve(1000); // 预分配空间
for(int i=0; i<1000; ++i) {
s += "x"; // 不会触发重分配
}
s.shrink_to_fit(); // 释放多余空间(C++11)
内存优化技巧:
- 小字符串优化(SSO):大多数实现会对短字符串进行特殊处理
- reserve()可以避免多次分配的开销
- 移动语义(C++11)可以减少不必要的拷贝
9.2 线程安全考虑
string在多线程环境中的行为:
- 同时读取是安全的
- 任何修改操作都需要同步
- c_str()返回的指针在多线程环境下需要特别小心
线程安全建议:
- 对于共享字符串,使用互斥锁保护
- 考虑使用不可变字符串设计
- 线程局部存储可以减少锁竞争
10. 常见问题与解决方案
10.1 典型错误案例
cpp复制// 错误1:越界访问
std::string s = "hello";
char c = s[10]; // 未定义行为
// 错误2:迭代器失效
auto it = s.begin();
s += " world"; // 可能导致it失效
*it = 'H'; // 危险!
// 错误3:错误理解容量
std::string s;
s.reserve(10);
s[5] = 'x'; // 未定义行为,size()仍为0
调试技巧:
- 使用at()而非[]进行边界检查
- 在修改操作后不要保留旧的迭代器
- 区分size()和capacity()的概念
10.2 性能陷阱
-
字符串拼接的多次分配:
cpp复制// 低效 std::string result; for(int i=0; i<10000; ++i) { result += "x"; // 可能多次重分配 } // 高效 std::string result; result.reserve(10000); for(int i=0; i<10000; ++i) { result += "x"; } -
不必要的临时字符串:
cpp复制// 低效 std::string s1 = "Hello"; std::string s2 = "World"; std::string s3 = s1 + " " + s2; // 创建临时对象 // 更高效 std::string s3; s3.reserve(s1.size() + s2.size() + 1); s3.append(s1).append(" ").append(s2); -
错误的查找顺序:
cpp复制// 低效 if(str.find("long-pattern") != std::string::npos || str.find("short") != std::string::npos) { // 先检查较长的模式可能更慢 }
优化建议:
- 使用reserve()预分配空间
- 避免中间临时字符串
- 考虑使用string_view减少拷贝(C++17)
- 对性能关键路径进行基准测试
11. 现代C++中的字符串增强
11.1 string_view(C++17)
string_view提供了对字符串的非拥有视图:
cpp复制#include <string_view>
void process(std::string_view sv) {
// 可以接受string、char*等参数
if(sv.starts_with("Hello")) {
// ...
}
}
std::string s = "Hello World";
char buffer[] = "Hello C++";
process(s); // OK
process(buffer); // OK
process("Hello"); // OK
string_view优势:
- 避免不必要的字符串拷贝
- 统一接口,可以接受多种字符串类型
- 提供类似string的接口,使用方便
注意事项:
- string_view不管理生命周期
- 必须确保底层字符串在string_view使用期间有效
- 不适合作为长期存储
11.2 格式化库(C++20)
新的格式化工具:
cpp复制#include <format>
std::string s = std::format("Hello {}!", "World"); // "Hello World!"
double pi = 3.1415926;
std::string s2 = std::format("{:.2f}", pi); // "3.14"
格式化特性:
- 类型安全,比sprintf更安全
- 性能优于stringstream
- 支持自定义类型的格式化
11.3 其他C++20增强
-
starts_with()/ends_with():
cpp复制std::string s = "Hello World"; bool b1 = s.starts_with("Hello"); // true bool b2 = s.ends_with("World"); // true -
contains():
cpp复制bool b3 = s.contains("lo W"); // true -
字符串拼接简化:
cpp复制using namespace std::literals; auto s = "Hello"s + " " + "World"; // 更简洁的字面量拼接
这些新方法使字符串操作更加直观和方便,减少了样板代码的编写。
12. 实战案例:字符串工具函数实现
12.1 常用工具函数
cpp复制// 去除字符串两端空格
std::string trim(const std::string& s) {
auto start = s.begin();
while(start != s.end() && std::isspace(*start)) {
start++;
}
auto end = s.end();
do {
end--;
} while(std::distance(start, end) > 0 && std::isspace(*end));
return std::string(start, end + 1);
}
// 字符串分割
std::vector<std::string> split(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while(std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
// 字符串连接
std::string join(const std::vector<std::string>& parts, const std::string& glue) {
if(parts.empty()) return "";
std::string result;
size_t total = glue.size() * (parts.size() - 1);
for(const auto& part : parts) {
total += part.size();
}
result.reserve(total);
result = parts[0];
for(size_t i = 1; i < parts.size(); ++i) {
result += glue + parts[i];
}
return result;
}
实现要点:
- trim()函数考虑了性能,避免不必要的拷贝
- split()使用istringstream处理各种空白字符
- join()预先计算总长度,避免多次重分配
12.2 高级应用:模板字符串替换
cpp复制std::string format_template(
const std::string& template_str,
const std::unordered_map<std::string, std::string>& values)
{
std::string result;
result.reserve(template_str.size() * 2); // 启发式预留空间
size_t pos = 0;
while(pos < template_str.size()) {
size_t var_start = template_str.find("${", pos);
if(var_start == std::string::npos) {
result.append(template_str, pos, template_str.size() - pos);
break;
}
result.append(template_str, pos, var_start - pos);
size_t var_end = template_str.find("}", var_start + 2);
if(var_end == std::string::npos) {
throw std::runtime_error("Unclosed variable");
}
std::string var_name = template_str.substr(
var_start + 2, var_end - var_start - 2);
auto it = values.find(var_name);
if(it != values.end()) {
result += it->second;
}
pos = var_end + 1;
}
return result;
}
使用示例:
cpp复制std::unordered_map<std::string, std::string> vars = {
{"name", "Alice"},
{"age", "30"}
};
std::string result = format_template(
"Hello ${name}, you are ${age} years old.", vars);
// "Hello Alice, you are 30 years old."
这个实现展示了如何高效地处理字符串扫描和替换,同时考虑了性能和错误处理。
13. 跨平台与编码问题
13.1 编码处理实践
处理多平台编码问题:
cpp复制// UTF-8与宽字符转换(C++11)
std::wstring utf8_to_wide(const std::string& utf8) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.from_bytes(utf8);
}
std::string wide_to_utf8(const std::string& wide) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.to_bytes(wide);
}
// 编码检测(简化版)
bool is_utf8(const std::string& str) {
const unsigned char* bytes = (const unsigned char*)str.data();
while(*bytes) {
if((*bytes & 0x80) == 0) {
bytes++;
} else if((*bytes & 0xE0) == 0xC0) {
if((bytes[1] & 0xC0) != 0x80) return false;
bytes += 2;
} else if((*bytes & 0xF0) == 0xE0) {
if((bytes[1] & 0xC0) != 0x80 || (bytes[2] & 0xC0) != 0x80) return false;
bytes += 3;
} else if((*bytes & 0xF8) == 0xF0) {
if((bytes[1] & 0xC0) != 0x80 || (bytes[2] & 0xC0) != 0x80 ||
(bytes[3] & 0xC0) != 0x80) return false;
bytes += 4;
} else {
return false;
}
}
return true;
}
编码处理建议:
- 内部统一使用UTF-8编码
- 与外部系统交互时明确编码约定
- 对于复杂的编码转换,考虑使用ICU等专业库
13.2 平台相关注意事项
不同平台的字符串处理差异:
-
Windows平台:
- 默认使用UTF-16编码
- 控制台输出可能需要特殊处理
- 文件路径使用宽字符API
-
Linux/macOS平台:
- 默认使用UTF-8编码
- 文件系统通常使用UTF-8
跨平台代码建议:
cpp复制#ifdef _WIN32
std::wstring utf8_to_platform(const std::string& utf8) {
// Windows实现
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(utf8);
}
#else
std::string utf8_to_platform(const std::string& utf8) {
// 非Windows直接返回
return utf8;
}
#endif
14. 字符串性能测试与分析
14.1 常见操作性能对比
测试不同字符串操作的性能:
cpp复制#include <chrono>
#include <vector>
void test_performance() {
const int iterations = 1000000;
// 测试1:拼接性能
{
auto start = std::chrono::high_resolution_clock::now();
std::string s;
for(int i = 0; i < iterations; ++i) {
s += "test";
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "+= time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms\n";
}
// 测试2:预分配后拼接
{
auto start = std::chrono::high_resolution_clock::now();
std::string s;
s.reserve(iterations * 4);
for(int i = 0; i < iterations; ++i) {
s += "test";
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "reserve+= time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms\n";
}
// 测试3:append性能
{
auto start = std::chrono::high_resolution_clock::now();
std::string s;
s.reserve(iterations * 4);
for(int i = 0; i < iterations; ++i) {
s.append("test");
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "append time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms\n";
}
}
典型测试结果(仅供参考):
- 普通+=操作:120ms
- 预分配后+=:45ms
- append操作:40ms
14.2 小字符串优化分析
大多数现代C++实现都采用了小字符串优化(SSO):
cpp复制void test_sso() {
std::string small = "short"; // 可能使用SSO
std::string large = "this is a very long string that definitely won't fit in SSO buffer";
std::cout << "small string capacity: " << small.capacity() << "\n";
std::cout << "large string capacity: " << large.capacity() << "\n";
// 通过内存地址分析
std::cout << "small string data address: " << (void*)small.data() << "\n";
std::cout << "small string object address: " << &small << "\n";
std::cout << "large string data address: " << (void*)large.data() << "\n";
std::cout << "large string object address: " << &large << "\n";
}
SSO特点:
- 小字符串(通常15-22字符)直接存储在对象内部,不分配堆内存
- 减少内存分配开销
- 提高缓存局部性
15. 第三方字符串库简介
15.1 Abseil字符串库
Google Abseil库提供了增强的字符串功能:
cpp复制#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
// 高效拼接
std::string s = absl::StrCat("The answer is ", 42, "!");
// 字符串分割
std::vector<std::string> parts = absl::StrSplit("a,b,c", ',');
Abseil优势:
- 更高效的字符串拼接
- 丰富的字符串处理工具
- 与Google内部实现一致
15.2 Boost字符串算法
Boost提供了大量字符串算法:
cpp复制#include <boost/algorithm/string.hpp>
std::string s = "Hello World";
// 大小写转换
boost::to_upper(s); // "HELLO WORLD"
// 修剪
boost::trim(s); // 去除两端空格
// 谓词操作
bool all_alpha = boost::all(s, boost::is_alpha());
Boost特点:
- 功能全面
- 良好的跨平台支持
- 与STL风格一致
15.3 其他选择
- ICU:专业的Unicode支持
- Qt QString:适合Qt应用程序
- folly fbstring:Facebook的高性能字符串实现
选择建议:
- 普通应用使用标准库足够
- 需要Unicode支持考虑ICU
- 性能关键应用可以考虑Abseil或folly
16. 字符串在项目中的应用模式
16.1 配置解析
典型的配置文件解析实现:
cpp复制class ConfigParser {
public:
void parse(const std::string& filename) {
std::ifstream file(filename);
std::string line;
while(std::getline(file, line)) {
// 跳过注释和空行
line = trim(line);
if(line.empty() || line[0] == '#') continue;
// 解析键值对
size_t pos = line.find('=');
if(pos == std::string::npos) {
throw std::runtime_error("Invalid config line: " + line);
}
std::string key = trim(line.substr(0, pos));
std::string value = trim(line.substr(pos + 1));
config_[key] = value;
}
}
std::string get(const std::string& key, const std::string& default_value = "") {
auto it = config_.find(key);
return it != config_.end() ? it->second : default_value;
}
private:
std::unordered_map<std::string, std::string> config_;
static std::string trim(const std::string& s) {
// 实现省略
}
};
设计要点:
- 简单的键值对格式
- 支持注释和空格
- 提供默认值机制
16.2 日志系统实现
基于字符串的简单日志系统:
cpp复制class Logger {
public:
enum Level { DEBUG, INFO, WARNING, ERROR };
Logger(const std::string& filename) : file_(filename) {}
void log(Level level, const std::string& message) {
std::string timestamp = get_current_time();
std::string level_str = level_to_string(level);
std::string entry = absl::StrFormat("[%s] [%s] %s\n",
timestamp, level_str, message);
std::lock_guard<std::mutex> lock(mutex_);
file_ << entry;
}
template <typename... Args>
void log(Level level, const std::string& format, Args... args) {
log(level, absl::StrFormat(format, args...));
}
private:
std::ofstream file_;
std::mutex mutex_;
std::string get_current_time() {
// 实现省略
}
std::string level_to_string(Level level) {
static const char* names[] = {"DEBUG", "INFO", "WARNING", "ERROR"};
return names[level];
}
};
使用示例:
cpp复制Logger logger("app.log");
logger.log(Logger::INFO, "System started");
logger.log(Logger::ERROR, "Failed to open file: %s", filename);
日志系统优化:
- 使用格式化而非字符串拼接
- 线程安全设计
- 支持多种日志级别
17. 字符串与模板元编程
17.1 编译期字符串处理
C++11后可以在编译期处理字符串:
cpp复制template <size_t N>
struct FixedString {
char data[N] = {};
constexpr FixedString(const char (&str)[N]) {
std::copy_n(str, N, data);
}
constexpr size_t size() const { return N - 1; }
};
template <FixedString Str>
constexpr auto make_string() {
return Str;
}
// 编译期字符串连接
template <FixedString A, FixedString B>
struct Concat {
static constexpr char value[sizeof(A.data) + sizeof(B.data) - 1] = {};
constexpr Concat() {
std::copy_n(A.data, A.size(), value);
std::copy_n(B.data, B.size(), value + A.size());
}
};
constexpr auto hello = make_string<"Hello">();
constexpr auto world = make_string<" World">();
constexpr auto hello_world = Concat<hello, world>{};
应用场景:
- 编译期字符串验证
- 模板元编程
- 嵌入式系统开发
17.2 类型安全的字符串格式化
结合模板的类型安全格式化:
cpp复制template <typename... Args>
std::string format_checked(const std::string& fmt, Args... args) {
const size_t expected = count_placeholders(fmt);
const size_t actual = sizeof...(Args);
if(expected != actual) {
throw std::runtime_error(absl::StrCat(
"Placeholder count mismatch. Expected ", expected,
", got ", actual));
}
return absl::StrFormat(fmt, args...);
}
size_t count_placeholders(const std::string& fmt) {
size_t count = 0;
bool in_placeholder = false;
for(char c : fmt) {
if(c == '%') {
if(in_placeholder) {
in_placeholder = false;
} else {
in_placeholder = true;
count++;
}
} else if(in_placeholder) {
in_placeholder = false;
}
}
return count;
}
这个实现提供了额外的类型安全检查,确保格式字符串与参数数量匹配。
18. 字符串与多语言国际化
18.1 国际化基础实现
简单的多语言支持系统:
cpp复制class I18n {
public:
I18n(const std::string& locale) {
load_translations(locale);
}
std::string translate(const std::string& key) {
auto it = translations_.find(key);
return it != translations_.end() ? it->second : key;
}
template <typename... Args>
std::string translate(const std::string& key, Args... args) {
std::string format = translate(key);
return absl::StrFormat(format, args...);
}
private:
std::unordered_map<std::string, std::string> translations_;
void load_translations(const std::string& locale) {
std::string filename = absl::StrCat("lang_", locale, ".txt");
std::ifstream file(filename);
std::string line;
while(std::getline(file, line)) {
size_t pos = line.find('=');
if(pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
translations_[key] = value;
}
}
}
};
使用示例:
cpp复制I18n i18n("zh_CN");
std::string greeting = i18n.translate("hello"); // 返回中文翻译
std::string message = i18n.translate("welcome", username); // 格式化翻译
国际化建议:
- 使用标准的locale标识符
- 将翻译文本外部化
- 考虑使用gettext等成熟方案
18.2 Unicode处理实践
正确处理Unicode字符串:
cpp复制// 计算UTF-8字符串的字符数(非字节数)
size_t utf8_length(const std::string& utf8) {
size_t length = 0;
const char* ptr = utf8.data();
const char* end = ptr + utf8.size();
while(ptr < end) {
unsigned char c = *ptr;
if((c & 0x80) == 0) {
ptr += 1;
} else if((c & 0xE0) == 0xC0) {
ptr += 2;
} else if((c & 0xF0) == 0xE0) {
ptr += 3;
} else if((c & 0xF8) == 0xF0) {
ptr += 4;
} else {
// 非法UTF-8
