QT新手开发中遇到的一些字符串转换问题和常见错误

上个任务是在QT工程中做一个多语言切换的功能,其中针对不同控件做了不少接口,涉及到很多字符串类型转换问题,不堪其扰,抽空系统的整理一下。

1.常用的三种类型:char, const char, QString

//char* C风格字符串
char* str = "Hello";  // 可修改指针,但指向的内容可能是只读的
char str2[] = "World"; // 可修改的字符数组

// 特点:
// - 以 null 字符('\0')结尾
// - 需要手动管理内存
// - 标准C库函数操作(strcpy, strlen, strcat等)
// - 可能产生缓冲区溢出

//const char* 常量C风格字符串
const char* str = "Hello";  // 指针和内容都不可修改

// 特点:
// - 字符串字面量通常是 const char*
// - 更安全,防止意外修改
// - 适合作为函数参数传递只读字符串

//QString QT字符串
QString str = "Hello";  // Qt的字符串类

// 特点:
// - Unicode支持(内部UTF-16编码)
// - 自动内存管理
// - 丰富的成员函数
// - 支持国际化
// - 线程安全

2.互相转换方法----特指中文字符串转化的优化

char*/const char* → QString

//比如现在有一个字符串
const char lang_text[10];
strcpy(lang_text, "你好世界");

//最常用的:
QString str = QString::fromUtf8(lang_text);

//如果原字符串直接是中文,可能涉及到GBK编码
QTextCodec* codec = QTextCodec::codecForName("GBK");
QSting str = codec->toUnicode("中文字符串");

QString → char*/const char*

QString qstr = "你好世界"

char* utf8_str = qstr.toUtf8().data();
const char* utf8_str = qstr.toUtf8().constData();

3.一些开发过程中遇到的问题

最常见的遇到一些从文件读取字符串时,可能出现各种状况

首先是字符比较、拼接、字符串长度等的对比:

字符比较
//如果是C风格字符串,大部分使用strcmp()函数
char text1 = "你好世界";
char text2[10];
if(strcmp(text1, text2)==0) ? print("字符相同”) : print("字符不同”);

//如果是QString,则简单些
QString text1 = "你好世界";
QString text2;
if(text1 == text2) ? print("字符相同”) : print("字符不同”);

字符拼接
char text1 = "你好";
char text2 = "世界";
strcat(text1, text2);
QString str = text1 + text2;
QString str = "Hello" + QString(" ") + "world" + "!";

获取字符串长度
int len = strlen(text1);
QString str = "Hello";
int len = str.length();
int size = str.size();   // 同上

字符串查找
char* str = "Hello World";
char* found = strstr(str, "World");  // 返回指针
if (found != nullptr) {
    int position = found - str;  // 计算位置 指针算术:当两个指针相减时,结果表示的是这两个指针之间相隔的元素个数,而不是字节数。 实际上 = 地址差/sizeof()
}

QString str = "Hello World";
int position = str.indexOf("World");  // 返回位置,-1表示未找到
if (position != -1) {
    // 找到了
}
// 反向查找
int lastPos = str.lastIndexOf("o");

// 检查是否包含
if (str.contains("World")) {
    // 包含子字符串
}

字符串截取
char* str = "Hello World";
char sub[20];

// 需要手动复制
strncpy(sub, str + 6, 5);  // 从位置6开始,取5个字符
sub[5] = '\0';  // 手动添加结束符

QString str = "Hello World";
// 直接使用mid函数
QString sub = str.mid(6, 5);  // 从位置6开始,取5个字符

// 其他截取方法
QString left = str.left(5);   // 前5个字符:"Hello"
QString right = str.right(5); // 后5个字符:"World"

字符串修改
char str[20] = "Hello World";
// 需要strcpy或手动修改
strcpy(str, "New Text");  // 完全替换
// 修改单个字符
str[0] = 'h';  // 改为"hello World"

QString str = "Hello World";
// 完全替换
str = "New Text";
// 使用replace替换部分内容
str.replace("Hello", "Hi");  // 变为 "Hi World"
// 修改单个字符
str[0] = 'h';  // 改为"hello World"

字符串格式化
char buffer[100];
int num = 42;
float pi = 3.14;
// 使用sprintf(不安全,可能缓冲区溢出)
sprintf(buffer, "Number: %d, Pi: %.2f", num, pi);
// 较安全的snprintf
snprintf(buffer, sizeof(buffer), "Number: %d, Pi: %.2f", num, pi);

