加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 第五章 字符串处理
    • 5.1 Str类
    • 5.2 包级字符串函数
    • 5.3 指定字符串范围
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

svlib文档翻译

2021/04/23
265
阅读需 19 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

中文版本由空白的贝塔君整理发布

第五章 字符串处理

SystemVerilog语言本身提供了许多字符串操作。然而,经验表明,内置方法不足以满足工作中的字符串处理任务,svlib提供了进一步的操作集来帮助满足这些需求。

在大多数情况下,字符串操作有两种不同的形式,用户可以自由选择更适合自己需要的形式。

  • 第一种形式是关于字符串变量的简单函数,通常(但不总是)返回字符串结果。这些函数在svlib包中定义,名称都以str_开头。第二种形式是Str类对象的方法(注意大写的S)。Str类是SystemVerilog字符串的wrapper,通过引用传递字符串,并使一些操作更方便。

对比使用简单函数,使用Str对象必须在所有操作之前构造对象。不过通过Str对象的许多操作的效率和便利性通常收益是利大于弊的。程序员可以自由选择对他们来说最方便的方法。如果只需要对一个字符串执行一个操作,那么pkg级函数可能是最方便的。如果要对同一个字符串执行许多连续操作,最好创建一个Str对象来进行处理。

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函数创建对象,必须使用Str::create方法。当然,创建对象是可以无视参s

对象创建以后,随时可以使用set方法更新字符串成员。而get方法则返回对象保存的字符串。len方法则返回字符串长度。copy函数则返回一个新的对象,并且它的内容与调用的对象一致。

5.1.2 枚举类型

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

这两个枚举用于指定某些方法的各种可选行为。ide_enum用于指定字符串的哪一侧将参与各种操作,特别是trimpadorigin_enum用于指定在rangereplace操作时从字符串的哪端计数。START指定字符串最左端,END指定最右端。这些选项的细节将在后面的小节中展开。

5.1.3 在Str对象的字符串后面拼接一个字符串

function void append(string s); 

这个函数通过使用简单的字符串连接,将指定的字符串拼接到一个Str对象的字符串成员后面,从而修改该对象的现有字符串内成员。

 

5.1.4 查找子字符串

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

first()在对象的字符串内容中搜索字符串子str的第一次出现的位置。它返回子字符串的最左边字符在原始字符串中的位置。如果搜索失败(在原始字符串中没有出现子字符串),则函数返回-1。这个方法的搜索是精确的文字匹配,不使用通配符或正则表达式匹配。

参数ignore指定搜索从哪里开始。默认值(ignore=0)将扫描整个字符串,并返回第一个匹配项。如果ignore大于零,搜索将从指定的字符位置开始。不管ignore的值是多少,成功匹配后的返回值都是匹配在原始字符串中的绝对起始位置。

last的行为方式类似,但它从字符串的最右端开始扫描,因此,如果查找的子字符串在原始字符串中出现多次,它将返回最后一个可能的匹配结果。最后,ignore参数指定在字符串最右端的要忽略的字符数——它的作用等效于这部分字符不存在。

「注意」:Str类的firstlast方法提供了一个简单快速的子字符串搜索方法。在第六章中,使用正则表达式匹配可以更灵活地进行搜索匹配,但这种灵活性的代价是参数配置增加和速度下降。在大多数情况下,是利大于弊的,正则表达式是首选。

 

5.1.5 切割和连接操作

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

「注意」:svlib内部定义了类型名qs,表示“queue of strings”,但用户代码不能调用它。如果你需要一个类型名来表示字符串队列,你应该自己定义类型名,能完全兼容(类型等效)qs。另外,也可以简单地声明字符串队列的变量,并使用它们作为参数和结果变量。

sjoin方法(不使用join作为名称,是因为和SystemVerilog关键字冲突)使用Str对象的内容作为“joiner”,将字符串队列中的元素组装成单个字符串。例如,它可以方便地创建逗号分隔的列表。

split方法获取Str对象的现有字符串(保持不变),并使用单个字符分割标记("splitter")将其分割成字符串队列。参数splitset是一个字符串,但它被视为一组单独的字符;对象的字符串变量被分割,分割的位置是出现splitset中字符的位置。如果splitset是一个空字符串,那么对象的字符串会被分割后的字符串队列的每个元素都将是单个字符。

如果keepsplittertrue(1)且splitset不是空字符串,则拆分字符将作为结果队列的单个成员出现在其对应的位置。如果keepsplitterfalse(默认值),拆分字符将不会出现在结果中。

「注意」:从svlib的0.5版开始,Regex类中有一个新的split方法(见第6章)。它提供了比这里的Str::split方法灵活得多的功能,在大多数情况下是首选方法。

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操作的更通用和统一的方法。当其中一个边界超出字符串时,它的表现会更加正常。在第5.3节中,详细地介绍了如何使用pnorigin参数指定字符串的一个切片的详细信息。range只返回指定的子字符串,返回类型为SystemVerilog的字符串类型。

