Windows程序设计 - 字符与字符串处理

目录

字符编码的问题

ANSI Vs Unicode

函数版本区分

C RunTime库

老生常谈的ASCII和UNICODE支持性

安全字符串函数

推介的字符和字符串处理的方式

ASCII与UNICODE互转

Reference


字符编码的问题

对于一些像我这样的初学者:很喜欢认为字符串的处理就是将字符排成一个序列(SequenceList),然后粗暴的以一个\0结尾草草了事,想要获取长度只能strlen遍历长度

// A simple Implemment of strlen:
int My_Strlen(const char* str){int len = 0;for(const char* pStrIndex = str;     // 铆定字符串的头部*pStrIndex == '\0';             // 循环退出条件是遇到了\0pStrIndex++, len++              // 更新游标);return len;
}

但是实际上,如果我们的程序走向国际,就不可避免的出现过国际化的问题,也就是字符串需要存储的是不同国家语言的文字。这样,我们就必须把目光转向Unicode了。

关于UTF8,UTF16以及Unicode等字符集的问题,不妨参考:Unicode、UTF-8、UTF-16之间的区别 - 知乎 (zhihu.com)

ANSI Vs Unicode

Microsoft C/C++中,我们编译器内置了一个类型就是wchar_t表示宽字符(当然有开关,只有在指定了/Zc:wchar_t的时候才会开,不过你放心,默认的都开着),你看可以这样表达一个宽字符:

wchar_t c = L'A';
wchar_t szBuffer[100] = L"A String";

使用一个L前缀来表达这个字符或者是字符串是宽字符或者是由宽字符组成的宽字符串!当然也可以使用Windows的内置宏来声明这个字符或者字符串是宽的,办法就是使用以下这些宏

#define TEXT(quote) __TEXT(quote)   // r_winnt
#define __TEXT(quote) L##quote      // r_winnt

Windows呢,自己整了一层抽象(尽管我觉得很鸡肋)

//
// ANSI (Multi-byte Character) types
//
typedef CHAR *PCHAR, *LPCH, *PCH;
typedef CONST CHAR *LPCCH, *PCCH;
​
typedef _Null_terminated_ CHAR *NPSTR, *LPSTR, *PSTR;
typedef _Null_terminated_ PSTR *PZPSTR;
typedef _Null_terminated_ CONST PSTR *PCZPSTR;
typedef _Null_terminated_ CONST CHAR *LPCSTR, *PCSTR;
typedef _Null_terminated_ PCSTR *PZPCSTR;
typedef _Null_terminated_ CONST PCSTR *PCZPCSTR;
​
typedef _NullNull_terminated_ CHAR *PZZSTR;
typedef _NullNull_terminated_ CONST CHAR *PCZZSTR;
​
typedef  CHAR *PNZCH;
typedef  CONST CHAR *PCNZCH;
​
//
// Neutral ANSI/UNICODE types and macros
//
#ifdef  UNICODE                     // r_winnt
​
#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */
​
typedef LPWCH LPTCH, PTCH;
typedef LPCWCH LPCTCH, PCTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
typedef LPUWSTR PUTSTR, LPUTSTR;
typedef LPCUWSTR PCUTSTR, LPCUTSTR;
typedef LPWSTR LP;
typedef PZZWSTR PZZTSTR;
typedef PCZZWSTR PCZZTSTR;
typedef PUZZWSTR PUZZTSTR;
typedef PCUZZWSTR PCUZZTSTR;
typedef PZPWSTR PZPTSTR;
typedef PNZWCH PNZTCH;
typedef PCNZWCH PCNZTCH;
typedef PUNZWCH PUNZTCH;
typedef PCUNZWCH PCUNZTCH;

看看就好,实际开发的时候跳转着看就行。

BTW:教大伙一个快速识别的办法:P大头的表示指针,L大头的表示宽

函数版本区分

在Windows中凡是涉及到字符串处理的函数,都有至少两个版本!他们的函数基名都是一致的!唯一不同的是针对处理字符集的不同它会有不一样的后缀名,比如说处理ASCII字符串的时候,它的后缀名是A!处理Unicode的字符串的时候它的后缀名是W!

以Windows著名狗屎API:创建一个窗口为一个例子:

HWND CreateWindowExA([in]           DWORD     dwExStyle,[in, optional] LPCSTR    lpClassName,[in, optional] LPCSTR    lpWindowName,[in]           DWORD     dwStyle,[in]           int       X,[in]           int       Y,[in]           int       nWidth,[in]           int       nHeight,[in, optional] HWND      hWndParent,[in, optional] HMENU     hMenu,[in, optional] HINSTANCE hInstance,[in, optional] LPVOID    lpParam
);
​
HWND CreateWindowExW([in]           DWORD     dwExStyle,[in, optional] LPCWSTR   lpClassName,[in, optional] LPCWSTR   lpWindowName,[in]           DWORD     dwStyle,[in]           int       X,[in]           int       Y,[in]           int       nWidth,[in]           int       nHeight,[in, optional] HWND      hWndParent,[in, optional] HMENU     hMenu,[in, optional] HINSTANCE hInstance,[in, optional] LPVOID    lpParam
);

但是实际上你使用的时候使用的是CreateWindowEx,,这是一个根据是否存在Unicode而静态解析的宏:

#ifdef UNICODE
#define CreateWindowEx  CreateWindowExW
#else
#define CreateWindowEx  CreateWindowExA

C RunTime库

C run-time library里面含有初始化代码,还有错误处理代码(例如divide by zero处理)。你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。因为C run-time library包含了C程序运行的最基本和最常用的函数。

也就是说他给我们C语言的环境提供了支持。

不关心其他,我们今天看看这里的字符串相关API。

老生常谈的ASCII和UNICODE支持性

使用_t...类的函数,他将会动态的决定是使用何种版本的字符串。对于宽字符串处理函数则是以w作为前缀:反之就是正常的C库函数:

_Check_return_
_ACRTIMP size_t __cdecl wcslen(_In_z_ wchar_t const* _String);_Check_return_
size_t __cdecl strlen(_In_z_ char const* _Str);

安全字符串函数

许多系统安全问题是由缓冲区处理不善和生成的缓冲区溢出引起的。(你猜猜为什么老谭的gets挂了,就是因为缓冲区问题) 糟糕的缓冲区处理通常与字符串操作相关联。 C/C++ 语言运行时库 (strcatstrcpysprintf 等) 提供的标准字符串操作函数不会阻止写入缓冲区末尾。

两组新的字符串操作函数(称为 安全字符串函数)提供额外的处理,以便在代码中正确处理缓冲区。 这些安全字符串函数在 Windows 驱动程序工具包 (WDK) 以及 Microsoft Windows XP SP1 及更高版本的驱动程序开发工具包 (DDK) 和 Windows SDK 中可用。 它们旨在替换其内置的 C/C++ 对应项和 Windows 提供的类似例程。

一组安全字符串函数用于内核模式代码。 这些函数在名为 Ntstrsafe.h 的头文件中原型。 WDK 中提供了此头文件和关联的库。

另一组安全字符串函数用于用户模式应用程序。 相应的头文件 Strsafe.h 包含这些函数的原型。 该文件和关联的库在 Windows SDK 中可用。

有关 Strsafe.h 的详细信息,请参阅 使用 Strsafe.h 函数。

推介的字符和字符串处理的方式

我们应该遵守如下准则:

  • 首先开始将文本字符串想象为字符的数组,而不是字节的数组。

  • 用通用数据类型来表示文本字符和字符串,用明确的数据类型来表示字节,字节指针和数据缓冲区。

  • 用这些宏TEXT() _T()来表示字面量字符和字符串,但是为了保持一致性和更好的可读性,请不要混用!

  • 执行全局替换!例如使用PTSTR替换PSTR

  • 修改与字符串相关的计算:例如函数经常希望我们传给他缓冲区大小的字符数,而不是字节数!这意味着我们需要传入__countof(szBuf)而不是sizeof(szBuf)!而且如果需要为一个字符串分配一个内存块,而且知道字符串中的字符数!那么要记住内存是以字节来进行分配的!需要调用malloc(NCh*sizeof(TCHAR))

  • 避免使用printf系列函数!尤其是不要用%S字段类型来进行ANSI与Unicode字符串之间的表示!正确的方式使用MultiByteToWideCharWideCharToMultiByte

对于字符串处理函数应该遵循以下原则

  • 始终使用安全的字符串处理函数,例如后缀为杠S的函数或者前缀为StringCCH的函数!后者主要在我们想明确控制截断的时候用,如果并不想明确控制截断,使用前者!

  • 不要使用不安全的C运行库字符串处理函数!一般情况下,如果一个缓冲区处理函数的参数中不包括目标缓冲区的长度,那么我们应该避免使用这个函数!而且还同样避免自己实现这样的函数!

  • 不要使用kernel32方法进行字符串处理比如lstrcat等函数

  • 对于想要显示在界面上的字符串如果想要处理它使用来类似于CompareString这样参数涉及到国际化的参数的函数处理!

    因为在比较字符串的时候它会考虑用户的区域设置

ASCII与UNICODE互转

ASCII转UNICODE

int MultiByteToWideChar([in]            UINT                              CodePage,[in]            DWORD                             dwFlags,[in]            _In_NLS_string_(cbMultiByte)LPCCH lpMultiByteStr,[in]            int                               cbMultiByte,[out, optional] LPWSTR                            lpWideCharStr,[in]            int                               cchWideChar
);

[in] lpMultiByteStr指向要转换的字符串的指针。

[in] cbMultiByte

lpMultiByteStr 参数指示的字符串的大小(以字节为单位)。 或者,如果字符串以 null 结尾,则可以将此参数设置为 -1。 请注意,如果 cbMultiByte0,则函数将失败。

如果此参数为 -1,则该函数将处理整个输入字符串,包括终止 null 字符。 因此,生成的 Unicode 字符串具有终止 null 字符,并且 函数返回的长度包括此字符。

如果此参数设置为正整数,则函数将完全处理指定的字节数。 如果提供的大小不包含终止 null 字符,则生成的 Unicode 字符串不会以 null 结尾,并且返回的长度不包括此字符。

[out, optional] lpWideCharStr指向接收转换字符串的缓冲区的指针。

[in] cchWideChar lpWideCharStr指示的缓冲区的大小(以字符为单位)。 如果此值为 0,则该函数返回所需的缓冲区大小(以字符为单位),包括任何终止 null 字符,并且不使用 lpWideCharStr 缓冲区。

咋用?

  1. 调用MultiByteToWideChar函数,而参数lpWideCharStr传入NULL,为cchWideChar参数传入0,cbMultiByte参数传入-1,,然后接受返回值!

  2. 分配一块足以容纳转换后的Unicode字符串的内存,它的大小是上一个函数调用的返回值乘以sizeof(wchar_t)

  3. 再次调用这个函数,这一次而参数lpWideCharStr传入分配的缓冲区地址,为cchWideChar参数传入缓冲区大小,`然后接受返回值!

  4. 使用它!

  5. 释放这个内存块!

UNICODE转ASCII

int WideCharToMultiByte([in]            UINT                               CodePage,[in]            DWORD                              dwFlags,[in]            _In_NLS_string_(cchWideChar)LPCWCH lpWideCharStr,[in]            int                                cchWideChar,[out, optional] LPSTR                              lpMultiByteStr,[in]            int                                cbMultiByte,[in, optional]  LPCCH                              lpDefaultChar,[out, optional] LPBOOL                             lpUsedDefaultChar
);

如法炮制!具体的说明请查看Reference的文档!

Reference

Unicode、UTF-8、UTF-16之间的区别 - 知乎 (zhihu.com)

C运行时库(C Run-time Library)详解_run_time-CSDN博客、

MultiByteToWideChar 函数 (stringapiset.h) - Win32 apps | Microsoft Learn

WideCharToMultiByte 函数 (stringapiset.h) - Win32 apps | Microsoft Learn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/4039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

了解ASK模块STX883Pro和超外接收模块SRX883Pro的独特之处 STX883Pro模块具有以下特点:

高发射功率:STX883Pro具有较高的发射功率,可实现长距离的信号传输,适用于需要覆盖广泛区域的应用场景。 高频率稳定性:具备稳定的频率输出,确保信号传输的可靠性和一致性,避免频率漂移导致的通信故障。 大…

Ubuntu 18.04 安装 CMake Ceres-Solver 记录

1. 安装 CMake-3.22.1 不可卸载原有版本 CMake(防止 ROS 出现问题),只需建立软链接即可 1. 源码下载并解压wget https://cmake.org/files/v3.22/cmake-3.22.1.tar.gz tar -xvzf cmake-3.22.1.tar.gz2. 移动到合适目录sudo mv cmake-3.22.1 /u…

C++ | Leetcode C++题解之第48题旋转图像

题目&#xff1a; 题解&#xff1a; class Solution { public:void rotate(vector<vector<int>>& matrix) {int n matrix.size();// 水平翻转for (int i 0; i < n / 2; i) {for (int j 0; j < n; j) {swap(matrix[i][j], matrix[n - i - 1][j]);}}//…

Thread方法具体解析

对于run方法 如果该线程是使用单独的 Runnable run 对象构造的&#xff0c;则调用该 Runnable 对象的 run 方法&#xff1b;否则&#xff0c;此方法不执行任何操作并返回。 对于start方法 导致该线程开始执行&#xff1b; Java虚拟机调用该线程的run方法。 这里介绍一个快捷键…

Java Spring 中构造函数注入和Setter注入的优缺点

在使用Java Spring框架进行依赖注入时&#xff0c;我们常常会遇到构造函数注入和Setter注入两种方式。这两种方式各有优缺点&#xff0c;本文将对它们进行比较和分析&#xff0c;帮助开发者在实际项目中做出合适的选择。 构造函数注入 构造函数注入是通过在类的构造函数中传入…

Windows Vscode ModuleNotFoundError: No module named

故障现象&#xff1a; Windows Vscode 经常会遇到模块路径查找失败的异常。 如运行2_from_import_test.py后&#xff0c;报错&#xff1a; 发生异常: ModuleNotFoundError No module named programmer File "D:\leolab\programmer\2_from_import_test.py", line 8…

什么是数字化运营?

目录 一、什么是数字化运营&#xff1f; 二、数字化运营的重要性是什么&#xff1f; 三、数字化运营的具体步骤和措施是什么&#xff1f; 四、数据化决策是什么&#xff1f; 一、什么是数字化运营&#xff1f; 数字化运营是利用数字技术和数据分析来优化企业的业务流程和运…

C语言 | Leetcode C语言题解之第51题N皇后

题目&#xff1a; 题解&#xff1a; int solutionsSize;char** generateBoard(int* queens, int n) {char** board (char**)malloc(sizeof(char*) * n);for (int i 0; i < n; i) {board[i] (char*)malloc(sizeof(char) * (n 1));for (int j 0; j < n; j) board[i][…

LeetCode-104-二叉树最大深度

题目&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 思路&#xff1a;不断往下遍历&#xff0c;每往深层遍历一层&#xff0c;深度加一&#xff0c;当遍历到null&#xff0c;与当前最大…

Linux内核驱动开发-006内核定时器

1驱动程序 /*************************************************************************> File Name: timer.c> Author: yas> Mail: rage_yashotmail.com> Created Time: 2024年04月23日 星期二 19时20分42秒*************************************************…

每日JAVA高级面试题

Java 高级面试问题及答案 以下是一些在Java高级面试中可能会遇到的问题&#xff0c;以及对这些问题的探讨和回答。 问题 1: Java内存模型是什么&#xff1f;请解释其重要性。 探讨&#xff1a; Java内存模型&#xff08;Java Memory Model, JMM&#xff09;是Java虚拟机&…

【unity】三维数学应用(计算线和面的交点)

【unity】三维数学应用&#xff08;计算线和面的交点&#xff09; 实现方法有多种&#xff0c;下面介绍一种简单的方法。利用一个点指向面上任意点的向量&#xff0c;到该面法线的投影长度相同的基本原理&#xff0c;结合相似三角形既可以求出交点。 原理 如下图 GD组成的线段…

c++图论基础(1)

目录 无向图 无向图度 无向图性质 有向图 有向图度 有向图性质 图的分类&#xff1a; 稀疏图&#xff1a; 稠密图&#xff1a; 零图&#xff1a; 有向完全图&#xff1a; 无向完全图&#xff1a; 度序列&#xff1a; 图是由顶点集合(简称点集)和顶点间的边(简称边…

华为机试:夺宝奇兵

夺宝奇兵 | 时间限制&#xff1a;1秒 | 内存限制&#xff1a;262144K 一个3人寻宝团队搜寻沉船成功&#xff0c;获得一笔宝藏&#xff0c;领头人为不起纷争&#xff0c;决定将财宝分成3N份&#xff0c;每次3人从分好的3堆宝藏中依次拿取&#xff0c;领头人第一拿&#xff0c;你…

Weblogic JMS

简介 全称:WebLogic Server的Java Messaging Service(JMS) WebLogic JMS 是与 WebLogic Server 平台紧密集成的企业级消息传递系统。 Java Message Service (JMS) API 是一种消息传递标准,允许基于 Java Platform Enterprise Edition (Java EE) 的应用程序组件创建、发送、…

DiffSpeaker 部署踩坑笔记

目录 依赖项&#xff1a; Windows环境下安装psbody 下载安装boost 编译安装psbody 保存mp4报错解决 语音驱动的3D面部动画&#xff0c;可以用扩散模型或Transformer架构实现。然而它们的简单组合并没有性能的提升。作者怀疑这是由于缺乏配对的音频-4D数据&#xff0c;这对…

windows rabbitMq安装

一、Erlang 环境准备 下载安装包 跟我们跑java项目&#xff0c;要装jdk类似。rabbitMQ是基于Erlang开发的&#xff0c;因此安装rabbitMQ服务器之前&#xff0c;需要先安装Erlang环境。 官网直接下载windows直装版本&#xff1a;https://www.erlang.org/downloads 无脑安装&a…

【RocketMQ知识点总结-1】

文章目录 RocketMQ介绍RocketMQ架构&#xff1a;NameServer:BrokerProducerTopic&#xff08;主题&#xff09;&#xff1a;Queue&#xff08;队列&#xff09;&#xff1a;Message&#xff08;消息&#xff09;&#xff1a; RocketMQ的工作流程RocketMQ的使用场景异步消息传递…

预训练扩散模型用于即插即用的医学图像增强

文章目录 Pre-trained Diffusion Models for Plug-and-Play Medical Image Enhancement摘要本文方法Image Enhancement with Denoising AlgorithmPre-Trained Diffusion Models for Plug-and-play Medical Image Enhancement 实验结果 Pre-trained Diffusion Models for Plug-a…

CentOS安装SonarQube

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 sonar是一款静态代码质量分析工具,支持Java、Python、PHP、JavaScript、…