引入
上次我在博客里介紹了OI中可能用到的STL中的功能, 今天我們接着來發掘C++標准庫中能為OI所用的部分.
眾所周知, OI中經常用到字符串相關的處理, 這時善用字符串庫可以使一些操作更加簡潔易懂並減少手打代碼量與錯誤概率, 特別是在一些對效率要求不太嚴格的應用或者隨機數據的應用下.
字符串庫
C++的字符串庫的內容主要定義在頭文件 <string> 中, (注意要和 <cstring> 和 <string.h> 區分開, 前者是C++字符串庫, 后面的兩個都是用來操作C風格字符串(C-Style String, C++中似乎還叫做空終止字符串(Null-terminated String), 本質上是一個以空字符為結尾標志的char類型的數組)的庫), 還有一些字符分類與本地化函數分布在 <cctype> <cstring> <cstdlib> <cwchar> <cuchar> <cwctype> 中. 空終止字符串還分為空終止單字節字符串/空終止寬字符串/空終止多字節字符串. 由於OI中處理的字符多半都是ASCII字符, 寬字符和多字節字符的使用極少(除非是各種OJ上的毒瘤出題人出的奇葩毒瘤題), 本文在空終止字符串的部分中僅對空終止單字節字符串的有關操作進行說明.
由上述頭文件所共同定義的C++字符串庫定義了兩種(在C++17前)字符串通用類型: std::basic_string 和空終止字符串. (C++17新增了 std::basic_string_view 類, 可以以輕量且對數據無所有權的只讀方式訪問字符串的子序列. 但是OI里似乎沒什么用而且OI連C++11的支持都是問題(笑))
<string>與std::basic_string
<string> 頭文件定義了 std::basic_string 類型, 提供對於字符序列的各種操作, 並使用 typedef 定義了一些常用類型的特化版本. 列表如下:
特化類型 | 原類型 |
std::string | std::basic_string<char> |
std::wstring | std::basic_string<wchar> |
std::u16string | std::basic_string<char16_t> |
std::u32string | std::basic_string<char32_t> |
其中 std::u16string 與 std::u32string 為C++11及以后所定義的特化類型.
std::basic_string 像 std::vector 一樣連續存儲保存在其中的數據. 模板參數如下:
template< class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> > class basic_string;
第一個必須指定的模板參數為字符類型, 第二個參數指定字符串的容器, 第三個參數指定內存分配方式. 其中如果要指定第二個參數的話必須保證二者所指名的類型為同一類, 否則會UB. OI里會用到的估計也就是特化類型 std::string 了OwO
std::basic_string的成員函數
成員函數均可在basic_string對象上使用 . 運算符來調用
operator=
用於支持字符串間的賦值. (數組不是一等公民系列23333)
at 和 operator[]
訪問字符串中的成員字符. 前者提供下標越界檢查並在下標越界時拋出 std::out_of_range 異常, 后者提供像普通字符數組一樣的訪問方式, 然而並不檢查下標OwO
data
返回指向第一個字符的指針. 可以用這個指針來進行C風格的修改操作, 或者其他需要用到可更改的char指針的應用. (一般沒有必須這樣的操作因為 std::basic_string 的接口已經很完備了OwO)
c_str
返回一個不可用於修改字符值的指向第一個字符的指針, 用於將 std::basic_string 傳遞給標准庫中需要C風格字符串的地方(多為原來C標准庫的內容) 比如 std::system 啥的
begin 與 cbegin
返回一個指向第一個字符的迭代器. 關於迭代器可以見我上一篇關於C++標准庫的博文. 不同的是前者返回的迭代器是可變迭代器而后者為常迭代器.
迭代器滿足隨機訪問迭代器的要求.
這是C++11及以后支持的特性.
end 與 cend
返回指向最后一個字符的下一個位置的迭代器. 這兩個函數仍然是可變迭代器與常迭代器的區別.
這是C++11及以后支持的特性.
rbegin 與 crbegin
返回指向字符串最后一個字符的反向迭代器. 后者為常迭代器.
這是C++11及以后支持的特性.
empty
與STL容器類似, 返回字符串是否為空.
size 與 length
返回字符串的長度. 二者(似乎)並沒有什么區別(除了函數名的長度)
clear
良心操作, 清空字符串OwO (謎一樣的容器適配器)
insert
將一個字符/一個basic_string/一個字符數組(C風格字符串/空終止字符串)插入指定位置. 第一個參數為插入位置, 第二個參數為要插入的東西.
對於字符來說第二個參數為要插入的數量, 第三個參數為要插入的字符
對於basic_string來說可以選擇第三/四個參數來指定一個子串, 分別指定開始插入的位置與要插入的子串長度.
對於空終止字符串來說可以選擇第三個參數來指定要插入的長度.
erase
可以指定兩個整數參數, $index$ 和 $count$ ,表示從下標 $index$ 開始刪除 $count$ 個字符.
或者刪除一個迭代器指向的一個字符.
也可以通過兩個迭代器在字符串內划分一個左閉右開區間來將其從字符串中刪除.
push_back
向字符串末尾添加一個字符. 時間復雜度 $O(1)$
operator+=
將一個字符/一個basic_string/一個空終止字符串附到字符串末尾.
compare
比較兩個字符串.
1. 與另一個basic_string比較
2. 先接受兩個參數指定要比較以某個下標開始的若干字符, 用指定的字符得到的子串與第三個參數(basic_string)比較
3. 先指定要比較的子串, 然后指定要比較的basic_string, 然后兩個參數用於在要比較的basic_string中指定一個子串, 比較划分出的兩個子串.
4. 與另一個空終止字符串比較
5. 划定一個子串與另一個空終止字符串比較
6. 接受兩個參數划定一個子串用於與另一個空終止字符串的前若干字符比較.
如果該字符串的字典序小於要比較的字符串則該函數返回一個小於零的值
如果比較結果為相等則返回零.
如果該字符串的字典序大於要比較的字符串則該函數返回一個大於零的值.
replace
將字符串的一個子串替換成指定的字符串. 划分子串可以用起始下標+長度划分, 也可以用一對迭代器划分.
這個指定的字符串可以是basic_string, 也可以是basic_string后跟着兩個用於划分子串的參數(將字符串的子串替換成指定字符串的子串), 或者是空終止字符串, 空終止字符串可以配合長度數據來截取.
substr
通過起始下標與長度來截取一個子串, 返回這個截得的子串.
find , rfind , find_first_of , find_first_not_of , find_last_of , find_last_not_of
在字符串中按照各自的規則查找給定的模式串, 成功則返回對應下標, 否則返回 std::basic_string::npos
以上六個查找函數的參數格式都一樣, 支持匹配basic_string格式的字符串/空終止字符串/一個字符, 且都支持指定要查找的模式串的要匹配的長度/區間.
以上函數的查找規則如下表:
函數 | 匹配規則 |
find | 在字符串中尋找字符(串) |
rfind | 尋找字符(串)最后一次出現的位置 |
find_first_of | 尋找字符串中任意字符第一次出現的位置 |
find_first_not_of | 尋找字符串中任意字符第一次缺失的位置 |
find_last_of | 尋找字符串中任意字符最后一次出現的位置 |
find_last_not_of | 尋找字符串中任意字符最后一次缺失的位置 |
需要注意的是這里使用的是朴素匹配算法, 在隨機數據中表現出色, 但是可能被特殊數據卡掉.
std::basic_string的靜態成員常量
npos
一個特殊值, 具體含義根據上下文可以解釋為查找失敗/字符串的結束等等
訪問方式為直接使用 std::basic_string::npos
std::basic_string的友元函數
operator+
可以用於連接一個std::basic_string和另一個std::basic_string/另一個空終止字符串/另一個字符. 就像平常理解的加號一樣OwO
六種大小比較運算符 != == < <= > >=
按字典序比較兩個basic_string的內容.
<cctype>與<cwctype>
<cctype> 和 <cwctype> 分別針對 char 類型和 wchar_t 類型定義了一系列字符分類與字符操作函數. 字符分類函數可以判斷字符是否屬於某類型, 比如數字/字母/特殊符號/可見字符/控制字符等等. 字符操作函數可以給字符轉一下大小寫啥的OwO
字符分類函數
字符分類函數有一大坨來着....懶癌發作直接列表(逃
函數 | 功能 |
isalnum iswalnum | 檢查字符是否是字母或數字 |
isalpha iswalpha | 檢查字符是否是字母 |
islower iswlower | 檢查字符是否是小寫字母 |
isupper iswupper | 檢查字符是否是大寫字母 |
isdigit iswdigit | 檢查字符是否是數字 |
isxdigit iswxdigit | 檢查字符是否是十六進制數字 |
iscntrl iswcntrl | 檢查字符是否是控制字符 |
isgraph iswgraph | 檢查字符是否是可見字符 |
isspace iswspace | 檢查字符是否是空白符 |
isprint iswprint | 檢查字符是否是可打印字符 |
ispunct iswpunct | 檢查字符是否是標點符號 |
isblank iswblank | 檢查字符是否是空格或Tab |
其中 isblank 是C++11及以后支持的函數.
這些函數對於各種字符的返回值結果如下表:
字符操作函數
用於對字符進行一些轉換(OI里主要就是轉大小寫啥的OwO不過C++也提供了比較強大的本地化庫, 可以進行一些如平假名片假名轉換之類的操作)
toupper 和 towupper
將字符轉化為大寫. 前者用於 char , 后者用於 wchar_t
tolower 和 towlower
將字符轉化為小寫. 前者用於 char , 后者用於 wchar_t
<cstring>與空終止字符串
<cstring> 庫原來是C庫中的 <string.h> , 提供了操作現在被叫做空終止字符串的字符數組的各種函數.
空終止字符串由於本質上是數組, 然而有句話說得好: C++中數組不是一等公民. 對數組很多操作都不能用非常簡潔的語句實現, 而是需要大坨大坨的循環.
所以為了在一定程度上彌補這些缺陷, C++中提供了一些空終止字符串的操作函數來讓C++使用者的語句盡量簡潔. 其中主要包括字符串操作函數, 字符串檢查函數與字符數組操作函數.
字符串操作函數
用於對字符串中的數據進行一定批量修改的函數. OI里用得到的主要是復制和連接兩種操作.
strcpy
將一個字符串復制到另一個字符串.
第一個參數為復制目標, 第二個參數為復制源. 二者類型都為 char* (復制源參數還加了 const 限定符). 自動復制到空字符為止. 如果沒有空字符或者字符串重疊的話產生UB.
strncpy
將一個字符串中的前若干字符復制到另一個字符串.
第一個參數為復制目標, 第二個參數為復制源. 參數類型同上. 第三個參數指定要復制的字符個數.
如果要復制的字符個數大於源串的長度(復制計數未達到指定值就讀取到空字符), 則復制出的字符串並不是空終止字符串, 並且會在復制目標的空字符后補上一系列空字符直到計數達到指定值.
如果復制目標的下標越界了就UB了OwO
strcat
連接兩個字符串.
將第二個參數指向的字符串復制到第一個參數指向的字符串后面.
復制過程中第一個串空間不足產生下標越界則造成UB.
strncat
strcat 的可指定復制字符數的版本. 作用與其基本一致.
前兩個參數與 strcat 相同, 第三個參數指定要復制的字符數量. 一樣是提前終止補空字, 下標越界產UB.
字符串檢查函數
用於掃描一個或多個空終止字符串來獲取一定信息的函數, 包括長度計數/模式匹配/字典序比較等操作. (在basic_string里都是封裝好的w)
strlen
掃描一遍字符串並返回字符串的長度. 遇到的第一個空字符則判定為字符串結束.
如果這個字符串里沒有空字符的話....恭喜你又UB了OwO
strcmp
按照字典序比較兩個字符串. 接受兩個參數, 分別指向要比較的兩個字符串.
第一個串字典序小於第二個串返回一個負整數, 兩個串相等返回 $0$ , 第一個串字典序大於第二個串則返回一個正整數.
如果兩個參數不是指向空終止字符串的指針的話還是UB(突然感覺C庫UB好多啊OwO)
strncmp
strcmp 的計數版本OwO, 多出來的第三個參數指定最多匹配多少個字符(也就是說如果一個字符串提前終止的話比較結束).
兩個參數不是指向空終止字符串的話UB.
strchr
查找在一個字符串中某個字符第一次出現的位置.
第一個參數指向要進行查找的字符串, 第二個參數為要查找的字符.
返回指向給定字符第一次出現位置的指針. 如果未找到的話返回空指針.
strrchr
strchr 的逆序版本, 查找最后一次出現的位置. 參數與返回值與其相同.(日常懶癌發作)
strpbrk
查找字符串中的任意一個字符在另一個字符串中第一次出現的位置. 返回指向找到的位置的指針. 未找到返回空指針.
第一個參數指向要查找的字符串, 第二個參數指向包含要查找的字符的字符串.
strstr
在一個字符串中查找另一個模式串. 或者說檢查一個字符串是否是另一個字符串的子串. 返回指向找到的子串的第一個字符的指針.
第一個參數指向要掃描的串, 第二個參數為要匹配的子串.
未找到則返回NULL.
字符數組操作函數
用於對整個字符數組進行各種設置, OI里拿來初始化普通數組也很方便OwO
memchr
在字符數組里查找指定字符第一次出現的位置.
memcmp
按字典序比較兩個數組的內容.
前兩個參數分別指向要比較的數組, 第三個參數指定要比較的字節數量.
第一個數組字典序較小則返回負整數, 相等返回 $0$ , 第一個字典序較大則返回正整數.
memset
將一個數組的每一個字節字節設置為一個指定的整數.
第一個參數指向要重設的數組, 第二個參數指定要重設的值($-128$ 到 $127$, 或者 $0x80$ 到 $0x7F$), 第三個參數指定要重置的字節數.
一般重設值設為為 $0x80$ 為重置為無窮小, $0x7F$ 為重置為無窮大, $0x3F$ 也可以重置為無窮大但是保證兩個無窮大加起來不會炸邊界. 相應的加和防炸的無窮小初始化值為 $0x40$.
memcpy
將一個數組的內容整個復制到另一個數組的位置.
第一個參數指定復制目標, 第二個參數指定復制源, 第三個參數指定要復制的字節數量.
結語
C++的字符串庫給我們提供了進行各種字符串操作的簡便方法. 雖然查找算法多數為朴素算法, 在實際OI應用中的字符串處理題目中可能被出題人的數據卡掉, 但是平常造數據或者在一些主要算法並非字符串算法等對字符串操作復雜度要求不高的題目/情況下應用很方便. 而且字符串庫還順便給我們提供了操作普通數組的庫函數(實際上后面操作字符數組的函數都可以用於普通數組, 但是如果是比較字典序的話要注意數據存儲的大小端). 所以善用C++標准庫是也是OIer的一個重要技能.
表示后續可能還會更新輸入輸出部分的奇技淫巧或者其他標准庫中的實用工具OwO
(厚臉皮求推薦qwq)
(技術博就不放圖包了qwq有需要請去本人其他博文自取(霧))