本 CString 类是基于 C++ 标准库std::string封装的轻量级字符串工具类,兼容常用字符串操作需求,无需手动管理内存,安全稳定。类提供完整的构造、赋值、拼接、比较、截取、查找、格式化等功能,包含GetAt、[]运算符重载,支持读写访问;实现Left/Right/Mid截取、Trim去空格、大小写转换、Format格式化等实用接口。所有接口做了越界、空指针等安全校验,避免程序崩溃,整体简洁易用、鲁棒性强,适用于日常字符串处理场景。
先上测试用例:
#include "CString.h"
#include <cstdio>
// 修复后的宏定义
#define TEST_CASE(str) printf("\n=== 测试: %s ===\n", str);
#define CHECK(a) printf("结果: %s\n", a);
int main()
{
//=========================
// 1. 构造函数测试
//=========================
TEST_CASE("构造函数");
CString s1;
CString s2("hello");
CString s3('A');
CString s4(s2);
CHECK(s1.GetBuffer(0));
CHECK(s2.GetBuffer(0));
CHECK(s3.GetBuffer(0));
CHECK(s4.GetBuffer(0));
//=========================
// 2. 基础属性
//=========================
TEST_CASE("基础属性");
char buf[64];
sprintf(buf, "长度: %d", s2.GetLength());
CHECK(buf);
sprintf(buf, "是否空: %s", s1.IsEmpty() ? "是" : "否");
CHECK(buf);
s1.Empty();
sprintf(buf, "清空后是否空: %s", s1.IsEmpty() ? "是" : "否");
CHECK(buf);
//=========================
// 3. 字符访问 GetAt / []
//=========================
TEST_CASE("字符访问");
CString s5("test123");
sprintf(buf, "%c", s5.GetAt(0));
CHECK(buf);
sprintf(buf, "%c", s5[1]);
CHECK(buf);
s5[0] = 'T';
CHECK(s5.GetBuffer(0));
//=========================
// 4. 赋值 =
//=========================
TEST_CASE("赋值");
CString s6;
s6 = "assign test";
CHECK(s6.GetBuffer(0));
s6 = 'X';
CHECK(s6.GetBuffer(0));
s6 = s5;
CHECK(s6.GetBuffer(0));
//=========================
// 5. 拼接 +=
//=========================
TEST_CASE("+=");
CString s7("abc");
s7 += "123";
s7 += '4';
s7 += CString("567");
CHECK(s7.GetBuffer(0));
//=========================
// 6. 截取 Left / Right / Mid
//=========================
TEST_CASE("Left / Right / Mid");
CString s8("123456789");
CHECK(s8.Left(3).GetBuffer(0));
CHECK(s8.Right(3).GetBuffer(0));
CHECK(s8.Mid(2).GetBuffer(0));
CHECK(s8.Mid(2, 4).GetBuffer(0));
//=========================
// 7. 查找 Find
//=========================
TEST_CASE("Find");
CString s9("abc123abc456");
sprintf(buf, "查找字符: %d", s9.Find('c'));
CHECK(buf);
sprintf(buf, "查找串: %d", s9.Find("123"));
CHECK(buf);
sprintf(buf, "从位置5查找: %d", s9.Find("abc", 5));
CHECK(buf);
sprintf(buf, "查找CString: %d", s9.Find(CString("456")));
CHECK(buf);
sprintf(buf, "查找CString: %d", s9.Find(CString("xx")));
CHECK(buf);
//=========================
// 8. Trim 系列
//=========================
TEST_CASE("Trim");
CString s10(" abc def \r\n");
s10.TrimLeft();
s10.TrimRight();
sprintf(buf, "TrimLeft+Right: %s", s10.GetBuffer(0));
CHECK(buf);
CString s11(" xyz ");
s11.Trim();
sprintf(buf, "Trim: %s", s11.GetBuffer(0));
CHECK(buf);
//=========================
// 9. 大小写 MakeLower / MakeUpper
//=========================
TEST_CASE("大小写");
CString s12("AbCdEfG");
s12.MakeUpper();
sprintf(buf, "大写: %s", s12.GetBuffer(0));
CHECK(buf);
s12.MakeLower();
sprintf(buf, "小写: %s", s12.GetBuffer(0));
CHECK(buf);
//=========================
// 10. GetBuffer
//=========================
TEST_CASE("GetBuffer");
CString s13("buffer test");
const char* pConst = s13.GetBuffer(0);
char* pNoConst = s13.GetBuffer(0);
CHECK(pConst);
CHECK(pNoConst);
//=========================
// 11. Format
//=========================
TEST_CASE("Format");
CString s14;
s14.Format("编号:%d,内容:%s", 100, "test string");
CHECK(s14.GetBuffer(0));
//=========================
// 12. 比较 == != > < >= <=
//=========================
TEST_CASE("比较运算符");
CString a("aaa"), b("bbb");
sprintf(buf, "==: %d", a == b);
CHECK(buf);
sprintf(buf, "!=: %d", a != b);
CHECK(buf);
sprintf(buf, "> : %d", a > b);
CHECK(buf);
sprintf(buf, "< : %d", a < b);
CHECK(buf);
sprintf(buf, ">=: %d", a >= b);
CHECK(buf);
sprintf(buf, "<=: %d", a <= b);
CHECK(buf);
sprintf(buf, "== const char*: %d", a == "aaa");
CHECK(buf);
//=========================
// 13. 全局 + 运算符
//=========================
TEST_CASE("+ 拼接");
CString x("111"), y("222");
CString r1 = x + y;
CString r2 = x + "333";
CString r3 = "444" + y;
CString r4 = x + '5';
CHECK(r1.GetBuffer(0));
CHECK(r2.GetBuffer(0));
CHECK(r3.GetBuffer(0));
CHECK(r4.GetBuffer(0));
printf("\n==== 所有函数测试完成 ====\n");
return 0;
}
测试结果:
=== 测试: 构造函数 ===
结果:
结果: hello
结果: A
结果: hello
=== 测试: 基础属性 ===
结果: 长度: 5
结果: 是否空: 是
结果: 清空后是否空: 是
=== 测试: 字符访问 ===
结果: t
结果: e
结果: Test123
=== 测试: 赋值 ===
结果: assign test
结果: X
结果: Test123
=== 测试: += ===
结果: abc1234567
=== 测试: Left / Right / Mid ===
结果: 123
结果: 789
结果: 3456789
结果: 3456
=== 测试: Find ===
结果: 查找字符: 2
结果: 查找串: 3
结果: 从位置5查找: 6
结果: 查找CString: 9
结果: 查找CString: -1
=== 测试: Trim ===
结果: TrimLeft+Right: abc def
结果: Trim: xyz
=== 测试: 大小写 ===
结果: 大写: ABCDEFG
结果: 小写: abcdefg
=== 测试: GetBuffer ===
结果: buffer test
结果: buffer test
=== 测试: Format ===
结果: 编号:100,内容:test string
=== 测试: 比较运算符 ===
结果: ==: 0
结果: !=: 1
结果: > : 0
结果: < : 1
结果: >=: 0
结果: <=: 1
结果: == const char*: 1
=== 测试: + 拼接 ===
结果: 111222
结果: 111333
结果: 444222
结果: 1115
==== 所有函数测试完成 ====
CString.h
cpp
#ifndef CSTRING_H
#define CSTRING_H
#include <string>
// 字符串封装类(模拟MFC CString)
class CString
{
private:
std::string m_str; // 内部存储字符串的成员变量
public:
// 构造与析构
CString(); // 默认构造(空字符串)
CString(const CString& str); // 拷贝构造
CString(const char* lpsz); // 从C字符串构造
CString(char ch); // 从单个字符构造
~CString(); // 析构函数
// 基础属性操作
int GetLength() const; // 获取字符串长度
bool IsEmpty() const; // 判断是否为空
void Empty(); // 清空字符串
// 字符访问
char GetAt(int iIndex) const; // 获取指定位置字符(带检查)
char operator[](int iIndex) const; // 只读下标访问
char& operator[](int iIndex); // 可写下标访问
// 赋值运算符
CString& operator=(const CString& str);
CString& operator=(const char* lpsz);
CString& operator=(char ch);
// 追加运算符
CString& operator+=(const CString& str);
CString& operator+=(const char* lpsz);
CString& operator+=(char ch);
// 字符串截取
CString Left(int nCount) const; // 取左侧n个字符
CString Right(int nCount) const; // 取右侧n个字符
CString Mid(int nFirst) const; // 从n开始取到末尾
CString Mid(int nFirst, int nCount) const; // 从n取count个字符
// 查找函数
int Find(char ch) const; // 查找字符
int Find(const char* lpszSub) const; // 查找子串
int Find(const char* lpszSub, int nStart) const; // 从指定位置查找
int Find(const CString& strSub) const; // 查找CString子串
// 修剪空白
void TrimLeft(); // 去除左侧空白
void TrimRight(); // 去除右侧空白
void Trim(); // 去除左右空白
// 大小写转换
void MakeLower(); // 转为小写
void MakeUpper(); // 转为大写
// 缓冲区操作
char* GetBuffer(int = 0); // 获取可写缓冲区
const char* GetBuffer(int = 0) const; // 获取只读缓冲区
void Format(const char* pstrFormat, ...); // 格式化字符串
// 比较运算符(CString)
bool operator==(const CString& str) const;
bool operator!=(const CString& str) const;
bool operator>(const CString& str) const;
bool operator<(const CString& str) const;
bool operator>=(const CString& str) const;
bool operator<=(const CString& str) const;
// 比较运算符(C字符串)
bool operator==(const char* lpsz) const;
bool operator!=(const char* lpsz) const;
bool operator>(const char* lpsz) const;
bool operator<(const char* lpsz) const;
bool operator>=(const char* lpsz) const;
bool operator<=(const char* lpsz) const;
// 友元拼接运算符
friend CString operator+(const CString& s1, const CString& s2);
friend CString operator+(const CString& s1, const char* s2);
friend CString operator+(const char* s1, const CString& s2);
friend CString operator+(const CString& s1, char ch);
};
#endif
CString.cpp
cpp
/*
本 CString 类是基于 C++ 标准库std::string封装的轻量级字符串工具类,兼容常用字符串操作需求,1
无需手动管理内存,安全稳定。类提供完整的构造、赋值、拼接、比较、截取、查找、格式化等功能,包含GetAt、
[]运算符重载,支持读写访问;实现Left/Right/Mid截取、Trim去空格、大小写转换、Format格式化等实用接口。
所有接口做了越界、空指针等安全校验,避免程序崩溃,整体简洁易用、鲁棒性强,适用于日常字符串处理场景。
*/
#include "CString.h"
#include <cstring>
#include <cstdarg>
#include <cstdio>
#include <cctype>
#include <memory>
#include <stdexcept> // 加这个!
// 默认构造:空字符串
CString::CString()
{
}
// 拷贝构造
CString::CString(const CString& str)
{
m_str = str.m_str;
}
// 从 C 字符串构造
CString::CString(const char* lpsz)
{
if(lpsz)
m_str = lpsz;
}
// 从单个字符构造
CString::CString(char ch)
{
m_str = ch;
}
// 析构函数
CString::~CString()
{
}
// 获取字符串长度
int CString::GetLength() const
{
return (int)m_str.size();
}
// 判断是否为空
bool CString::IsEmpty() const
{
return m_str.empty();
}
// 清空字符串
void CString::Empty()
{
m_str.clear();
}
// 获取指定位置字符
char CString::GetAt(int iIndex) const
{
if(iIndex < 0 || iIndex >= GetLength())
return 0;
return m_str[iIndex];
}
// 只读下标访问
char CString::operator[](int iIndex) const
{
return GetAt(iIndex);
}
// 可写下标访问
char& CString::operator[](int iIndex)
{
if (iIndex < 0 || iIndex >= GetLength())
{
throw std::out_of_range("CString[] index out of range");
}
return m_str[iIndex];
}
// 赋值运算符(CString)
CString& CString::operator=(const CString& str)
{
m_str = str.m_str;
return *this;
}
// 赋值运算符(C字符串)
CString& CString::operator=(const char* lpsz)
{
if(lpsz)
m_str = lpsz;
else
m_str.clear();
return *this;
}
// 赋值运算符(单个字符)
CString& CString::operator=(char ch)
{
m_str = ch;
return *this;
}
// 追加字符串(CString)
CString& CString::operator+=(const CString& str)
{
m_str += str.m_str;
return *this;
}
// 追加字符串(C字符串)
CString& CString::operator+=(const char* lpsz)
{
if(lpsz)
m_str += lpsz;
return *this;
}
// 追加单个字符
CString& CString::operator+=(char ch)
{
m_str += ch;
return *this;
}
// 取左边nCount个字符
CString CString::Left(int nCount) const
{
if(nCount <= 0)
return CString();
if(nCount > GetLength())
nCount = GetLength();
return CString(m_str.substr(0, nCount).c_str());
}
// 取右边nCount个字符
CString CString::Right(int nCount) const
{
int len = GetLength();
if(nCount <= 0 || len == 0)
return CString();
if(nCount > len)
nCount = len;
return CString(m_str.substr(len - nCount, nCount).c_str());
}
// 从nFirst取到末尾
CString CString::Mid(int nFirst) const
{
int len = GetLength();
if(nFirst < 0 || nFirst >= len)
return CString();
return CString(m_str.substr(nFirst).c_str());
}
// 从nFirst取nCount个字符
CString CString::Mid(int nFirst, int nCount) const
{
int len = GetLength();
if(nFirst < 0 || nCount <= 0 || nFirst >= len)
return CString();
if(nFirst + nCount > len)
nCount = len - nFirst;
return CString(m_str.substr(nFirst, nCount).c_str());
}
// 查找单个字符
int CString::Find(char ch) const
{
size_t pos = m_str.find(ch);
if(pos == std::string::npos)
return -1;
return (int)pos;
}
// 查找子串(C字符串)
int CString::Find(const char* lpszSub) const
{
if(!lpszSub || *lpszSub == 0)
return -1;
size_t pos = m_str.find(lpszSub);
if(pos == std::string::npos)
return -1;
return (int)pos;
}
// 从指定位置查找子串(C字符串)
int CString::Find(const char* lpszSub, int nStart) const
{
if(!lpszSub) return -1;
if(*lpszSub == 0) return 0;
if(nStart < 0 || nStart >= GetLength())
return -1;
size_t pos = m_str.find(lpszSub, nStart);
if(pos == std::string::npos)
return -1;
return (int)pos;
}
// 查找子串(CString)
int CString::Find(const CString& strSub) const
{
return Find(strSub.GetBuffer(0));
}
// 获取缓冲区
char* CString::GetBuffer(int)
{
return (char*)m_str.c_str();
}
// 获取只读缓冲区
const char* CString::GetBuffer(int) const
{
return m_str.c_str();
}
void CString::Format(const char* pstrFormat, ...)
{
if (pstrFormat == nullptr)
{
m_str.clear();
return;
}
va_list argList;
va_start(argList, pstrFormat);
// 1. 先复制一份用来计算长度
va_list argsCopy;
va_copy(argsCopy, argList);
int len = vsnprintf(nullptr, 0, pstrFormat, argsCopy);
va_end(argsCopy);
if (len < 0) {
va_end(argList); // 这里必须结束主列表
m_str.clear();
return;
}
// 2. 再复制一份用来真正输出
std::unique_ptr<char[]> buf(new char[len + 1]);
va_copy(argsCopy, argList); // ✅ 重新复制
vsnprintf(buf.get(), len + 1, pstrFormat, argsCopy);
va_end(argsCopy);
// 最后结束主列表
va_end(argList);
m_str = buf.get();
}
// 相等比较
bool CString::operator==(const CString& str) const
{
return m_str == str.m_str;
}
// 不等比较
bool CString::operator!=(const CString& str) const
{
return m_str != str.m_str;
}
// 与C字符串相等比较
bool CString::operator==(const char* lpsz) const
{
if(!lpsz)
return IsEmpty();
return m_str == lpsz;
}
// 与C字符串不等比较
bool CString::operator!=(const char* lpsz) const
{
return !(*this == lpsz);
}
// 字符串拼接(CString + CString)
CString operator+(const CString& s1, const CString& s2)
{
CString s;
s.m_str = s1.m_str + s2.m_str;
return s;
}
// 字符串拼接(CString + C字符串)
CString operator+(const CString& s1, const char* s2)
{
CString s;
if(s2)
{
s.m_str = s1.m_str + s2;
}
else
{
s.m_str = s1.m_str;
}
return s;
}
// 字符串拼接(C字符串 + CString)
CString operator+(const char* s1, const CString& s2)
{
CString s;
if(s1)
{
s.m_str = s1 + s2.m_str;
}
else
{
s.m_str = s2.m_str;
}
return s;
}
// 字符串拼接(CString + 字符)
CString operator+(const CString& s1, char ch)
{
CString s;
s.m_str = s1.m_str + ch;
return s;
}
// 去除左侧空白
void CString::TrimLeft()
{
size_t start = m_str.find_first_not_of(" \t\n\r");
if(start == std::string::npos)
m_str.clear();
else
m_str = m_str.substr(start);
}
// 去除右侧空白
void CString::TrimRight()
{
size_t end = m_str.find_last_not_of(" \t\n\r");
if(end == std::string::npos)
m_str.clear();
else
m_str.erase(end + 1);
}
// 去除左右空白
void CString::Trim()
{
TrimLeft();
TrimRight();
}
// 转小写
void CString::MakeLower()
{
int len = GetLength();
for (int i = 0; i < len; i++)
{
m_str[i] = tolower(m_str[i]);
}
}
// 转大写
void CString::MakeUpper()
{
int len = GetLength();
for (int i = 0; i < len; i++)
{
m_str[i] = toupper(m_str[i]);
}
}
// 大于比较(CString)
bool CString::operator>(const CString& str) const
{
return m_str > str.m_str;
}
// 小于比较(CString)
bool CString::operator<(const CString& str) const
{
return m_str < str.m_str;
}
// 大于等于(CString)
bool CString::operator>=(const CString& str) const
{
return m_str >= str.m_str;
}
// 小于等于(CString)
bool CString::operator<=(const CString& str) const
{
return m_str <= str.m_str;
}
// 大于比较(C字符串)
bool CString::operator>(const char* lpsz) const
{
if(!lpsz)
return !IsEmpty();
return m_str > lpsz;
}
// 小于比较(C字符串)
bool CString::operator<(const char* lpsz) const
{
if(!lpsz)
return false;
return m_str < lpsz;
}
// 大于等于(C字符串)
bool CString::operator>=(const char* lpsz) const
{
if(!lpsz)
return !IsEmpty();
return m_str >= lpsz;
}
// 小于等于(C字符串)
bool CString::operator<=(const char* lpsz) const
{
if(!lpsz)
return false;
return m_str <= lpsz;
}