• 正文
    • 第五章 字符串處理
    • 5.1 Str類
    • 5.2 包級字符串函數(shù)
    • 5.3 指定字符串范圍
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

svlib文檔翻譯

2021/04/23
216
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

中文版本由空白的貝塔君整理發(fā)布

第五章 字符串處理

SystemVerilog語言本身提供了許多字符串操作。然而,經(jīng)驗表明,內(nèi)置方法不足以滿足工作中的字符串處理任務(wù),svlib提供了進(jìn)一步的操作集來幫助滿足這些需求。

在大多數(shù)情況下,字符串操作有兩種不同的形式,用戶可以自由選擇更適合自己需要的形式。

  • 第一種形式是關(guān)于字符串變量的簡單函數(shù),通常(但不總是)返回字符串結(jié)果。這些函數(shù)在svlib包中定義,名稱都以str_開頭。第二種形式是Str類對象的方法(注意大寫的S)。Str類是SystemVerilog字符串的wrapper,通過引用傳遞字符串,并使一些操作更方便。

對比使用簡單函數(shù),使用Str對象必須在所有操作之前構(gòu)造對象。不過通過Str對象的許多操作的效率和便利性通常收益是利大于弊的。程序員可以自由選擇對他們來說最方便的方法。如果只需要對一個字符串執(zhí)行一個操作,那么pkg級函數(shù)可能是最方便的。如果要對同一個字符串執(zhí)行許多連續(xù)操作,最好創(chuàng)建一個Str對象來進(jìn)行處理。

5.1 Str

5.1.1 處理Str對象和成員的方法

static function Str Str::create(string s = ""); 
function void   set   (string s); 
function string get   (); 
function Str    copy  (); 
function int    len   (); 

前文提到過,用戶不能直接通過new函數(shù)創(chuàng)建對象,必須使用Str::create方法。當(dāng)然,創(chuàng)建對象是可以無視參s。

對象創(chuàng)建以后,隨時可以使用set方法更新字符串成員。而get方法則返回對象保存的字符串。len方法則返回字符串長度。copy函數(shù)則返回一個新的對象,并且它的內(nèi)容與調(diào)用的對象一致。

5.1.2 枚舉類型

typedef enum {NONE, LEFT, RIGHT, BOTH} side_enum; 
typedef enum {START, END} origin_enum; 

這兩個枚舉用于指定某些方法的各種可選行為。ide_enum用于指定字符串的哪一側(cè)將參與各種操作,特別是trimpadorigin_enum用于指定在rangereplace操作時從字符串的哪端計數(shù)。START指定字符串最左端,END指定最右端。這些選項的細(xì)節(jié)將在后面的小節(jié)中展開。

5.1.3 在Str對象的字符串后面拼接一個字符串

function void append(string s); 

這個函數(shù)通過使用簡單的字符串連接,將指定的字符串拼接到一個Str對象的字符串成員后面,從而修改該對象的現(xiàn)有字符串內(nèi)成員。

 

5.1.4 查找子字符串

function int first (string substr, int ignore=0); 
function int last (string substr, int ignore=0); 

first()在對象的字符串內(nèi)容中搜索字符串子str的第一次出現(xiàn)的位置。它返回子字符串的最左邊字符在原始字符串中的位置。如果搜索失敗(在原始字符串中沒有出現(xiàn)子字符串),則函數(shù)返回-1。這個方法的搜索是精確的文字匹配,不使用通配符或正則表達(dá)式匹配。

參數(shù)ignore指定搜索從哪里開始。默認(rèn)值(ignore=0)將掃描整個字符串,并返回第一個匹配項。如果ignore大于零,搜索將從指定的字符位置開始。不管ignore的值是多少,成功匹配后的返回值都是匹配在原始字符串中的絕對起始位置。

last的行為方式類似,但它從字符串的最右端開始掃描,因此,如果查找的子字符串在原始字符串中出現(xiàn)多次,它將返回最后一個可能的匹配結(jié)果。最后,ignore參數(shù)指定在字符串最右端的要忽略的字符數(shù)——它的作用等效于這部分字符不存在。

「注意」:Str類的firstlast方法提供了一個簡單快速的子字符串搜索方法。在第六章中,使用正則表達(dá)式匹配可以更靈活地進(jìn)行搜索匹配,但這種靈活性的代價是參數(shù)配置增加和速度下降。在大多數(shù)情況下,是利大于弊的,正則表達(dá)式是首選。

 

