重拾 char 与 ASCII

博客讨论了在LeetCode上解决变位词组问题时遇到的一个编码陷阱,涉及字符计数和ASCII码的关系。文章指出,使用char类型存储字符出现次数可能会导致超出char范围的问题,因char在计算机中是-128到127的范围,而ASCII只使用了0到127的部分。此外,还介绍了ASCII编码、其他字符集(如Unicode、UTF-8)及其在不同编码规则下的兼容性问题。

引入

在leetcode上做的一道题,对其中一个解法有个小坑,可以讨论下
面试题 10.02. 变位词组
先放代码

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        map<string, int> mp;
        vector<vector<string>> ans;
        mp.clear();
        ans.clear();
        int i = 0;
        for(string &str : strs) {
            string status(26, 0);
            for(char ch : str) {
                status[ch - 'a']++;
            }
            if(mp.find(status) != mp.end()) {
                ans[mp[status]].push_back(str);
            } else {
                mp[status] = i++;
                vector<string> v{str};
                ans.push_back(v);
            }
        }
        return ans;
    }
};

这个解法的关键在于如何存每个字符的在一个字符串中出现的次数,并将出现次数的情况当作map的key来使用,当然,我们可以选择用一个大小为26的int数组来存每个字符的次数,但每个字符串的情况不好当作map的key来存(虽然可以对次数做字符拼接,或者次数乘以质数的数学方法,但这里就不讨论了,我们主要讨论由此引出的对ASCII的讨论)
这里我们用了string来存,也就是一个string类型的变量status(26, 0),一个字符串中的每个元素是char类型的,对每个元素ch, 我们做status[ch - ‘a’]++操作,表示第ch-‘a’个字符处存的是字符ch在字符串中出现的次数。而这个次数其实是用char来存的,因为char在计算机中实际上是整数类型。

但这里我们忽略了一点,char在计算机中我们规定是一个字节,即8bit,表示的范围只有-128 ~ 127,所以当一个字符串中某个字符数超过127时,(由于补码的缘故)存在char中的值会变成负数,也就是在status[ch - ‘a’]++的过程中其所存的值只会是0 ~ 127,-128 ~ 0,0 ~ 127的循环,而不是我们预期的129,156 这样可能的实际出现的次数。

结论

ASCII的范围是0 ~ 127,char的范围是-128 ~ 127,好像没啥大关系,都在一个字节范围内。

而ASCII其实是一种很早就被编码到计算机里的一套字符编码规则,也就是编码表,仅有128个对应关系,即当整数在[0, 127]这个范围内时,可以通过内置在计算机中的编码方式,将整数映射成ASCII中的字符,如65在ASCII表中对应A,则以%c的规格打印65时,输出的就是65.

另外,随着中文等其他语言引入计算机,也就多出了后面许多的编码规则如GB2312(中国)、Shift_JIS(日本)、Unicode字符集(大集合)、UTF-8(可变长编码)等字符集编入计算机中,当我们选择某一规则显示当前文本时,编辑器就会以对应的规则去解释每个字符的表示,各字符集会有兼容和不兼容的缘故,因此会有部分乱码或者全篇乱码的现象。

顺手贴个表吧:
Ascii Table

这种入门级的计算机知识,竟然以这种方式让我重拾一番,汗颜~

将 `unsigned char` 类型的数组或变量转换为 ASCII 英文字母字符串的过程,本质上是将每个字节解释为 ASCII 编码的字符,然后将其转换为 C 或 C++ 中的字符串类型。 --- ## ✅ 示例一:C语言中将 `unsigned char[]` 转换为 ASCII 字符串 ```c #include <stdio.h> #include <string.h> int main() { unsigned char data[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0' }; char *str = (char *)data; printf("ASCII 字符串: %s\n", str); return 0; } ``` ### 📌 说明: - `unsigned char[]` 本质上就是一个字节数组,ASCII 字符范围是 0~127。 - 强制类型转换为 `char*` 后可以直接使用 `%s` 打印字符串。 - 字符串必须以 `\0` 结尾。 --- ## ✅ 示例二:C++中将 `unsigned char[]` 转换为 `std::string` ```cpp #include <iostream> #include <string> int main() { unsigned char data[] = { 'Q', 't', ' ', 'i', 's', ' ', 'g', 'r', 'e', 'a', 't', '!', '\0' }; std::string str(reinterpret_cast<char*>(data)); std::cout << "std::string: " << str << std::endl; return 0; } ``` ### 📌 说明: - 使用 `reinterpret_cast<char*>(data)` 将 `unsigned char*` 转换为 `char*`。 - `std::string` 构造函数会自动识别 `\0` 结尾。 --- ## ✅ 示例三:Qt 中将 `unsigned char[]` 转换为 `QString` ```cpp #include <QApplication> #include <QLabel> #include <QWidget> #include <QVBoxLayout> #include <QString> int main(int argc, char *argv[]) { QApplication app(argc, argv); unsigned char Rbuf[64] = {0}; const char* version = "v1.2.3-20250405"; memcpy(Rbuf + 3, version, strlen(version)); // 模拟数据在第4~33字节 // 提取第4~33字节(索引3到32) char versionStr[31]; memcpy(versionStr, Rbuf + 3, 30); versionStr[30] = '\0'; QString versionInfo = QString::fromUtf8(versionStr); QWidget window; QVBoxLayout *layout = new QVBoxLayout(&window); QLabel *label = new QLabel("Dongle 版本信息: " + versionInfo); layout->addWidget(label); window.setLayout(layout); window.show(); return app.exec(); } ``` ### 📌 说明: - `QString::fromUtf8()` 可以将 ASCII 或 UTF-8 编码的字符数组转换为 `QString`。 - 如果你确定是纯 ASCII,也可以使用 `QString::fromAscii()`(Qt5 支持,Qt6 已弃用)。 --- ## ✅ 示例四:C++中将单个 `unsigned char` 转换为字符 ```cpp #include <iostream> int main() { unsigned char ch = 'A'; std::cout << "ASCII 字符: " << static_cast<char>(ch) << std::endl; return 0; } ``` --- ## 📌 总结 | 类型 | 转换方式 | 示例 | |------|----------|------| | `unsigned char[]` -> C字符串 | `(char*)data` | `printf("%s", (char*)data);` | | `unsigned char[]` -> `std::string` | 构造函数 | `std::string(reinterpret_cast<char*>(data))` | | `unsigned char[]` -> `QString` | `fromUtf8()` | `QString::fromUtf8(reinterpret_cast<char*>(data))` | | 单个 `unsigned char` -> `char` | 强制转换 | `static_cast<char>(ch)` | --- ##
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值