什么是非打印字符

news/2024/10/20 14:57:47/

1.什么是非打印字符

非打印字符(Non-printable characters)指的是那些无法在终端、控制台或文本编辑器中直接显示为可见符号的字符。它们通常用于控制设备行为或传递特殊信息,而不是作为可见的文本字符。非打印字符包括像空格(\n 换行符、\t 制表符)以及控制字符(如 NULL 字符 \0ESC 键的字符)。

1.1 分类

  • 控制字符:这类字符不代表实际的符号,而是用于控制显示、输入/输出设备的行为。它们位于 ASCII 表的前 32 个位置(值 0-31),比如:

    • \0:空字符,表示字符串的结束。
    • \n:换行符,移动光标到下一行的开始位置。
    • \r:回车符,移动光标到当前行的开头。
    • \t:水平制表符,插入一个制表位。
    • ESC:转义字符,通常用于终端命令序列。
  • 不可见符号:一些字符虽然具有实际的符号含义,但在某些上下文中被认为是非打印的。例如,某些 Unicode 字符或者特殊的格式控制字符在大多数情况下不可见。

  • 二进制数据中的任意字节:当处理二进制数据时,这些字节可能不符合任何字符编码规范,因此无法直接映射到可见字符。它们的含义取决于数据的结构和解释方式。

2.二进制数据

二进制数据并不是文本,它每一个字节可以是任何 8 位数值(0 到 255),并不是所有这些值都能对应到可打印的字符。例如,字节值 0x010xFF 这样的二进制值在字符编码中没有对应的可打印字符,因此它们是不可打印的。

二进制数据不是用于直接显示的,而是用于在内存、磁盘、网络等介质中存储和传输数据。这些数据可以是加密信息、压缩文件、协议消息等,不需要对应可视化的符号,而是需要设备或程序对其进行解码和处理。

2.1 二进制数据 vs 可打印字符

  • 可打印字符:可打印字符通常指在某种字符编码(如 ASCII 或 UTF-8)中对应于显示符号的字符。它们可以在控制台或屏幕上显示,例如字母、数字、标点符号等。

    • 例如,0x41 对应的是可打印字符 'A'
  • 非打印字符和二进制数据:二进制数据中的任意字节(例如 0x01, 0xFF, 0x00 等)通常是非打印字符,因为它们在字符集(如 ASCII)中没有对应的可见符号

std::string binary_data;
binary_data.push_back(0x01);  // 非打印字符,二进制值 0x01。
binary_data.push_back(0xFF);  // 非打印字符,二进制值 0xFF。// 打印出这些字节,将无法显示可见字符。
std::cout << binary_data << std::endl;  // 输出会是乱码或不可见内容————————————————————
char printable_char = 'A';  // 可打印字符 'A',对应的 ASCII 值是 0x41。
std::cout << printable_char << std::endl;  // 输出: A

2.2 为什么不自动打印为01串

因为计算机内部存储的数据是以 字节 为单位的,每个字节由 8 位组成,存储了一个二进制数值(0 到 255 的整数),但这些数字在打印时会根据不同的上下文字符十六进制整数其他格式 表示,而不会直接以二进制形式显示。

当你使用 std::cout 这样的函数打印数据时,C++ 会根据数据类型来决定如何显示。例如:

  • 对于 char 类型的数据,std::cout 会假定这是一个 可打印字符,并尝试显示对应的 ASCII 字符。
  • 对于 int 类型的数据,它会显示其对应的 十进制 数值。
  • 对于 std::string,它会打印字符串的内容,但不会把字符串内容转换为其底层的二进制位(即 01)。

二进制数据不直接打印为 01 串的原因

  • 效率问题:直接打印 01 的二进制表示非常低效,因为每个字节需要 8 个字符来表示。例如,字节 65 对应的二进制是 01000001,而用 std::cout 打印只需显示字符 A 或整数 65,远比打印 01000001 这样的二进制串高效得多。
  • 可读性问题:人类更习惯查看十进制数字或字符,二进制串对于人类来说不易阅读和理解。
  • 字符编码的应用:当打印一个 char 类型时,系统会尝试将该字节解释为一个 ASCII 或其他字符编码中的字符,而不是显示该字节的二进制位。
char c = 65;  // 65 是 'A' 的 ASCII 值
std::cout << c << std::endl;  // 输出: Aint num = 5;
std::cout << num << std::endl;  // 输出: 5// 打印二进制数据
std::string binary_data;
binary_data.push_back(65);  // 65 是 'A' 的 ASCII 编码
std::cout << binary_data << std::endl;  // 输出: A

3.string为什么可以存储非打印字符

std::string 的底层实现只是一个字节数组,它不对存储的内容进行任何格式上的限制。char 只是一个字节(8 位),可以存储任意的 8 位数据,即二进制数据。

std::string 维护一个长度值来记录实际存储的数据长度,而不是依赖像 C 语言中的 \0(空字符)来标识字符串的结束。因此,即使二进制数据中包含 \0 字节,它也不会被视为字符串的终止符。

尽管 std::string 名字中有 "string"(字符串),但它本质上是一个字节容器,可以存储任意的字节序列,包括二进制数据。