5.1.5 切割和連接操作

function string sjoin (qs elements); 
function qs split (string splitset="", bit keepSplitters=0); 

「注意」:svlib內(nèi)部定義了類型名qs,表示“queue of strings”,但用戶代碼不能調(diào)用它。如果你需要一個類型名來表示字符串隊列,你應(yīng)該自己定義類型名,能完全兼容(類型等效)qs。另外,也可以簡單地聲明字符串隊列的變量,并使用它們作為參數(shù)和結(jié)果變量。

sjoin方法(不使用join作為名稱,是因為和SystemVerilog關(guān)鍵字沖突)使用Str對象的內(nèi)容作為“joiner”,將字符串隊列中的元素組裝成單個字符串。例如,它可以方便地創(chuàng)建逗號分隔的列表。

split方法獲取Str對象的現(xiàn)有字符串(保持不變),并使用單個字符分割標(biāo)記("splitter")將其分割成字符串隊列。參數(shù)splitset是一個字符串,但它被視為一組單獨的字符;對象的字符串變量被分割,分割的位置是出現(xiàn)splitset中字符的位置。如果splitset是一個空字符串,那么對象的字符串會被分割后的字符串隊列的每個元素都將是單個字符。

如果keepsplittertrue(1)且splitset不是空字符串,則拆分字符將作為結(jié)果隊列的單個成員出現(xiàn)在其對應(yīng)的位置。如果keepsplitterfalse(默認(rèn)值),拆分字符將不會出現(xiàn)在結(jié)果中。

「注意」:從svlib的0.5版開始,Regex類中有一個新的split方法(見第6章)。它提供了比這里的Str::split方法靈活得多的功能,在大多數(shù)情況下是首選方法。

5.1.6 提取子字符串和替換操作

function string range (int p, int n, origin_enum origin=START); 
function void replace(string rs, int p, int n, origin_enum origin=START); 

range提供了比SystemVerilog原生字符串的substr操作的更通用和統(tǒng)一的方法。當(dāng)其中一個邊界超出字符串時,它的表現(xiàn)會更加正常。在第5.3節(jié)中,詳細(xì)地介紹了如何使用p、norigin參數(shù)指定字符串的一個切片的詳細(xì)信息。range只返回指定的子字符串,返回類型為SystemVerilog的字符串類型。

replace以完全相同的方式指定子字符串,然后用rs替換該子字符串,并修改Str對象的內(nèi)容。replace非常靈活,有時可以單獨使用。例如:

  • 通過傳入空的rs參數(shù),刪除指定子字符串通過下面的方式可以實現(xiàn)在尾部添加一個字符串

s.replace(append_string, 0, 0, Str::END); 
  • 通過下面的方式可以實現(xiàn)在開頭添加一個字符串

s.replace(prefix_string, 0, 0, Str::START); 

傳入的rs字符串的長度沒有限制,不需要和被替換的字符串長度一致。

5.1.7 在字符串的開頭和結(jié)尾刪除或添加空白字符

function void trim (side_enum side=BOTH); 
function void pad (int width, side_enum side=BOTH); 

trim刪除字符串的開頭或者結(jié)尾的所有空白字符,它會修改Str對象的現(xiàn)有內(nèi)容。參數(shù)side指定要修剪字符串的哪一端。如果side是Str::LEFT,則從字符串的左端刪除空白;RIGHT刪除尾隨空格;BOTH刪除兩端的空格。最后,如果指定了NONE,就不會產(chǎn)生任何效果。

空白字符包括任何空格、制表符、換行符、回車符和不間斷空格(ASCII碼160)。

如果字符串完全由空格組成,并且side參數(shù)不是NONE,則結(jié)果將是一個空字符串。

pad會在開頭或者結(jié)尾添加空白字符(使用空格字符),使結(jié)果字符串的長度正好是width。如果字符串已經(jīng)大于width,則不進(jìn)行任何操作。如果sideNONE,則字符串不變。否則,將根據(jù)需要在指定的字符串末尾添加空格。如果sideBOTH,則在兩邊添加相同數(shù)量的空格(必要時在右側(cè)添加一個額外的空格)。此方法對于以表格格式打印的文本對齊非常有用。