int num = 42;
float pi = 3.14;
// 使用arg函数(安全,类型安全)
QString str = QString("Number: %1, Pi: %2").arg(num).arg(pi, 0, 'f', 2);//%1 %2代表第一个 第二个占位符 
//arg(pi, 0, 'f', 2)
// 参数解释:
// arg(值, 最小宽度, 格式, 精度)
// - 0: 最小宽度(0表示不填充)多的用0填充
// - 'f': 固定小数格式
// - 2: 保留2位小数

// 或者
QString str = QString("Number: %1, Pi: %2")
                .arg(num)
                .arg(pi, 0, 'f', 2);

空字符串检查
char* str = "Hello";
// 需要检查nullptr和空字符串
if (str != nullptr && str[0] != '\0') {
    // 非空
}

QString str = "Hello";
// 直接使用isEmpty()
if (!str.isEmpty()) {
    // 非空
}
// 或者检查是否为null字符串
if (str.isNull()) {
    // 为null(不同于空字符串)
}

字符串分割
char* str = "apple,orange,banana";
char* token = strtok(str, ",");  // 修改原字符串
while (token != nullptr) {
    printf("%s\n", token);
    token = strtok(nullptr, ",");
}

QString str = "apple,orange,banana";
// 使用split(不修改原字符串)
QStringList fruits = str.split(",");
foreach (QString fruit, fruits) {
    qDebug() << fruit;
}

其次是从文件中读取字段上来的时候,有时候会莫名其妙的有一些多的符号,整理了几个函数用于排查:

//比如你本来是从文件中读的“内存管理”,但是总是与实际字符串不匹配
//假设str是从底部读的
strcmp(str, "内存管理") != 0 
//此时在确定好加载文件和读取字符串的代码没问题时,可能是遇到了字符串中含多余符号的问题;
//先调试测定字符串的ASCLL码,看看是否跟预期字符串相同
void printf_string(const char* str){
	int length = strlen(str);
	qDebug()<<"String length:"<<length;
	for(int i=0;i<length;i++){
		qDebug()<<"Character: '%c' =="<<str[i];
		qDebug()<<"ASCII: %d\n =="<<(unsigned char)str[i];
	}
}
//比较两个字符串的ASCLL码,如果不同,就是含有多余符号
//去除字符串的隐藏字符(空格、制表符、换行符等)
char* trim(char* str){
	char *end;

	//去除前导空白字符
	while(isspace((unsigned char)*str)) str++;//移动指针到第一个非空白字符
	if(*str == 0){ //空字符串
		return str;
	}

	//去除尾部的空白字符
	end = str + strlen(str) - 1;
	while(end > str && isspace((unsigned char)*end)) end--;

	//添加新的字符串终止符
	*(end+1) = '\0';

	return str;
}
// 移除字符串中的BOM字节序标记
char* remove_bom(char* str){
	unsigned char bom[] = {0xEF, 0XBB, 0XBF};
	if(strncmp((char*)str, (char*)bom, 3) == 0){
		//跳过前三个字符
		return str + 3;
	}
	return str;
}

最后是常见的指针传递问题,举一个在实际中读取文本文件内容的例子:
例如文本文件内容为:

{c:1},{t:文本1},{i:A00000}
{c:2},{t:文本2},{i:A00001}
{c:3},{t:文本3},{i:A00002}
...

首先肯定是希望拿到我们的结构体中:

typedef struct {
	int index;
	char text;
	char id;
	}single_text_value;

typedef std::vector<single_text_value > text_value;

typedef struct {
		char_t  file_name[256];
		text_value index;
}whole_text_value;

static whole_text_value whole_index[5];
single_text_value temp;
memset(&temp, '\0', sizeof(temp));
whole_index[i].index.push_back(temp);

主要是值传递的问题,拷贝字符串(strcpy 或 strncpy),是直接拷贝复制,指针赋值(浅拷贝)则需要注意会出现指针悬挂或覆盖的问题。

最后整理一下关于函数返回参数

//常用的错误
const char* lang_text(char_t* id){
    char_t trans_text[32];
    lang_apply(id, trans_text);
    return trans_text;
}
这里返回的是局部变量的指针,函数周期结束后会变成悬空指针
一般我是再传入一个参数,专做返回处理

还有一些C++做法可以完全避免这个问题:

std::string lang_text(char* text){
	char trans_text[32];
	lang_apply(id, trans_text);
	return std::string(trans_text); // 返回一个字符串对象

//std::vector 是一个动态容器,不需要手动释放内存,而且它的生命周期会自动管理
std::vector<char> lang_text(char_t* id) {
    std::vector<char> trans_text(32);  // 动态分配大小为 32 的字符数组
    lang_apply(id, trans_text.data());

    return trans_text;  // 返回一个 vector 对象
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值