加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 第五章 字符串處理
    • 5.1 Str類
    • 5.2 包級(jí)字符串函數(shù)
    • 5.3 指定字符串范圍
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

svlib文檔翻譯

2021/04/23
271
閱讀需 19 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

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

第五章 字符串處理

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

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

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

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

5.1 Str

5.1.1 處理Str對(duì)象和成員的方法

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

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

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

5.1.2 枚舉類型

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

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

5.1.3 在Str對(duì)象的字符串后面拼接一個(gè)字符串

function void append(string s); 

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

 

5.1.4 查找子字符串

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

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

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

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

「注意」:Str類的firstlast方法提供了一個(gè)簡(jiǎn)單快速的子字符串搜索方法。在第六章中,使用正則表達(dá)式匹配可以更靈活地進(jìn)行搜索匹配,但這種靈活性的代價(jià)是參數(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)用它。如果你需要一個(gè)類型名來(lái)表示字符串隊(duì)列,你應(yīng)該自己定義類型名,能完全兼容(類型等效)qs。另外,也可以簡(jiǎn)單地聲明字符串隊(duì)列的變量,并使用它們作為參數(shù)和結(jié)果變量。

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

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

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

「注意」:從svlib的0.5版開(kāi)始,Regex類中有一個(gè)新的split方法(見(jiàn)第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)其中一個(gè)邊界超出字符串時(shí),它的表現(xiàn)會(huì)更加正常。在第5.3節(jié)中,詳細(xì)地介紹了如何使用p、norigin參數(shù)指定字符串的一個(gè)切片的詳細(xì)信息。range只返回指定的子字符串,返回類型為SystemVerilog的字符串類型。

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

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

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

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

傳入的rs字符串的長(zhǎng)度沒(méi)有限制,不需要和被替換的字符串長(zhǎng)度一致。

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

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

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

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

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

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

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

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

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

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

function void quote (); 

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

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

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

5.3 指定字符串范圍

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

5.3.1 起點(diǎn)的定義

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

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

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

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

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

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

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è)開(kāi)始的四個(gè)字符位置。然而,其中兩個(gè)字符位置不在原來(lái)的5個(gè)字符的字符串中,因此范圍操作的結(jié)果是兩個(gè)字符的字符串“lo”。

5.3.4 一些例子

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

相關(guān)推薦

電子產(chǎn)業(yè)圖譜