int main(){// 创建包含二进制数据的字符串,包含非打印字符(0x00, 0x01, 0x02)std::string binary_data;binary_data.push_back(0x00);  // 添加一个字节 0x00binary_data.push_back(0x01);  // 添加一个字节 0x01binary_data.push_back(0x02);  // 添加一个字节 0x02// 打印字符串的大小std::cout << "Binary data size: " << binary_data.size() << std::endl;// 遍历并以十六进制形式打印每个字节for (unsigned char c : binary_data) {printf("%02x ", c);}std::cout << std::endl;cout<<endl;return 0;
}// 打印结果
// Binary data size: 3
// 00 01 02 

4.string类型如何存储汉字

std::string 并不直接理解多字节字符的编码。汉字等非 ASCII 字符在 std::string 中存储时,通常使用 UTF-8 编码。

UTF-8 编码

  • UTF-8 是一种变长的字符编码方式,使用 1 到 4 个字节来表示一个 Unicode 字符。
  • ASCII 字符(如字母、数字等)使用 1 个字节存储,编码与 ASCII 完全兼容。
  • 汉字等非 ASCII 字符 使用 3 个字节(大多数汉字)或 4 个字节来表示。常见的汉字在 UTF-8 中通常占用 3 个字节。
  • 每个字节仍然是 char 类型,即 8 位的整数。虽然汉字的表示需要多个字节,但 std::string 可以按顺序存储这些字节。

std::string 的字节存储

  • std::string 底层是一个 char 数组,而 char 代表 8 位的数据。因此,std::string 可以存储任意字节序列,不关心数据是否是汉字或其他特殊符号。
  • 当存储汉字时,每个汉字的 UTF-8 编码会拆分成多个字节,并按顺序存储在 std::string 中。
int main(){std::string utf8_str = "汉";  // UTF-8 编码的汉字cout<<utf8_str<<", "<<utf8_str.size()<<endl;for (unsigned char c : utf8_str) {printf("%02x ", c);  // 逐个字节输出其十六进制表示}cout<<endl;return 0;
}// 运行结果
// 汉, 3
// e6 b1 89 

打印时是根据终端的编码类型来编码输出的,使用locale命令查看终端编码类型是UTF-8所以可以正确输出为汉字。


http://www.ppmy.cn/news/1540544.html

相关文章

Android OpenGL触控反馈

在许多游戏和应用程序中&#xff0c;优秀的用户交互设计是至关重要的&#xff0c;它能够让用户感受到与现实世界中物体的互动&#xff0c;即便他们实际上只是在与屏幕上的像素进行交互。而在安卓上则主要以触控交互为主。本篇我们将探讨如何通过添加触控功能来增强一个场景的交…

星海智算:【萤火遛AI-Stable-Diffusion】无需部署一键启动

部署流程 1、注册算力云平台&#xff1a;星海智算 https://gpu.spacehpc.com/ 2、创建实例&#xff0c;镜像请依次点击&#xff1a;“镜像市场”->“更换”->“AI绘画”->“萤火遛AI-Stable Diffusion”。 程序首次启动可能需要几分钟&#xff0c;待实例显示“运行…

visio导出pdf公式变形问题杂谈

其实不会变形。 我自己的情况是直接用edge PDF阅读器打开pdf看到的是公式有变形&#xff08;常见是字体、形状变了&#xff09;&#xff0c;但换一个pdf阅读器如adobe的就是正常的了 不过大家一般是用edge pdf阅读器直接打开查看&#xff0c;所以通过visio打印的方式导出pdf可…

后端直接从文件URL下载文件

后端直接从文件URL下载文件 问题描述&#xff1a; 需要直接在后端根据文件url下载文件 解决方案&#xff1a; 可根据实际情况改代码&#xff1a; public AidingStudentsImportExcelRespVO excelResolver(String url, Long aidingStudentsManageId){//创建temp.xlsxString …

《深度学习》OpenCV 风格迁移、DNN模块 案例解析及实现

目录 一、风格迁移 1、什么是风格迁移 2、步骤 1&#xff09;训练 2&#xff09;迁移 二、DNN模块 1、什么是DNN模块 2、DNN模块特点 1&#xff09;轻量 2&#xff09;外部依赖性低 3&#xff09;方便 4&#xff09;集成 5&#xff09;通用性 3、流程图 4、图像…

JavaWeb开发4

JS对象 Array Array对象用于定义数组 var 变量名new Array(元素列表)&#xff1b; var 变量名[元素列表] 访问 arr[索引]值&#xff1b; 注意&#xff1a;JS中数组相对于Java中集合&#xff0c;数组的长度是可变的&#xff0c;JS是弱类型&#xff0c;所以可以存储任意类型…

js 精确计算(加减乘除)

js 精确计算(加减乘除) 工具类CalcUtil.js class CalcUtil {// 两数相加calcAdd(num1, num2) {var r1, r2, m;try {r1 num1.toString().split(.)[1].length;} catch (e) {r1 0;}try {r2 num2.toString().split(.)[1].length;} catch (e) {r2 0;}m Math.pow(10, Math.max…

使用 Docker 升级 MySQL 的优化步骤与说明

在升级 MySQL 之前&#xff0c;进行系统化的准备和实施能够显著降低数据丢失的风险&#xff0c;并确保顺利过渡。以下是优化后的详细步骤及分析。 一、备份原数据库所有数据 重要性分析 在任何数据库操作中&#xff0c;备份都是最重要的一步。确保备份包含所有数据库、表及其…