字符串比较
要在 MQL5 中比较字符串,可使用标准 比较运算符,尤其是 '=='、'!='、'>'、'<'。所有这些运算符以逐字符、区分大小写的方式执行比较。
每个字符都有一个 ushort 类型整数的 Unicode 码。相应地,首先,比较两个字符串的第一个字符的代码,然后比较第二个字符的代码,以此类推,直至出现第一个不匹配项或达到其中一个字符串的末尾。
例如,"ABC" 字符串小于 "abc",因为在字符表中大写字母的代码低于相应小写字母的代码(对于第一个字符,我们已经得到 "A" < "a")。如果字符串在开头具有匹配字符,但其中一个长于另一个,则较长的字符串被视为更大 ("ABCD" > "ABC")。
这些字符串关系形成词典序。当字符串 "A" 小于字符串 "B" ("A" < "B"),"A" 被视为在 "B" 之前。
要熟悉字符代码,可使用标准 Windows 应用程序“字符映射表”。在该字符映射表中,字符按增加代码的顺序排列。除了包括多种国家语言的常规 Unicode 表,还有代码页:含单字节字符代码的 ANSI 标准表 - 对于每种语言或语言组都各不相同。我们将在以下章节详细探讨该问题: 处理符号和代码页。
字符映射表的初始部分(代码 0 到 127)对于所有语言均相同。该部分显示在下表中。
ASCII 字符代码表
要获取字符代码,取左边的十六进制数字(该字符所在行的行号),然后与顶部的数字(该字符所在列的列号)相加:结果是一个十二进制数字。例如,对于 '!',左边为 2,顶上为 1,意味着字符代码为 0x21 或 33(十进制)。
至 32 的代码为控制代码。其中,尤其你可以找到制表符(代码 0x9)、换行(代码 0xA)以及回车(代码 0xD)。
Windows 文本文件中使用彼此跟随的一对字符 0xD 0xA,用于中断至新行。我们在 字符类型 章节熟悉了相应的 MQL5 字面量:0xA 可表示为 '\n',而 0xD 表示为 '\r'。制表符 0x9 也有自己的表示方法:'\t'。
MQL5 API 提供了 StringCompare 函数,可用于在比较字符串时禁用区分大小写。
int StringCompare(const string &string1, const string &string2, const bool case_sensitive = true)
该函数比较两个字符串并返回三个值之一:如果第一个字符串“大于”第二个,则返回 +1;如果字符串“相等”,则返回 0;如果第一个字符串“小于”第二个,则返回 -1。“大于”、“小于”和“等于”的概念取决于 case_sensitive 参数。
若 case_sensitive 参数等于 true(默认),比较区分大小写,大写字母视为大于对应小写字母。这与根据字符代码的标准词典序相反。
若区分大小写,StringCompare 函数使用不同于词典序的大小写字母序。例如,我们知道关系 "A" < "a" 为 true,其中运算符 '<' 以字符代码为依据。因此,在假设的字典(数组)中,首字母大写的单词应出现在具有相同小写字母的单词之前。但是,当使用 StringCompare("A", "a") 函数比较 "A" 和 "a" 时,我们得到 +1,表示 "A" 大于 "a"。因此,在排序词典中,以小写字母开始的单词将在先,而以大写字母开始的单词在后。
换言之,该函数按字母顺序对字符串排序。此外,在区分大小写模式下,还有一条额外规则:如果字符串仅存在大小写的差别,包含大写字母的字符串会排在对应小写字母字符串的后面(在单词中相同位置的情况下)。
如果 case_sensitive 参数等于 false,则字母不区分大小写,因此字符串 "A" 和 "a" 视为相等,函数返回 0。
可以使用 StringCompare.mq5 脚本检查由 StringCompare 函数和运算符比较的不同比较结果。
void OnStart()
|
在 函数模板 章节中,我们创建了模板化快速排序算法。我们将其转换为模板类,并将其用于若干排序选择:在区分和不区分大小写的情况下使用比较运算符以及使用 StringCompare 函数。我们将新的 QuickSortT 类放入 QuickSortT.mqh 头文件,并将其连接到 StringCompare.mq5 测试脚本。
排序 API 几乎没有变化。
template<typename T>
|
主要的不同之处在于我们添加了 Compare 虚方法,其默认包含使用 '>' 和 '<' 运算符的比较,并以与 StringCompare 相同的方式返回 +1、-1 或 0。Compare 方法现在用于 QuickSort 方法中,代替简单比较,必须在子类中被重写,以使用 StringCompare 函数或任何其它比较方法。
尤其在 StringCompare.mq5 文件中,我们实现了派生自 QuickSortT<string> 的以下“比较器”类:
class SortingStringCompare : public QuickSortT<string>
|
构造函数采用 1 个参数,用于指定字符串比较符号,同时考虑 (true) 或忽略 (false) 寄存器。字符串比较本身在重新定义的 Compare 虚方法中完成,该方法调用具有给定自变量和设置的 StringCompare 函数。
为测试排序,我们需要一系列组合了大小写字母的字符串。我们可以自行生成这些字符串:只需开发一个类,对预定义集合(字母表)中的字符按给定集合长度(字符串)执行排列组合(可重复)。例如,可以限定使用小写字母表 "abcABC",即大小写的前三个英语字母,然后据此生成所有可能的 2 个字符的字符串。
PermutationGenerator 类在 PermutationGenerator.mqh 文件中提供,稍后再单独探讨。在这里我们仅了解其公共接口。
class PermutationGenerator
|
创建生成器对象时,必须指定生成的集合的长度 length(在本例中,这将是字符串的长度,比如 2)以及将组成集合的不同元素的数量(在本例中,这是唯一字母的数量,也就是 6)。利用这些输入数据,应可获得 6 * 6 = 36 的行变体。
该过程本身由 run 方法执行。模板类用于返回结果为 SimpleArray 的数组,这已在 方法模板 章节讨论过。在此情况下,其由 result 结构体类型进行参数化。
GenerateStringList 辅助函数负责调用生成器,并根据从生成器收到的排列数组(以所有可能字符串各位置的字母索引形式表示)实际创建字符串。
void GenerateStringList(const string symbols, const int len, string &result[])
|
我们使用几个还不熟悉的函数 (ArrayResize, ShortToString),但我们将很快就会了解。目前,我们只需知道,ShortToString 函数返回由该单个字符基于 ushort 类型字符代码组成的字符串。通过 += 运算符,我们将这些单字符字符串拼接成最终的字符串。别忘了,为字符串定义了运算符 [],因此,symbols[k] 将返回 symbols 字符串的第 k 个字符。当然,k 也可反过来成为整数表达式,并且这里的 r[i].indices[j] 指的是 r 数组的第 i 个元素,从该数组读取了字符串的第 j 位置对应的“字母”字符索引。
接收到的每个字符串存储在 result 数组参数中。
我们在 OnStart 函数中应用这一信息。
void OnStart()
|
脚本首先将所有字符串选择纳入消息数组,然后以 3 种模式排序:使用内置比较运算符、使用 StringCompare 函数且不区分大小写、使用该函数且区分大小写。
我们将得到以下日志输出:
Original data[36]:
|
输出显示这三种模式的差异。