ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# Windows API 中的字符串 > 原文: [http://zetcode.com/gui/winapi/strings/](http://zetcode.com/gui/winapi/strings/) 在 C 语言中,没有字符串数据类型。 程序中的字符串字面值是字符数组。 每当我们说字符串时,都是指一个字符数组。 我们有五组处理字符串的函数; 在 C 运行时库(CRT)和 Windows API 中: * ANSI C 标准函数 * 安全性增强的 CRT 函数 * Windows API 内核和用户函数 * Windows API Shell 轻量级工具函数 * Windows API StrSafe 函数 建议您使用安全性增强的标准函数或 Windows API 安全函数。 ## ANSI C 字符串函数 C 运行时(CRT)库函数的开销很小,因为它们在下面调用 Windows API 函数。 这些函数提供了可移植性,但有一些限制。 如果使用不当,可能会导致安全隐患。 这些函数失败时不会返回错误值。 `ansic_functions.c` ```c #include <windows.h> #include <wchar.h> #define STR_EQUAL 0 int wmain(void) { wchar_t str1[] = L"There are 15 pines"; wprintf(L"The length of the string is %lld characters\n", wcslen(str1)); wchar_t buf[20]; wcscpy(buf, L"Wuthering"); wcscat(buf, L" heights\n"); wprintf(buf); if (wcscmp(L"rain", L"rainy")== STR_EQUAL) { wprintf(L"rain and rainy are equal strings\n"); } else { wprintf(L"rain and rainy are not equal strings\n"); } return 0; } ``` 在示例中,我们介绍了 CRT 库中的一些字符串函数。 ```c wprintf(L"The length of the string is %lld characters\n", wcslen(str1)); ``` `wcslen()`返回字符串中的宽字符数。 ```c wcscpy(buf, L"Wuthering"); ``` `wcscpy()`将字符串复制到字符串缓冲区。 ```c wcscat(buf, L" heights\n"); ``` `wcscat()`函数将字符串附加到字符串缓冲区。 ```c if (wcscmp(L"rain", L"rainy")== STR_EQUAL) { wprintf(L"rain and rainy are equal strings\n"); } else { wprintf(L"rain and rainy are not equal strings\n"); } ``` `wcscmp()`比较两个字符串。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\ANSICFunctions>ANSICFunctions.exe The length of the string is 18 characters Wuthering heights rain and rainy are not equal strings ``` 这是示例的输出。 ## 安全性增强的 CRT 函数 安全性 CRT 函数为 CRT 函数增加了额外的安全性。 (它们不是标准函数,而是 MS 扩展。)这些函数验证参数,获取大小缓冲区,检查字符串是否以 NULL 终止以及提供增强的错误报告。 安全 CRT 函数的后缀为`_s`。 `security_enhanced.c` ```c #include <windows.h> #include <wchar.h> #define BUF_LEN 25 int wmain(void) { wchar_t str1[] = L"There are 15 pines"; const int MAX_CHARS = 50; size_t count = wcsnlen_s(str1, MAX_CHARS); wprintf(L"The length of the string is %ld characters\n", count); wchar_t buf[BUF_LEN] = {0}; int r = wcscpy_s(buf, BUF_LEN, L"Wuthering"); if (r != 0) { wprintf(L"wcscpy_s() failed %ld", r); } r = wcscat_s(buf, BUF_LEN, L" heights\n"); if (r != 0) { wcscat_s(L"wcscat_s() failed %ld", r); } wprintf_s(buf); return 0; } ``` 在示例中,我们展示了四个函数:`wcsnlen_s()`,`wcscpy_s()`,`wcscat_s()`和`wprintf_s()`。 ```c const int MAX_CHARS = 50; size_t count = wcsnlen_s(str1, MAX_CHARS); ``` `wcsnlen_s()`计算宽字符串的长度。 该函数仅检查前`MAX_CHARS`个字符。 ```c int r = wcscpy_s(buf, BUF_LEN, L"Wuthering"); ``` 使用`wcscpy_s()`函数,我们将`L"Wuthering"`字符串复制到缓冲区中。 该函数使用缓冲区中的最大字符数,如果失败,则返回错误代码。 该函数成功返回 0。 ```c r = wcscat_s(buf, BUF_LEN, L" heights\n"); ``` `wcscat_s()`是`wcscat()`函数的安全扩展。 ```c wprintf_s(buf); ``` 甚至还具有增强安全性的`wprintf()`函数; 它具有一些运行时约束。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\SecurityEnhanced>SecurityEnhanced.exe The length of the string is 18 characters Wuthering heights ``` 这是`SecurityEnhanced.exe`示例的输出。 ## Windows API 内核和用户字符串函数 这些函数特定于 Windows OS。 它们在`User32.lib`和`Kernel32.lib`中可用。 它们比 CRT 同类产品轻。 内核字符串函数起源于 Windows 内核的开发。 它们以`l`字母为前缀。 ### 字符串长度 最常见的要求之一是弄清楚字符串的长度。 `lstrlen()`函数以字符为单位返回指定字符串的长度。 它不计算终止的空字符。 ```c int WINAPI lstrlenA(LPCSTR lpString); int WINAPI lstrlenW(LPCWSTR lpString); ``` ANSI 和 UNICODE 函数将字符串作为参数,并返回字符串中的字符数。 `winapi_string_lenght.c` ```c #include <windows.h> #include <wchar.h> int wmain(void) { char *name = "Jane"; wchar_t *town = L"Bratislava"; wprintf(L"The length of the name string is %d\n", lstrlenA(name)); wprintf(L"The town string length is %d\n", lstrlenW(town)); return 0; } ``` 我们计算两个字符串的长度。 `lstrlen()`函数实际上是`lstrlenA()`或`lstrlenW()`的宏。 第一个用于 ANSI 字符串,第二个用于宽字符串。 ```c wprintf(L"The town string length is %d\n", lstrlenW(town)); ``` 我们使用`lstrlenW()`函数打印`L"Bratislava"`字符串的长度。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringLength>WinapiStringLength.exe The length of the name string is 4 The town string length is 10 ``` 这是`WinapiStringLength.exe`程序的输出。 ### 连接字符串 `lstrcatW()`函数将一个字符串附加到另一个字符串。 ```c LPWSTR WINAPI lstrcatW(LPWSTR lpString1, LPCWSTR lpString2); ``` 第一个参数是缓冲区,其中应包含两个字符串。 它必须足够大以包含两个字符,包括`NULL`终止字符。 返回值是指向缓冲区的指针。 `winapi_string_concat.c` ```c #include <windows.h> #include <wchar.h> int main(void) { wchar_t *s1 = L"ZetCode, "; wchar_t *s2 = L"tutorials "; wchar_t *s3 = L"for "; wchar_t *s4 = L"programmers.\n"; int len = lstrlenW(s1) + lstrlenW(s2) + lstrlenW(s3) + lstrlenW(s4); wchar_t buf[len+1]; lstrcpyW(buf, s1); lstrcatW(buf, s2); lstrcatW(buf, s3); lstrcatW(buf, s4); wprintf(buf); return 0; } ``` 在示例中,我们连接了四个字符串。 ```c wchar_t *s1 = L"ZetCode, "; wchar_t *s2 = L"tutorials "; wchar_t *s3 = L"for "; wchar_t *s4 = L"programmers.\n"; ``` 这些是我们要连接的字符串。 ```c int len = lstrlenW(s1) + lstrlenW(s2) + lstrlenW(s3) + lstrlenW(s4); ``` 我们使用`lstrlenW()`函数计算四个字符串的长度。 ```c wchar_t buf[len+1]; ``` 我们创建一个缓冲区来保存最终的字符串。 请注意,我们加 1 来包含`NULL`字符。 ```c lstrcpyW(buf, s1); ``` 我们使用`lstrcpyW()`函数将第一个字符串复制到缓冲区。 ```c lstrcatW(buf, s2); lstrcatW(buf, s3); lstrcatW(buf, s4); ``` 我们用`lstrcatW()`函数附加其余的字符串。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringConcat>WinapiStringConcat.exe ZetCode, tutorials for programmers. ``` 这是`WinapiStringConcat.exe`程序的输出。 ### 转换字符 我们有两种将字符转换为大写或小写的方法。 `CharLowerW()`函数将字符串或单个字符转换为小写。 `CharUpperW()`函数将字符串或单个字符转换为大写。 如果操作数是字符串,则该函数将就地转换字符。 换句话说,它们已被修改。 ```c LPWSTR WINAPI CharLowerW(LPWSTR lpsz); LPWSTR WINAPI CharUpperW(LPWSTR lpsz); ``` 这些函数在适当位置修改字符串,并返回指向修改后的字符串的指针。 `winapi_string_case.c` ```c #include <windows.h> #include <wchar.h> #pragma comment(lib, "User32.lib") int wmain(void) { wchar_t str[] = L"Europa"; CharLowerW(str); wprintf(L"%ls\n", str); CharUpperW(str); wprintf(L"%ls\n", str); return 0; } ``` 我们有一个字符串,可以将其转换为小写和大写形式。 ```c CharLowerW(str); wprintf(L"%ls\n", str); ``` 我们使用`CharLowerW()`方法将`str`字符串转换为小写。 字符串被修改。 ```c C:\winapi\examples2\strings\UpperLower>UpperLower.exe europa EUROPA ``` 这是`UpperLower.exe`程序的输出。 ### 比较字符串 `lstrcmpW()`函数比较两个字符串。 如果字符串相等,则返回 0。 比较是区分大小写的。 这意味着`Cup`和`cup`是两个不同的字符串。 `lstrcmpiW()`产生不区分大小写的字符串比较。 对于此函数,`Cup`和`cup`相等。 ```c int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2); int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2); ``` 这些函数采用两个字符串作为参数。 返回值指示字符串的相等性。 对于相等的字符串,返回 0 值。 `winapi_string_compare.c` ```c #include <windows.h> #include <wchar.h> #define STR_EQUAL 0 int wmain(void) { wchar_t *s1 = L"Strong"; wchar_t *s2 = L"strong"; if (lstrcmpW(s1, s2) == STR_EQUAL) { wprintf(L"%ls and %ls are equal\n", s1, s2); } else { wprintf(L"%ls and %ls are not equal\n", s1, s2); } wprintf(L"When applying case insensitive comparison:\n"); if (lstrcmpiW(s1, s2) == STR_EQUAL) { wprintf(L"%ls and %ls are equal\n", s1, s2); } else { wprintf(L"%ls and %ls are not equal\n", s1, s2); } return 0; } ``` 我们有两个字符串。 我们使用区分大小写和不区分大小写的字符串比较来比较它们。 ```c if (lstrcmpW(s1, s2) == STR_EQUAL) { wprintf(L"%ls and %ls are equal\n", s1, s2); } else { wprintf(L"%ls and %ls are not equal\n", s1, s2); } ``` 如果`lstrcmpW()`函数返回定义为 0 的`STR_EQUAL`,则我们向控制台显示两个字符串相等。 否则,我们打印出它们不相等。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringCompare>WinapiStringCompare.exe Strong and strong are not equal When applying case insensitive comparison: Strong and strong are equal ``` `WinapiStringCompare.exe`程序给出以上输出。 ### 填充缓冲区 在 C 编程中,用格式化的数据填充缓冲区至关重要。 `wsprintfW()`函数将格式化的数据写入指定的缓冲区。 ```c int __cdecl wsprintfW(LPWSTR lpOut, LPCWSTR lpFmt, ... ); ``` 该函数的第一个参数是要接收格式化输出的缓冲区。 第二个是包含格式控制规范的字符串。 然后,我们有一个或多个可选参数,它们对应于格式控制规范。 `winapi_string_fillbuffer.c` ```c #include <windows.h> #include <wchar.h> #pragma comment(lib, "User32.lib") int wmain(void) { SYSTEMTIME st = {0}; wchar_t buf[128] = {0}; GetLocalTime(&st); wsprintfW(buf, L"Today is %lu.%lu.%lu\n", st.wDay, st.wMonth, st.wYear); wprintf(buf); return 0; } ``` 我们建立一个用当前日期填充的字符串。 ```c wchar_t buf[128] = {0}; ``` 在这种特殊情况下,我们可以安全地假设字符串不会超过 128 个字符。 ```c GetLocalTime(&st); ``` `GetLocalTime()`函数检索当前的本地日期和时间。 ```c wsprintfW(buf, L"Today is %lu.%lu.%lu\n", st.wDay, st.wMonth, st.wYear); ``` `wsprintfW()`用宽字符串填充缓冲区。 根据格式说明符将参数复制到字符串。 ```c wprintf(buf); ``` 缓冲区的内容将打印到控制台。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringFillBuffer>WinapiStringFillBuffer.exe Today is 11.2.2016 ``` 这是`WinapiStringFillBuffer.exe`示例的输出。 ### 字符类型 字符有多种类型。 它们可以是数字,空格,字母,标点符号或控制字符。 ```c BOOL WINAPI GetStringTypeW(DWORD dwInfoType, LPCWSTR lpSrcStr, int cchSrc, LPWORD lpCharType); ``` `GetStringTypeW()`函数检索指定 Unicode 字符串中字符的字符类型信息。 第一个参数是指定信息类型的标志。 | 标志 | 含义 | | --- | --- | | `CT_CTYPE1` | 检索字符类型信息。 | | `CT_CTYPE2` | 检索双向布局信息。 | | `CT_CTYPE3` | 检索文本处理信息。 | Table: Character info types 第二个参数是要检索其字符类型的 Unicode 字符串。 第三个参数是字符串的大小。 最后一个参数是指向 16 位值数组的指针。 该数组的长度必须足够大,以便为源字符串中的每个字符接收一个 16 位值。 该数组将包含一个与源字符串中每个字符相对应的单词。 `GetStringTypeW()`函数返回一个值,该值是类型的组合。 我们可以使用&运算符查询特定类型。 | 值 | 含义 | | --- | --- | | `C1_DIGIT` | 小数位数 | | `C1_SPACE` | 空格字符 | | `C1_PUNCT` | 标点 | | `C1_CNTRL` | 控制字符 | | `C1_ALPHA` | 任何语言特征 | Table: Partial list of character types 如果失败,该函数将返回 0。 `winapi_string_types.c` ```c #include <windows.h> #include <wchar.h> #include <stdbool.h> int wmain(void) { wchar_t str[] = L"7 white, 3 red roses.\n"; int alphas = 0; int digits = 0; int spaces = 0; int puncts = 0; int contrs = 0; int size = lstrlenW(str); WORD types[size]; ZeroMemory(types, size); bool rv = GetStringTypeW(CT_CTYPE1, str, size, types); if (!rv) { wprintf(L"Could not get character types (%ld)", GetLastError()); return EXIT_FAILURE; } for (int i=0; i<size; i++) { if (types[i] & C1_ALPHA) { alphas++; } if (types[i] & C1_DIGIT) { digits++; } if (types[i] & C1_SPACE) { spaces++; } if (types[i] & C1_PUNCT) { puncts++; } if (types[i] & C1_CNTRL) { contrs++; } } wprintf(L"There are %ld letter(s), %ld digit(s), " L"%ld space(s), %ld punctuation character(s), " L"and %ld control character(s)\n", alphas, digits, spaces, puncts, contrs); return 0; } ``` 我们有一句话。 `GetStringTypeW()`函数用于确定字符串的字符类型。 ```c wchar_t str[] = L"7 white, 3 red roses.\n"; ``` 这是由各种宽字符组成的简短句子。 ```c int alphas = 0; int digits = 0; int spaces = 0; int puncts = 0; int contrs = 0; ``` 这些变量将用于计算字母,数字,空格,标点和控制字符。 ```c int size = lstrlenW(str); WORD types[size]; ZeroMemory(types, size); ``` 我们获取字符串的大小,并创建值数组。 大小不包含`NULL`终止字符。 我们可以加 1 以包括它。 它将被算作控制字符。 ```c bool rv = GetStringTypeW(CT_CTYPE1, str, size, types); ``` 我们得到句子的字符类型。 `types`数组填充有字符类型值。 ```c if (types[i] & C1_DIGIT) { digits++; } ``` 如果该值包含`C1_DIGIT`标志,我们将增加数字计数器。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringTypes>WinapiStringTypes.exe There are 13 letter(s), 2 digit(s), 5 space(s), 2 punctuation character(s), and 1 control character(s) ``` 这是`WinapiStringTypes.exe`示例的输出。 ## Windows API Shell 轻量级工具函数 这些函数特定于 Windows OS。 它们在`Shlwapi.lib`中可用。 ### 修剪字符串 `StrTrimW()`函数从字符串中删除指定的开头和结尾字符。 如果删除了任何字符,则返回`true`;否则返回`false`。 否则为假。 ```c BOOL WINAPI StrTrimW(LPWSTR psz, LPCWSTR pszTrimChars); ``` 第一个参数是指向要修剪的字符串的指针。 当此函数成功返回时,`psz`接收到修剪后的字符串。 第二个参数是一个指向字符串的指针,该字符串包含要从`psz`中修剪的字符。 `winapi_shell_trim.c` ```c #include <windows.h> #include <wchar.h> #include <stdbool.h> #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") int wmain(void) { wchar_t buf[] = L"23tennis74"; wchar_t trim[] = L"0123456789"; wprintf(L"Original string: %ls\n", buf); bool r = StrTrimW(buf, trim); if (r == true) { wprintf(L"The StrTrim() trimmed some characters\n", buf); } else { wprintf(L"No characters were trimmed\n", buf); } wprintf(L"Trimmed string: %ls\n", buf); return 0; } ``` 在该示例中,我们从字符串中删除所有数字。 ```c wchar_t buf[] = L"23tennis74"; ``` 我们将从该字符串中删除所有数字。 ```c wchar_t trim[] = L"0123456789"; ``` 该字符串包含所有要删除的字符。 ```c bool r = StrTrimW(buf, trim); ``` 使用`StrTrimW()`函数,我们可以修剪缓冲区中的数字。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\ShellTrimString>ShellTrimString.exe Original string: 23tennis74 The StrTrim() trimmed some characters Trimmed string: tennis ``` 这是`ShellTrimString.exe`示例的输出。 ### 将字符串转换为整数 `StrToIntExW()`将代表十进制或十六进制数字的字符串转换为整数。 成功时该函数返回`true`。 ```c BOOL WINAPI StrToIntExW(LPCWSTR pszString, DWORD dwFlags, int *piRet); ``` 第一个参数是指向要转换的字符串的指针。 第二个参数是标志之一,用于指定应如何解析`pszString`以将其转换为整数。 第三个参数是指向接收转换后的字符串的`int`的指针。 `winapi_shell_convert.c` ```c #include <windows.h> #include <wchar.h> #include <stdbool.h> #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") int wmain(void) { wchar_t str1[] = L"512"; wchar_t str2[] = L"0xAB12"; int n = 0; bool r = StrToIntExW(str1, STIF_DEFAULT, &n); if (r == true) { wprintf(L"The value is %d\n", n); } else { wprintf(L"The first conversion failed\n"); return 1; } r = StrToIntExW(str2, STIF_SUPPORT_HEX, &n); if (r == true) { wprintf(L"The value is %d\n", n); } else { wprintf(L"The second conversion failed\n"); return 1; } return 0; } ``` 在示例中,我们转换了两个字符串; 一个代表十进制值,一个代表十六进制值。 ```c wchar_t str1[] = L"512"; wchar_t str2[] = L"0xAB12"; ``` 第一个字符串代表十进制数;第二个字符串代表十进制数。 第二个字符串表示一个十六进制数。 ```c bool r = StrToIntExW(str1, STIF_DEFAULT, &n); ``` 使用`StrToIntExW()`函数,我们将第一个字符串转换为整数。 `STIF_DEFAULT`标志告诉函数转换十进制值。 ```c r = StrToIntExW(str2, STIF_SUPPORT_HEX, &n); ``` 通过`STIF_SUPPORT_HEX`标志,我们告诉函数转换一个十六进制值。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\ShellConvertString>ShellConvertString.exe The value is 512 The value is 43794 ``` 这是`ShellConvertString.exe`程序的输出。 ### 搜索字符串 `StrStrW()`函数查找字符串中子字符串的首次出现。 比较是区分大小写的。 ```c LPWSTR WINAPI StrStrW(LPCWSTR pszFirst, LPCWSTR pszSrch); ``` 第一个参数是指向要搜索的字符串的指针。 第二个参数是指向要搜索的子字符串的指针。 如果成功,该函数将返回匹配子字符串的第一个匹配项的地址,否则返回`NULL`。 `winapi_shell_search.c` ```c #include <windows.h> #include <wchar.h> #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") int wmain(void) { wchar_t buf[] = L"Today is a rainy day."; wchar_t *search_word = L"rainy"; int len = wcslen(search_word); LPWSTR pr = StrStrW(buf, search_word); if (pr == NULL) { wprintf(L"No match\n", buf); } else { wprintf(L"%.*ls is found\n", len, pr); } return 0; } ``` 在代码示例中,我们在句子中搜索单词。 ```c wchar_t buf[] = L"Today is a rainy day."; ``` 我们从这句话中搜索一个单词。 ```c wchar_t *search_word = L"rainy"; ``` 这是我们要搜索的词。 ```c LPWSTR pr = StrStrW(buf, search_word); ``` `StrStrW()`函数在句子中搜索单词。 如果成功,它将返回一个指向匹配子字符串的指针。 ```c C:\Users\Jano\Documents\Pelles C Projects\strings\ShellSearchString>ShellSearchString.exe rainy is found ``` 这是`ShellSearchString.exe`程序的输出。 ## Windows API StrSafe 函数 为了提高应用的安全性,发布了 StrSafe 函数。 这些函数需要目标缓冲区的大小作为输入。 保证缓冲区为空终止。 该函数返回错误代码。 这样可以进行正确的错误处理。 每个函数都有相应的字符计数`Cch`或字节计数`Cb`版本。 ### 字符串长度 `StringCchLengthW()`和`StringCbLengthW()`函数可以确定字符和字节的字符串长度。 ```c HRESULT StringCchLengthW(LPCWSTR psz, size_t cchMax, size_t *pcch); HRESULT StringCbLengthW(LPCWSTR psz, size_t cbMax, size_t *pcb); ``` 函数的第一个参数是要检查其长度的字符串。 第二个参数是`psz`参数中允许的最大字符数(字节)。 该值不能超过`STRSAFE_MAX_CCH`。 第三个参数是`psz`中的字符数(字节),不包括终止的空字符。 函数成功返回`S_OK`,失败返回`STRSAFE_E_INVALID_PARAMETER`。 如果`psz`中的值为`NULL`,`cchMax`大于`STRSAFE_MAX_CCH`或`psz`大于`cchMax`,则函数将失败。 `SUCCEEDED`和`FAILED`宏可用于检查函数的返回值。 `safe_length.c` ```c #include <windows.h> #include <strsafe.h> #include <wchar.h> int wmain(void) { wchar_t str[] = L"ZetCode"; size_t target_size = 0; size_t size = sizeof(str); HRESULT r = StringCbLengthW(str, size, &target_size); if (SUCCEEDED(r)) { wprintf(L"The string has %lld bytes\n", target_size); } else { wprintf(L"StringCbLengthW() failed\n"); return 1; } size = sizeof(str)/sizeof(wchar_t); r = StringCchLengthW(str, size, &target_size); if (SUCCEEDED(r)) { wprintf(L"The string has %lld characters\n", target_size); } else { wprintf(L"StringCchLengthW() failed\n"); return 1; } return 0; } ``` 该代码示例确定给定字符串(以字符和字节为单位)的长度。 ```c wchar_t str[] = L"ZetCode"; ``` 我们将确定此字符串的长度。 ```c size_t target_size = 0; ``` 函数返回时,`target_size`变量将填充计数值。 ```c size_t size = sizeof(str); ``` 使用`sizeof`运算符,我们获得字符数组的大小(以字节为单位)。 该值用作`StringCbLengthW()`函数的字符串中允许的最大字符数。 ```c HRESULT r = StringCbLengthW(str, size, &target_size); ``` 使用`StringCbLengthW()`函数,我们可以确定字符串的长度(以字节为单位)。 长度存储在`target_size`变量中。 ```c if (SUCCEEDED(r)) { wprintf(L"The string has %lld bytes\n", target_size); } else { wprintf(L"StringCbLengthW() failed\n"); return 1; } ``` 我们使用`SUCCEEDED`宏检查返回的值。 成功后,我们将打印字符串中的字节数。 如果出现错误,我们将显示一条错误消息。 ```c size = sizeof(str)/sizeof(wchar_t); ``` 在这里,我们确定字符串中允许的最大字符数。 `wchar_t`是一种宽字符类型; 它的大小取决于编译器。 ```c r = StringCchLengthW(str, size, &target_size); ``` 使用`StringCchLengthW()`函数,我们可以得到字符串的大小(以字符为单位)。 ```c if (SUCCEEDED(r)) { wprintf(L"The string has %lld characters\n", target_size); } else { wprintf(L"StringCchLengthW() failed\n"); return 1; } ``` 成功后,我们将字符串中的字符数打印到控制台。 出现错误时,我们会打印一条错误消息。 ```c C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeLength>SafeLength.exe The string has 14 bytes The string has 7 characters ``` 该字符串包含 14 个字节或 7 个字符。 ### 读取标准输入 `StringCchGetsW()`从标准输入读取一行,包括换行符。 ```c HRESULT StringCchGetsW(LPWSTR pszDest, size_t cchDest); ``` 第一个参数是目标缓冲区,它接收复制的字符。 第二个参数是目标缓冲区的大小(以字符为单位)。 `safe_gets.c` ```c #include <windows.h> #include <strsafe.h> #include <wchar.h> #define BUF_LEN 8191 int wmain(void) { wchar_t buf[BUF_LEN] = {0}; HRESULT r = StringCchGetsW(buf, ARRAYSIZE(buf)); if (SUCCEEDED(r)) { wprintf(L"You have entered: %ls\n", buf); } else { wprintf(L"StringCchGets() failed\n"); return 1; } return 0; } ``` 在示例中,我们从标准输入中读取了一行。 该行被打印回到控制台。 ```c #define BUF_LEN 8191 ``` 根据 MSDN 文档,命令提示符下的最大输入不能超过 8191 个字符。 ```c wchar_t buf[BUF_LEN] = {0}; ``` 我们为输入字符串创建一个缓冲区。 ```c HRESULT r = StringCchGetsW(buf, ARRAYSIZE(buf)); ``` `StringCchGetsW()`从`stdin`读取一行。 ```c C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeGets>SafeGets.exe Today is a rainy day. You have entered: Today is a rainy day. ``` 这是`SafeGets.exe`程序的示例运行。 ### 复制字符串 `StringCchCopyW()`将一个字符串复制到另一个。 ```c HRESULT StringCchCopyW(LPTSTR pszDest, size_t cchDest, LPCWSTR pszSrc); ``` 第一个参数是目标缓冲区,它接收复制的字符串。 第二个参数是目标缓冲区的大小(以字符为单位)。 第三个参数是源字符串。 `safe_copy.c` ```c #include <windows.h> #include <strsafe.h> #include <wchar.h> int wmain(void) { wchar_t *sentence = L"Today is a rainy day."; size_t size = wcslen(sentence) + 1; wchar_t buf[size]; ZeroMemory(buf, size); HRESULT r = StringCchCopyW(buf, size, sentence); if (SUCCEEDED(r)) { wprintf(L"%ls\n", buf); } else { wprintf(L"StringCchCopyW() failed\n"); return 1; } return 0; } ``` 在代码示例中,我们使用`StringCchCopyW()`函数复制一个字符串。 ```c wchar_t *sentence = L"Today is a rainy day."; ``` 这是要复制的字符串。 ```c size_t size = wcslen(sentence) + 1; ``` 我们用`wcslen()`函数确定它的长度; 换行符保留一个字符。 ```c wchar_t buf[size]; ZeroMemory(buf, size); ``` 我们使用`ZeroMemory()`函数创建一个缓冲区,并使用零。 ```c HRESULT r = StringCchCopyW(buf, size, sentence); ``` 使用`StringCchCopyW()`,我们将字符串复制到缓冲区中。 提供目标缓冲区的大小是为了确保它不会在该缓冲区的末尾写入。 ```c C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeCopy>SafeCopy.exe Today is a rainy day. ``` 这是`SafeCopy.exe`程序的输出。 ### 连接字符串 `StringCchCatW()`将一个字符串连接到另一个字符串。 ```c HRESULT StringCchCatW(LPWSTR pszDest, size_t cchDest, LPCWSTR pszSrc); ``` 第一个参数是目标缓冲区。 第二个参数是目标缓冲区的大小(以字符为单位)。 第三个参数是要连接到目标缓冲区末尾的源字符串。 `safe_concat.c` ```c #include <windows.h> #include <strsafe.h> #include <wchar.h> #define BUF_LEN 256 int wmain(void) { wchar_t buf[BUF_LEN] = {0}; HRESULT r = StringCchCatW(buf, BUF_LEN, L"Hello "); if (FAILED(r)) { wprintf(L"StringCchCatW() failed\n"); return 1; } r = StringCchCatW(buf, BUF_LEN, L"there"); if (FAILED(r)) { wprintf(L"StringCchCatW() failed\n"); return 1; } wprintf(L"%ls\n", buf); return 0; } ``` 在代码示例中,我们使用`StringCchCatW()`函数将两个字符串连接在一起。 ```c HRESULT r = StringCchCatW(buf, BUF_LEN, L"Hello "); ``` `StringCchCatW()`函数将`L"Hello "`字符串添加到`buf`数组。 ```c r = StringCchCatW(buf, BUF_LEN, L"there"); ``` 稍后,第二个字符串将添加到缓冲区。 ```c C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeConcat>SafeConcat.exe Hello there ``` 这是`SafeConcat.exe`程序的输出。 ### 格式化字符串 `StringCchPrintfW()`函数将格式化的数据写入目标缓冲区。 ```c HRESULT StringCchPrintfW(LPWSTR pszDest, size_t cchDest, LPCWSTR pszFormat, ...); ``` 第一个参数是目标缓冲区,它接收从`pszFormat`及其参数创建的格式化字符串。 第二个参数是目标缓冲区,以字符为单位。 第三个参数是格式字符串。 将以下参数插入`pszFormat`字符串中。 `safe_format.c` ```c #include <windows.h> #include <strsafe.h> #include <wchar.h> #define BUF_LEN 256 int wmain(void) { wchar_t *word = L"table"; int count = 6; wchar_t buf[BUF_LEN] = {0}; wchar_t *line = L"There are %d %lss"; HRESULT r = StringCchPrintfW(buf, ARRAYSIZE(buf), line, count, word); if (SUCCEEDED(r)) { wprintf(L"%ls\n", buf); } else { wprintf(L"StringCchPrintfW() failed\n"); return 1; } return 0; } ``` 在代码示例中,我们使用`StringCchPrintfW()`函数创建格式化的字符串。 ```c wchar_t *line = L"There are %d %lss"; ``` 这是格式字符串; 它具有两个格式说明符:`%d`和`%ls`。 ```c HRESULT r = StringCchPrintfW(buf, ARRAYSIZE(buf), line, count, word); ``` 使用`StringCchPrintfW()`函数,我们将两个值插入目标缓冲区。 ```c C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeFormat>SafeFormat.exe There are 6 tables ``` 这是`SafeFormat.exe`程序的输出。 在 Windows API 教程的这一部分中,我们使用了字符串。