replace以完全相同的方式指定子字符串,然后用rs替换该子字符串,并修改Str对象的内容。replace非常灵活,有时可以单独使用。例如:

  • 通过传入空的rs参数,删除指定子字符串通过下面的方式可以实现在尾部添加一个字符串

s.replace(append_string, 0, 0, Str::END); 
  • 通过下面的方式可以实现在开头添加一个字符串

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

传入的rs字符串的长度没有限制,不需要和被替换的字符串长度一致。

5.1.7 在字符串的开头和结尾删除或添加空白字符

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

trim删除字符串的开头或者结尾的所有空白字符,它会修改Str对象的现有内容。参数side指定要修剪字符串的哪一端。如果side是Str::LEFT,则从字符串的左端删除空白;RIGHT删除尾随空格;BOTH删除两端的空格。最后,如果指定了NONE,就不会产生任何效果。

空白字符包括任何空格、制表符、换行符、回车符和不间断空格(ASCII码160)。

如果字符串完全由空格组成,并且side参数不是NONE,则结果将是一个空字符串。

pad会在开头或者结尾添加空白字符(使用空格字符),使结果字符串的长度正好是width。如果字符串已经大于width,则不进行任何操作。如果sideNONE,则字符串不变。否则,将根据需要在指定的字符串末尾添加空格。如果sideBOTH,则在两边添加相同数量的空格(必要时在右侧添加一个额外的空格)。此方法对于以表格格式打印的文本对齐非常有用。

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

function void strip (string chars = " \t\n\13\14\15\240\177"); 

strip删除Str对象中以字符形式出现的所有字符。默认情况下是删除所有空白字符,但您可以指定一个包含您想要删除的任何字符的字符串。

5.1.9 将字符串转换为systemverilog的标准字符串

function void quote (); 

此方法会更新对象,对字符串的进行转义处理。使用转义字符,如"和\n,将特殊字符(反斜杠,双引号,控制字符等)替换为等价字符。在需要的地方使用更通用的\xNN表示法。最后,整个字符串由一对字符串引号(")包围。结果总是一个完整的、合法的SystemVerilog字符串。

这个函数是用来编写SystemVerilog的,用于生成SystemVerilog源代码。在以逗号分隔值(CSV)等格式写入文件时,也很有用。

5.2 包级字符串函数

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 = " \t\n\13\14\15\240\177"); 

如果只想进行简单的操作,创建一个对象其实是很不方便的。因此,svlib提供了一些字符串操作作为包级函数,作为类方法的替代。这些函数执行的操作与Str类的相应方法完全相同。在方法内部,都用参数string s填充Str对象,然后再执行操作,并最终返回对应的结果。这些方法性能开销很小,因为库维护了一个Str对象池,专门用于此类操作。

5.3 指定字符串范围

svlib使用单一且一致的方式指定子字符串范围(字符串的切片)。它显式地在Str的方法range和replace(以及相应的包级函数str_range和str_replace)中使用,也在其他地方隐式地使用。它的设计是为了降低SystemVerilog的自带的字符串类型的substr操作的复杂性。

5.3.1 起点的定义

不根据字符数指定字符串范围,因为这会导致在处理零长度字符串切片时出现奇怪的不连续。字符串切片的边界是根据字符之间的位置指定的。为了说明这一点,考虑5个字符的字符串“Hello”:

使用这种方法,我们有一种一致的方式来指定子字符串的边界位置,使用参数p,将origin参数指定为Str::START(默认值)。通过这种方式可以直观地理解,负的,或者大于字符串的长度,所代表的位置。

也可以根据字符串的Str::END(最右边的位置)指定边界。在下例中,修改了对不同p参数值的定义,p从右(结束)字符边界向左计算:

我们直接定义了p的超出范围值时的意义。因此,如果将origin指定为Str::END,我们就可以指定字符串的末尾部分,而不必关心字符串的确切长度。

5.3.2 长度参数n的定义

在为字符串范围建立了起点之后,现在需要考虑希望获取的切片长度。这个参数n的解释不受原始值的任何影响。它指定从p指定的边界移动多远,以找到我们的子字符串的第二个边界。n为正表示向右移动。负值表示向左移动。

5.3.3 最终范围的定义

originnp三者指定了字符范围。例如,如果我们要调用函数str_range(.s("Hello"), .p(3), .n(4), .origin(Str::START)),它将指定下面图表中阴影代表的范围:

p=3, origin=START指定了起始位置3;n=4指定了从p指定的位置右侧开始的四个字符位置。然而,其中两个字符位置不在原来的5个字符的字符串中,因此范围操作的结果是两个字符的字符串“lo”。

5.3.4 一些例子

下面是各种情况的一些例子。

相关推荐

电子产业图谱