5.1.8 刪除字符串中不想要的字符

function void strip (string chars = " tn131415240177"); 

strip刪除Str對象中以字符形式出現(xiàn)的所有字符。默認(rèn)情況下是刪除所有空白字符,但您可以指定一個包含您想要刪除的任何字符的字符串。

5.1.9 將字符串轉(zhuǎn)換為systemverilog的標(biāo)準(zhǔn)字符串

function void quote (); 

此方法會更新對象,對字符串的進(jìn)行轉(zhuǎn)義處理。使用轉(zhuǎn)義字符,如"和n,將特殊字符(反斜杠,雙引號,控制字符等)替換為等價字符。在需要的地方使用更通用的xNN表示法。最后,整個字符串由一對字符串引號(")包圍。結(jié)果總是一個完整的、合法的SystemVerilog字符串。

這個函數(shù)是用來編寫SystemVerilog的,用于生成SystemVerilog源代碼。在以逗號分隔值(CSV)等格式寫入文件時,也很有用。

5.2 包級字符串函數(shù)

function string str_sjoin(qs elements, string joiner); 
function string str_trim(string s, Str::side_enum side=Str::BOTH); 
function string str_pad( 
          string s, int width, Str::side_enum side=Str::BOTH); 
function string str_quote(string s); 
function string str_replace( 
          string s, string rs,  
          int p, int n, Str::origin_enum origin=Str::START); 
function string strip ( 
          string s, string chars = " tn131415240177"); 

如果只想進(jìn)行簡單的操作,創(chuàng)建一個對象其實是很不方便的。因此,svlib提供了一些字符串操作作為包級函數(shù),作為類方法的替代。這些函數(shù)執(zhí)行的操作與Str類的相應(yīng)方法完全相同。在方法內(nèi)部,都用參數(shù)string s填充Str對象,然后再執(zhí)行操作,并最終返回對應(yīng)的結(jié)果。這些方法性能開銷很小,因為庫維護了一個Str對象池,專門用于此類操作。

5.3 指定字符串范圍

svlib使用單一且一致的方式指定子字符串范圍(字符串的切片)。它顯式地在Str的方法range和replace(以及相應(yīng)的包級函數(shù)str_range和str_replace)中使用,也在其他地方隱式地使用。它的設(shè)計是為了降低SystemVerilog的自帶的字符串類型的substr操作的復(fù)雜性。

5.3.1 起點的定義

不根據(jù)字符數(shù)指定字符串范圍,因為這會導(dǎo)致在處理零長度字符串切片時出現(xiàn)奇怪的不連續(xù)。字符串切片的邊界是根據(jù)字符之間的位置指定的。為了說明這一點,考慮5個字符的字符串“Hello”:

使用這種方法,我們有一種一致的方式來指定子字符串的邊界位置,使用參數(shù)p,將origin參數(shù)指定為Str::START(默認(rèn)值)。通過這種方式可以直觀地理解,負(fù)的,或者大于字符串的長度,所代表的位置。

也可以根據(jù)字符串的Str::END(最右邊的位置)指定邊界。在下例中,修改了對不同p參數(shù)值的定義,p從右(結(jié)束)字符邊界向左計算:

我們直接定義了p的超出范圍值時的意義。因此,如果將origin指定為Str::END,我們就可以指定字符串的末尾部分,而不必關(guān)心字符串的確切長度。

5.3.2 長度參數(shù)n的定義

在為字符串范圍建立了起點之后,現(xiàn)在需要考慮希望獲取的切片長度。這個參數(shù)n的解釋不受原始值的任何影響。它指定從p指定的邊界移動多遠(yuǎn),以找到我們的子字符串的第二個邊界。n為正表示向右移動。負(fù)值表示向左移動。

5.3.3 最終范圍的定義

origin、np三者指定了字符范圍。例如,如果我們要調(diào)用函數(shù)str_range(.s("Hello"), .p(3), .n(4), .origin(Str::START)),它將指定下面圖表中陰影代表的范圍:

p=3, origin=START指定了起始位置3;n=4指定了從p指定的位置右側(cè)開始的四個字符位置。然而,其中兩個字符位置不在原來的5個字符的字符串中,因此范圍操作的結(jié)果是兩個字符的字符串“lo”。

5.3.4 一些例子

下面是各種情況的一些例子。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