C\C++語言22|C風格字符串及其字符串函數、字符串類

編譯器 編程語言 小智雅匯 2019-06-04

字符串處理是任何編程語言都要涉及到的內容。

在C中,字符串是以空字符'\0'為終止元素的字符數組來表示的,稱為C風格的字符串,C標準庫同時在庫<string.h>中提供了一族字符串操作函數。

在C++中,字符串被封裝為類,由類庫<string>提供相應成員函數的接口和實現。

相對來說,C風格字符串效率較高,string類安全性要好。同時,因為C++出現的時間相對於C較晚,一些函數庫是在C++出現之前開發的,所以使用的是C風格的字符串,而不是string類,如win32 API。

1 C風格字符串及其庫函數

在C中,字符串是以空字符(\0)結尾的字符數組。

可以像其他數組那樣聲明並初始化字符串:

char carr[] = {'w','w','u','h','n','\0'};

也可以用字面量初始化字符串的簡捷方式:

char carr[] = "wwuhn";

這種初始化方法不需要使用空字符,而是由編譯器自動添加。

當然也可以使用指針的方式:

char *cp = "wwuhn";

字符串的字面量會保存到內存的數據段(字符串以外的字面量會以代碼的形式存在),返回的是一個常量指針。數組初始化不一樣,其右值的字符串保存在棧中。

所以,cp指向的是字面量,相當於常量,不能單個元素更改(其本身cp是一個變量,可以另有指向)。

cp[2] = 'r'; //錯誤
cp = carr; //正確

因為其是常量指針,所以一般寫成:

const char *cp = "wwuhn";

carr是常量,數組名是指針常量,本身不能再另有指向,而其指向的內容卻是可以更新的:

carr[2] = 'r'; //正確
carr = cp; //錯誤

