查找、替换和提取字符串片断
处理字符串时,或许最常见的操作就是查找、替换以及提取片断。在本节中,我们将学习帮助解决这些问题的 MQL5 API 函数。这些函数的使用示例在 StringFindReplace.mq5 文件中进行了汇总。
int StringFind(string value, string wanted, int start = 0)
该函数在value 字符串中搜索 wanted 子串,从 start 位置开始搜索。如果找到该子串,则该函数将返回该子串的开始位置,并将该字符串中的字符从 0 开始编号。如果找不到,则该函数将返回 -1。两个参数均通过值传递,不仅允许处理变量,而且允许处理计算的中间结果(表达式、函数调用)。
搜索基于严格字符匹配执行,即区分大小写。如果要以区分大小写的方式搜索,必须首先使用 StringToLower或StringToUpper将源字符串转换为单大写或单小写。
我们尝试使用 StringFind 来统计文本中找到的目标子串的数量。为此,我们编写 CountSubstring 辅助函数,其将循环调用 StringFind 逐渐偏移最后参数 start 中的搜索起始位置。只要找到新的子串,循环就继续。
int CountSubstring(const string value, const string wanted)
|
务必要注意,所提供的实现是查找能够重叠的子串。这是因为在开始查找下一个匹配结果之前会将当前位置加 1 (++cursor)。因此,当在字符串 "AAAAA" 中查找子串 "AAA" 时,将找到 3 个匹配结果。但实践中的搜索技术要求可能与此行为存在差异。特别是有一种实践,是在之前找到的片断结束的位置之后继续搜索。这种情况下就需要修改算法,使得游标以等于 StringLen(wanted) 的步长移动。
我们为 OnStart 函数中的不同自变量调用 CountSubstring。
void OnStart()
|
int StringReplace(string &variable, const string wanted, const string replacement)
该函数在 variable 字符串中,用 replacement 子串替换找到的所有 wanted 子串。
该函数返回完成的替换数,如果出错,则返回 -1。错误代码可通过调用函数 GetLastError来获得。这些错误尤其可能是内存不足错误,或者是使用了未初始化的字符串 (NULL) 作为自变量。variables 和 wanted 参数必须是非零长度的字符串。
若为 replacement 自变量提供了空字符串 "",所有找到的 wanted 结果直接从原始字符串剪切。
如果没有进行替换,则函数结果为 0。
我们以 StringFindReplace.mq5 为例来观察 StringReplace 的实际效果。
string abracadabra = "ABRACADABRA";
|
接下来,使用 StringReplace 函数,我们尝试执行在处理任意文本的过程中遇到的其中一项任务。我们将确保某些分隔符始终用作单字符,即若干个连续分隔符须被替换为一个字符。通常这是指单词之间的空格,但在技术数据中可能有其它分隔符。我们在程序中测试分隔符 '-'。
我们用单独 NormalizeSeparatorsByReplace 函数实现该算法:
int NormalizeSeparatorsByReplace(string &value, const ushort separator = ' ')
|
程序尝试在一个 do-while 循环中用单分隔符序列替换双分隔符序列,并且只要 StringReplace 函数返回大于 0 的值(即仍然有要替换的内容),则循环继续。函数返回进行替换的总次数。
在 OnStart 函数中,我们从文本中清除多个 '-' 字符。
...
|
int StringSplit(const string value, const ushort separator, string &result[])
该函数基于给定的分隔符将传递的 value 字符串分离为子串,并将它们放入 result 数组。该函数返回接收的子串数,如果出错,则返回 -1。
如果字符串中没有分隔符,则数组将具有一个等于整个字符串的元素。
如果源字符串为空或 NULL,则函数将返回 0。
为演示该函数的运算,我们使用 StringSplit 以新的方式解决前面的问题。为此,我们编写 NormalizeSeparatorsBySplit 函数。
int NormalizeSeparatorsBySplit(string &value, const ushort separator = ' ')
|
当源文本中先后出现分隔符,输出数组 StringSplit 中的对应元素最终为空字符串 ""。此外,如果文本以分隔符开始,则空字符串将位于数组的开头,而如果文本以分隔符结束,则空字符串位于数组末尾。
为获得“清除过的”文本,需要从数组添加所有非空字符串,以单分隔符“粘合”它们。此外,只有当数组中的空元素前一个元素也是非空时,才应将该空元素转换为分隔符
当然,这只是实现此功能的可能选项之一。我们在 OnStart 函数中检验。
...
|
string StringSubstr(string value, int start, int length = -1)
该函数从传递的文本 value 中提取一个从指定位置 start 开始且长度为 length 的子串。开始位置可以从 0 到字符串长度减 1。如果 length 长度为 -1 或者大于从 start 到字符串结束的字符数,则字符串剩余部分将被完整提取。
该函数返回一个子串,如果参数不正确,则返回空字符串。
我们看看其工作方式。
PRT(StringSubstr("ABRACADABRA", 4, 3)); // 'CAD'
|