c#截取字符串后几位_基础库的字符串设计
C++在字符串上表現,一直以來很受人詬病,沒有一個庫的字符串類的表現能讓人滿意, std的string,mfc的CString,Qt的QString等等字符串類,都存在這樣那樣的問題,以至于字符串處理竟然成為C++的弱項,這也太不應該了。
話說回來,C++對待字符串的態度也很奇葩,其他語言都將字符串當做是內建類,在編譯器層面來實現,只有C++是通過庫的形式來提供字符串的抽象,也就是說,C++的語言層面上并沒有字符串類,而是提供制造字符串輪子的語言機制。這樣有兩個好處,一方面既然這些語言機制能實現字符串,說不定還能干點別的什么事情,授人以魚不如授人以漁;另一方面,更重要的是,字符串內部的緩沖區涉及到動態內存分配,一旦涉及到內存資源管理,在C++下從來就不是簡單的事情,最明顯的一點,就是稍微處理方式不恰當,就勢必影響應用層對字符串全方位的粒度控制,這就違背了C++的語言哲學了。
雖說字符串類的設計不容易,C++面世到現在,未曾出現過差強人意的相關庫即是明證。但也不至于困難到語言都面世幾十年了,還在字符串處理上灰頭土臉的地步。其實,只要牢牢把握以下幾點,還是有希望做出來好用的字符串庫的。
1、采用utf8編碼,這個就不解釋太多了,字符串不可能搞成支持多種字符編碼的模板類(不必要的麻煩太多),所以自然要選擇一種Unicode編碼,在空間性能、時間性能、跨平臺(大端小端)、api設計等這些因素,utf8的綜合表現最為杰出。要操作其他編碼的字符串時,都要求先臨時轉換為utf8編碼,操作完成后,再將結果轉回為原始編碼的字符串,效率上稍微存在損失,但是真的不打緊;
2、空間效率與時間效率上的最高要求,哪怕由此導致api使用上的丑陋,也在所不惜。只因字符串的應用范圍太廣了,不能讓它成為影響性能的因素。std的string之所以存在那么多的成員函數,還不是為了性能上的需要,其中大部分都只是函數重載,僅僅只是為了避免臨時string的誕生,標準庫string的這種應對方式也太實誠了
3、使用上的方便性,不必遵從什么最小完整接口的戒條,只管往里面添加各種各樣的成員函數,怎么方便就怎么來,當然也不能無節制,QString就太過分了,啥都往里面塞。
有鑒于此,我們將string設計為兩個類,U8View和U8String,分別代表只讀字符串和字符緩沖區生命周期管理類,這類似于Java的String和StringBuilder,其實這也對應著C++17的string_view和string,這個方案既簡單直觀又靈活高效,奇怪的是,C++社區竟然要到17才會有這樣的認識,并且,就算這樣,std的字符串表現也還是垃圾。
U8View代表著一段連續字符的只讀內存塊,單個的char也可以看成是長度為1的U8View,不要求字符緩沖以0結束,這一點很關鍵,標準庫的string就是在此放不開手腳,才導致那么蹩腳的設計。U8View很輕量級,它只有兩個字段,分別代表字符緩沖的起始地址以及長度,常量字符串,U8String,字符數組這些內部有著連續字符緩沖的對象,全部都可以看成是U8View。U8View不得修改其緩沖區的任何內容。
顯然,U8View有一個很好的特性,那就是U8View的任何一部分也都是U8View,因此,U8View提取子字符串的操作必然很輕量級,簡直不存在任何性能上的損失,一步到位就做好了,無須搞任何內存分配的事情。所以,U8View自然就不必理會其緩沖區的生命周期,緩沖區的有效性由應用層代碼來保證,U8View在其使用期間,都假設了字符緩沖的有效性。
bool IsValidUtf8Text(const char* str, size_t len);struct U8View {typedef const char* const_pointer;const_pointer mStr;size_t mLength;U8View(const_pointer str, size_t len) :mStr(str), mLength(len){assert(IsValidUtf8Text(str, len));}U8View(const_pointer start, const_pointer last);U8View(const_pointer str);template<size_t _Size> U8View(const char(&str)[_Size]);bool empty()const { return mLength == 0; }char operator[](size_t index)const;//... };U8View里面有大量的成員函數,大致可分為這6類,所有的成員函數幾乎為const屬性。
1、 構造函數,通過各種常量字符指針來初始化U8View,各個函數體內部要assert字符串utf8的有效性。
2、 查找函數,Find,RFind(查找字符,查找子字符串,正向查找,反向查找),FindOneOf查找字符串參數里面的任何一個unicode字符;
3、 比較函數,CompareTo大小寫方式比較,CompareTo忽略大小寫比較,StartsWith, EndsWith,各種比較操作符的重載函數,empty可空判斷
4、 截取子字符串,不管時間空間,都是O(1),零懲罰。這里又分為3類,a)直接通過字節數來截取,Sub,Left,Right,Trim,TrimLeft,TrimRight,關鍵的一點是返回時候,都要assert其utf8字符串的完整性;b)以Unicode的codepoint數目來截取子字符串,SubPoints,LeftPoints等;c)以Unicde的glyph數目來截取。
5、 迭代器,正常的begin、end函數, Points返回一對訪問codepoint的迭代器,Glyphs返回迭代glyph的迭代器,正向迭代,反向迭代;返回各個位置上(比如字符串頭尾)的char值或者codepoint值
6、 生成U8String:Replace替換已有子字符串以生成新的U8String,U8View的加號操作符重載,Concat多個U8View等。
至于U8View轉換為各類數字類型的轉換函數,就不在這里出現了,另有更好的安排。有了U8View之后,基本上所有同步函數的字符串參數,都可以為U8View的類型。其實,很多字符串運算的場合下,只要U8View就完全足夠,效率(時間空間)以及易用性,均能達到最優。U8View其實為值類型,C++在抽象值類型時,不管是性能還是api使用形式,每一項的表現都很杰出,其他任何猿語都很難再超過大C++了。
由于U8View已經剝離出來很多常見的字符串操作,U8String的設計就很簡單了,只需老老實實本本分分管理字符緩沖的生命周期就好了,也就是實現析構函數,復制移動構造函數,賦值操作符重載,這沒什么好說的,只是要注意到U8String內部的字符緩沖,必須以0結束。U8String的成員函數無非就是替換,追加,修改,交換等。其中很重要的一個成員函數View,用以返回U8View對象,之后就能以只讀形式操作U8String的字符緩沖。這里要強調的一點就是,除了移動構造和移動賦值函數,所有其他成員函數的字符串參數類型都是U8View類型,因此我們就無須為了效率,要對每一個操作都重載那么多成員函數,何其簡潔。
不知道大家發現沒有,U8View和U8String這兩個類型互相依賴,你中有我,我中有你,相依為命。這種相互糾纏的設計方案,一般情況下避之唯恐不及,但是在字符串這里必須這樣搞,用起來才方便。但是,編譯器可不喜歡這種設計,這可怎么辦?只好讓U8View成為U8String的內部類,并將其命名為TextView。然后再在外面將U8String的TextView typedef為U8View,代碼如下。
struct U8String {struct TextView {char* mStr; size_t mLength;//...};MemoryAllocator* mAlloc;size_t mCapacity;char* mStr; //故意保持跟TextView一樣的內存布局size_t mLength;//... }; typedef U8String::TextView U8View;字符串的故事就這樣結束了嗎?不,這才剛剛開始,U8String U8View必須配套格式化,字符串解析,字符串流的輸入輸出,正則表達式(運行時以及編譯時)等功能之后,才真正好用起來,然后就可以發現,C++其實還是很擅長于處理字符串的。但是,在此之前,我們必須先解決幾個基礎問題,那就是反射、Trait(非侵入式的接口)、allocator、甚至stl各個容器的重新設計。
總結
以上是生活随笔為你收集整理的c#截取字符串后几位_基础库的字符串设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python怎么启动服务器_如何通过cg
- 下一篇: axure文件如何加密_rp文件命名也可