C或C++的函數庫中提供了一些用來處理字符串的函數。這些函數在庫<string.h>(C++中寫成<cstring>中。

空字符'\0'標記字符數組中存儲的C 字符串的結束。如果字符數組以這種方式使用,該數組就稱為C 字符串變量。雖然空字符'\0'要寫成兩個符號,但實際只代表一個字符,可以用char 類型的變量或者字符數組的索引變量來存儲它。

不能用=對C 字符串變量進行賦值。用==測試C 字符串相等性也得不到你希望的結果。原因很簡單,因為C 字符串和C 字符串變量本質上是數組。

為C 字符串變量賦值不像為其他類型的變量賦值那麼簡單。

char aString[10];
aString = "Hello"; //非法,只能在初始化時賦值

雖然可在聲明時用等號為C 字符串變量賦值,但在程序的其他任何地方都不能這樣做。從技術上說,在聲明時使用等號,就像下面這樣:

char happyString[7] = "DoBeDo";

它其實是一個初始化操作,而不是賦值操作。要為C 字符串變量賦值,必須採取其他方式。

可採取許多不同的方式為C 字符串變量賦值。最簡單的是使用預定義函數strcpy,

strcpy(aString, "Hello");

這也是為什麼將後面的值複製到前面的值,而不是返回一個新的存儲空間的指針的原因。

這會將aString 的值設為"Hello"。遺憾的是,strcpy 函數的這個版本不檢查複製的字符串是否超過字符串變量(第一個參數)的長度。

許多(但並非全部)C++版本還提供了strcpy 的一個更安全的版本。這個安全版本要拼寫成strncpy(多了一個n)。strncpy 函數要獲取第3 個參數,指定最多能複製多少字符。

char anotherString[10];
strncpy(anotherString, aStringVariable, 9);

使用這個strncpy 函數,最多能從C 字符串變量aStringVariable 中複製9 個字符(包括'\0'),無論aStringVariable 中的字符串有多長。

也不能在表達式中使用操作符==測試兩個C 字符串是否相等。更糟的是,可以為C 字符串使用==,但作用不是測試C 字符串是否相等。所以,用==測試兩個C 字符串的相等性可能得到錯誤結果,而且編譯器不報告任何錯誤!要測試兩個C 字符串是否相等,可以使用預定義函數strcmp。例如下面的代碼:

if (strcmp(cString1, cString2))
cout << "The strings are NOT the same.";
else
cout << "The strings are the same.";

注意,strcmp 函數的工作方式可能和你想的不同。兩個字符串不匹配,結果反而是true。strcmp 函數每次比較C 字符串參數的一個字符。任何時候只要cString1 的一個字符的數值編碼小於cString2 的對應字符的數值編碼,測試就會停止,並返回一個負數。

如果cString1 的字符大於cString2 的對應字符,則返回一個正數(strcmp 的有些實現返回字符編碼的差值,但不應依賴於此)。

兩個C 字符串完全相同,則返回0。字符比較依據的是詞典順序(lexicographic order)。它的重點是,兩個字符串採用全部大寫或小寫的形式,詞典順序就和字母順序一樣。

基於詞典順序,在C 字符串比較結果是小於、大於或者等於的情況下,strcmp 分別返回負值、正值或零。在if 或循環語句中將strcmp 作為布爾表達式使用,在字符串不相等的前提下,返回的所有非零值都被轉換成true;在字符串相等的前提下,返回的零值被轉換成false。所以在測試C 字符串的相等性時,務必記住這一反轉邏輯。

字符串庫函數(包括內存塊函數):

strcat()	連接兩個字符串
strchr() 查找某字符在字符串中首次出現的位置
strcmp() 比較兩個字符串
strcoll() 採用目前區域的字符排列次序來比較字符串
strcpy() 拷貝字符串
strcspn() 在某字符串中匹配指定字符串
strerror() 返回錯誤碼對應的文本信息
strlen() 返回指定字符串的長度
strncat() 連接某一長度的兩個字符串
strncmp() 比較某一長度的兩個字符串
strncpy() 複製某一長度的一個字符串到另一字符串中
strpbrk() 查找某字符串在另一字符串中首次出現的位置
strrchr() 查找某字符在字符串中末次出現的位置
strspn() 返回子串的長度,子串的字符都出現包含於另一字符串中
strstr() 在一字符串中查找指定的子串首次出現的位置
strtod() 將字符串轉換成浮點數
strtok() 查找指定字符之前的子串
strtol() 將字符串轉換成長整型數
strtoul() 將字符串轉換成無符號長整型數
strxfrm() 轉換子串, 可以用於字符串比較
memchr() 在某一內存範圍中查找一特定字符
memcmp() 比較內存內容
memcpy() 拷貝內存內容
memmove() 拷貝內存內容
memset() 將一段內存空間填入某值

2 字符串格式化

在將各種類型的數據構造成字符串時,sprintf 的強大功能很少會讓你失望。由於sprintf 跟printf 在用法上幾乎一樣,只是打印的目的地不同而已,前者打印到字符串中,後者則直接在命令行上輸出。這也導致sprintf 比printf 有用得多。

sprintf指的是字符串格式化命令,主要功能是把格式化的數據寫入某個字符串中。sprintf 是個變參函數。使用sprintf 對於寫入buffer的字符數是沒有限制的,這就存在了buffer溢出的可能性。解決這個問題,可以考慮使用 snprintf函數,該函數可對寫入字符數做出限制。

實例:

#include <stdio.h>
#include <stdlib.h>
int main()

{
char buffer[200], s[] = "computer", c = 'l';
int i = 35, j;
float fp = 1.7320534f;
// 格式化並打印各種數據到buffer
j = sprintf( buffer, " String: %s\n", s );
j += sprintf( buffer + j, " Character: %c\n", c );
j += sprintf( buffer + j, " Integer: %d\n", i );
j += sprintf( buffer + j, " Real: %f\n", fp );

printf( "Output:\n%s\ncharacter count = %d\n", buffer, j );
system("pause");
return 0;
}

輸出:

C\C++語言22|C風格字符串及其字符串函數、字符串類

3 C++string類

string 類在名稱同為的庫中定義,定義放在std 命名空間中。所以,要使用string 類,代碼必須包含以下語句(或其他等價的語句):

#include <string>
using namespace std;

string 類的默認構造函數將string 對象初始化成空字符串,另一個構造函數獲取一個C 字符串作為參數,將string 對象初始化成一個值來表示由參數給定的字符串。例如:

string s1, s2("Hello");

string對象要獲得C風格表示,可使用成員函數c_str()。

string類函數成員:

append()	在字符串的末尾添加文本
assign() 為字符串賦新值
at() 按給定索引值返回字符
begin() 返回一個迭代器,指向第一個字符
c_str() 將字符串以C字符數組的形式返回
capacity() 返回重新分配空間前的字符容量
compare() 比較兩個字符串
copy() 將內容複製為一個字符數組
data() 返回內容的字符數組形式
empty() 如果字符串為空,返回真
end() 返回一個迭代器,指向字符串的末尾。(最後一個字符的下一個位置)
erase() 刪除字符
find() 在字符串中查找字符
find_first_of() 查找第一個與value中的某值相等的字符
find_first_not_of() 查找第一個與value中的所有值都不相等的字符
find_last_of() 查找最後一個與value中的某值相等的字符
find_last_not_of() 查找最後一個與value中的所有值都不相等的字符
get_allocator() 返回配置器
insert() 插入字符
length() 返回字符串的長度
max_size() 返回字符的最大可能個數
rbegin() 返回一個逆向迭代器,指向最後一個字符
rend() 返回一個逆向迭代器,指向第一個元素的前一個位置
replace() 替換字符
reserve() 保留一定容量以容納字符串(設置capacity值)
resize() 重新設置字符串的大小
rfind() 查找最後一個與value相等的字符(逆向查找)
size() 返回字符串中字符的數量
substr() 返回某個子字符串
swap() 交換兩個字符串的內容

4 寬字符串

對於字符類型char,其寬度為1個字節,用多個字節來代表的字符稱之為寬字符。

<wchar.h>中定義了類型wchar_t(unsigned short),用來表示WCHAR,及寬字符。

寬字符字符串表示為一個 wchar_t[] 數組並由 wchar_t* 指針指向它。可以通過用字母 L 作為字符的前綴將任何 ASCII 字符表示為寬字符形式。例如,L'\0' 是終止寬(16 位)NULL 字符。同樣,可以通過用字母 L 作為 ASCII 字符串的前綴 (L"Hello") 將任何 ASCII 字符串表示為寬字符字符串形式。

通常,寬字符在內存中佔用的空間比多字節字符多,但處理速度更快,因為很多系統的內核包括Windows NT內核都是從底層向上使用Unicode編碼的。另外,在多字節編碼中一次只能表示一個區域設置,Unicode編碼可以毫無障礙地在世界上任何書面語言的字符中轉換。

實例1


#include <iostream>
using namespace std;
#include <wchar.h>
void main()
{
wchar_t wstr[] = L"hi";
wcout<<wstr<<endl;
}

實例2

#include <tchar.h>
#include <stdio.h>
void main()
{
TCHAR wstr[] = "你好";
printf("%s\n",wstr);
}

實例3

#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
char input[50];
cout<<"請輸入包含漢字的字符串:";
cin>>input;
int size;
size=::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,NULL,0);
if(size==0)
return -1;
wchar_t *widebuff=new wchar_t[size];
::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,widebuff,size);

cout<<endl<<"輸出:"<<input<<endl;

system("pause");
}
/*
請輸入包含漢字的字符串:中國abc
輸出:中國abc
/*

當然,也可以用字符串類string來處理:

std::string是ANSI,但std::wstring是Unicode。如果要定義Unicode,只需要在正常的ANSI字符串前加上“L”就可以了。

#include <iostream>
#include <string>
using namespace std;
void main()
{
string str = "你好";
wstring str2 = L"你好";
cout<<str.length()<<endl;//4
cout<<str2.length()<<endl;//2,string處理wstring有問題
wcout.imbue(locale("chs"));
wcout<<str2<<endl;//你好
}

-End-

相關推薦

推薦中...