MQL5 编程基础:字符串

Dmitry Fedoseev | 4 三月, 2014

简介

字符串,更准确地说是字符串变量,用于存储字符数据,即文本:

string str="Any text";

MQL5 语言为使用字符串提供了丰富的用户友好功能。在 EA 交易和指标的编写过程中,字符串大多被用于生成信息消息。在指标中,可能是满足特定条件(比如交易信号)的相关消息;而在 EA 交易中,则可能是交易活动结果的报告。运行时,EA 交易、脚本或指标可检查由用户设置的参数,如果参数设置无效,则会显示通知。除通知外,您有时还能看到提示消息,就参数设置给出建议。一般来讲,在 MQL5 中编程时,字符串首当其冲实现用户友好。

而且,字符串在操作文件时也不可或缺。写入数据或从文件中读取数据,都要利用字符串变量来实现。显然,人们可以选择另一种操作文件的方式——一种专为读取和写入数值变量和数组而提供的二进制法。但是,如果数据量不太大,最好还是使用文本文件和字符串。在这种情况下,程序操作对用户而言更清晰,而程序开发的过程也更简单,实现了数据的即时控制。文本文件数据看起来与程序内的数据一样。

在事前不清楚所需参数数量(比如平均累积的手数)的情况下,使用字符串可以极大地拓宽与数据(参数)输入相关的程序功能。这种情况下,可将值写入一个由分隔符(比如分号)分隔的单独字符串:

input string Lots="0.1; 0.2; 0.3; 0.5";

然后,当 EA 交易初始化时,字符串被分割,并填写一个数组的数值。遗憾的是,要在优化过程中仔细检查此类字符串参数(即,随着步长值设置初始值与最终值)是不可能的。在某些情况下,它可能更倾向于在属性窗口中使用大量的数值变量。但是,由于它们在数量上几乎没有限制,所以,我们可能会面临方便性与用途方面的问题(不管有无优化可能性方面的要求)。

已知某参数不要求优化的情况也很可能出现,比如启用通知。MQL5 支持各种用户通知方法:声音通知、弹窗通知、电邮通知和推送通知。您可以在属性窗口中为上述每一种通知创建一个布尔型开关(要求至少 4 个变量),或是将变量数量缩减为一个字符串变量。

如您需要启用声音通知,则写入 "s" (声音)。如还需要电邮通知,则添加 "e"。由此,只利用一个变量,您就可以启用任何通知组合。对于 EA 交易而言,外部参数的数量一点也不重要。只是用户友好性的问题。

另一方面,制定指标时,您要努力减少外部参数的数量。指标极有可能通过一个 EA 交易或使用 iCustom()IndicatorCreate() 函数的另一指标调用,而上述指标的参数数量受限(iCustom() 仅有 64 个参数,而 IndicatorCreate() 函数的参数数组大小为 60 个元素)。所以,使用字符串将有极大的实用价值。

本文将重温使用字符串的所有标准 MQL5 函数,而且我们还会创建几个有用的自定义函数。

 

声明一个字符串变量

与所有其它类型的变量一样,字符串变量亦可声明:

string str;
或是在声明后赋予一个值(初始化为一个值):
string str="Any text";

字符串长度方面没有限制。出于方便考虑,可将长字符串分割成多个子字符串:

string str= "A long string can be "
            "split into several "
            "substrings";
//--- output the results
Alert(str);

以此法初始化时,str 变量就会拥有包含“一个长字符串可分成多个子字符串”的字符串。

这里提前说一下,我们要注意:不带参数声明的字符串变量值与空字符串并不相同:

string str="";

您可以自己看一下:

string str1;
string str2="";
//--- output the comparison results
Alert(str1==str2);

运行此代码时,会弹出一个警示 "false" (错误)的窗口。未初始化的字符串变量有 NULL 值,与空字符串 "" 不同。您要记住这一点!使用字符串时,我们必须经常检查其是否为空。因此,您或者遵守将带有空字符串 "" 的所有字符串初始化的规则,或者确保其不与 "" 和 NULL 等同:

if(str!="" && str!=NULL)
  {
   //--- some operation with a string
  }

第一种方法更明智,因为它简化了检验条件。

检验变量的大小时,您也可以这样做。要确定大小,我们使用 StringLen() 函数:

if(StringLen(str)!=0)
  { 
   //--- some operation with a string
  }


连接字符串

使用字符串时,您要执行的最常见的主要操作就是将其连接起来,即连词成句。而连接是利用 "+" 号完成的:

string str1,str2,str3,str4,str5;
//--- assign values
str1="Programming";
str2="in MQL5";
str3="for MetaTrader 5";
//--- add up the strings
str4=str1+" "+str2;
str5=str1+" "+str3;
//--- output the results
Alert(str4);
Alert(str5);

执行此代码后,str4 变量将包含“MQL5 中的编程”,而 str5 变量则包含“MetaTrader 5 中的编程”。上例所示,是连接两个字符串、并将作为结果的字符串分配给另一变量的方式。

更多的情况通常是,一个附加的字符串被连接到主字符串:

string str1,str2,str3;
//--- assign values
str1="Programming";
str2="in MQL5";
str3="for MetaTrader 5";
//--- add up the strings to the main string
str1=str1+" "+str2;
str1=str1+" "+str3;
//--- output the results
Alert(str1);

执行此代码后,str1 字符串将包含“用于 MetaTrader 5 的 MQL5 中的编程”。上例所示,是将字符串与主字符串 str1 连接、并将结果分配给后者的方式。同样的操作可以通过一种更加简单的方式来编写:

str1+=str2;
str1+=str3;
或:         
str1+=str2+str3;

"=" 左边的 "+" 号,是指 "=" 右边的表达式被附加到 str1 变量。

还可以向主字符串的开始处添加一个字符串。可按第二到最后一个示例所示完成实施:主字符串被添加到附加字符串,而作为结果的字符串则被分配到主变量:

string str1,str2,str3;
//--- assign values
str1="Programming";
str2="in MQL5";
str3="for MetaTrader 5";
//--- concatenate strings, with a string being added to the beginning
str3=str2+" "+str3;
str3=str1+" "+str3;
//--- output the results
Alert(str3);

下述短句:“用于 MetaTrader 5 的 MQL5 中的编程”现在位于 str3 变量中。您可以使用某单一字符串完成相同实施:

str3=str1+" "+str2+" "+str3;
某些情况下,您可以利用 "," (逗号)执行连接。此法在调用 Alert()、Print() 或 Comment() 函数时可行:
Print(str1," ",str2," ",str3);

这种情况下的最终结果将与使用 "+" 号相同:

Print(str1+" "+str2+" "+str3);

"," 号实际上并不连接字符串。逗号是所有函数中各参数的一个分隔符。对于 Alert()Print()Comment() 函数而言,也是如此。这几个函数都有一个必选参数和大量的可选参数。参数实际都被传递到它们所连接的函数。参数的最大数量为 64。

如利用 FileWrite() 函数编写一个到文件的字符串,我们即可看到类似的内容。但是,如果您在 FILE_CSV 模式(带字段分隔符)打开一份文件,则要用打开文件时指定的分隔符替换逗号(如未指定分隔符,则默认使用制表符。)如果在不指定分隔符的情况下以 FILE_TXT 模式打开文件,则使用 "+" 号与 "," 的结果是一样的:

//--- write to the first file
int h=FileOpen("1.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1","2","3");
FileClose(h);
//--- write to the second file
h=FileOpen("2.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1"+"2"+"3");
FileClose(h);
//--- write to the third file
h=FileOpen("3.txt",FILE_WRITE|FILE_ANSI|FILE_TXT);
FileWrite(h,"1","2","3");
FileClose(h);
//--- write to the fourth file
h=FileOpen("4.txt",FILE_WRITE|FILE_ANSI|FILE_TXT);
FileWrite(h,"1"+"2"+"3");
FileClose(h);

执行此代码后,1.txt 文件中将包含 "1    2    3",而 2.txt 文件中则包含 "123" (已以 FILE_CSV 模式打开文件)。3.txt 和 4.txt 的内容则完全相同:"123" (以 FILE_TXT 模式打开)。本文并不是要讨论文件操作。因此,如果上个示例中有什么不懂的地方,也不必介意,因为它不会影响到您对下文内容的理解。只是要注意,处理字符串的添加时,使用 "+" 和 "," 所产生的效果不总是相同。

除 "+" 号之外,MQL5 还提供字符串添加专用的函数:StringAdd()StringConcatenate()。根据“MQL5 参考”中对于上述函数的描述,它们允许我们更省空间(在有效内存占用方面)、更快速地添加字符串。StringAdd() 函数允许我们将一个字符串添加到另一个:

string str1,str2,str3;
//--- assign values
str1="Programming";
str2="in MQL5";
str3="for MetaTrader 5";
//--- call the function to concatenate strings
StringAdd(str1," ");
StringAdd(str1,str2);
StringAdd(str1," ");
StringAdd(str1,str3);
//--- output the results
Alert(str1);

执行此代码后,str1 变量将拥有包含“用于 MetaTrader 5 的 MQL5 中的编程”的字符串。

StringConcatenate() 变量允许您同时组合多个字符串。传递给此函数的第一个参数,就是进一步列出的字符串被添加到的字符串变量。您可以传递给此函数的参数数量最多为 64:

string str1,str2,str3;
//--- assign values
str1="Programming";
str2="in MQL5";

str3="for MetaTrader 5";
//--- call the function for combining several strings
StringConcatenate(str1,str1," ",str2," ",str3);
//--- output the results
Alert(str1);

执行此代码后,str1 变量也将包含“用于 MetaTrader 5 的 MQL5 中的编程”。

 

将多个变量转换为一个字符串

生成消息字符串时,我们通常需要添加数值变量的值。要将整数变量(char、uchar、bool、short、ushort、int、uint、color、long、ulong、datetime)的值转换为字符串,我们使用 IntegerToString() 函数:

int x=1;
string str="x = "+IntegerToString(x);

如要转换布尔型变量,则返回的字符串中将包含 "0" (false) 或 "1" (true)。与之类似,如果您要转换颜色或日期类型的变量,则返回的字符串中将包含一个颜色或日期的数值表达式(比如,"65535" 为 clrYellow 的黄色,"1325376000" 为下述日期: 2012.01.01 00:00).

要将实际变量(双精度、浮点)转换为字符串,我们采用 DoubleToString() 函数。此函数的第二个参数会确定精确度(小数位数):

double x=1.23456;
string str1="x = "+DoubleToString(x,2);
string str2="x = "+DoubleToString(x,3);

执行此代码后,str1 变量将包含字符串 "1.23",而 str2 变量则将包含字符串 "1.235"。截断至指定位数,则是利用数学舍入规则执行。

TimeToString() 函数用于将日期和时间转换为一种标准格式的字符串(人类可理解):

datetime tm=TimeCurrent(); // Current time 
string str1=IntegerToString(tm);
string str2=TimeToString(tm);

执行此代码后,str1 变量中将包含带有一个时间数值表达式的字符串(自 1970 年 1 月 1 日以来逝去的秒数);而 str2 变量中则将包含格式化时间,比如 "2012.11.02 22:00" (年、月、日、小时、分钟)。

调用 TimeToString() 函数时,您可以选择指定日期和时间格式。可用选择包括:

string str1="Date and time with minutes: "+TimeToString(tm);
string str2="Date only: "+TimeToString(tm,TIME_DATE);
string str3="Time with minutes only: "+TimeToString(tm,TIME_MINUTES);
string str4="Time with seconds only: "+TimeToString(tm,TIME_SECONDS);
string str5="Date and time with seconds: "+TimeToString(tm,TIME_DATE|TIME_SECONDS);

为创建在程序属性窗口中作为下拉选项列表显示的枚举,MQL5 提供了一种极其方便的功能。而此类变量的值,亦可利用 EnumToString() 函数转换为一个字符串。下面是演示此函数运行的脚本代码:

//+------------------------------------------------------------------+
//| Create an enumeration                                            |
//+------------------------------------------------------------------+
enum EMode
  {
   OFF=0,
   Mode1 = 1,
   Mode2 = 2,
   Mode3 = 3 
  };
//+------------------------------------------------------------------+
//| Start the script                                                 |
//+------------------------------------------------------------------+
void OnStart()
  {
   EMode Value=1;
   //--- join strings together
   string str="The "+IntegerToString(Value)+ value" corresponds to "+EnumToString(Value)+ entry" of the Emode enumeration";
   //--- output the results
   Alert(str);
  } 
至于转换颜色变量,也有类似的可能性。您可以利用 ColorToString() 函数,将颜色值转换为颜色的名称
color ColorValue=clrRed;
string str=ColorToString(ColorValue,true);

执行此代码后,str 变量会存储包含 "clrRed" 的字符串。如果第二项参数被设置为 false,则此函数会返回带有 RGB 分量(红、绿、蓝)值的字符串:

color ColorValue=clrRed;
string str=ColorToString(ColorValue,false);

这种情况下,str 变量中存储的字符串会是 "255,0,0"。如果您处理的并非标准色(未于 web 调色板上定义,且因此没有名称),那么,可利用 ColorToString() 函数返回带分量值的字符串,不管第二项参数取什么值。

还有另一种利用类型转换转换变量的方法:

int x=123;
string str=(string)x;

如果利用此法转换布尔型变量,则字符串值为 " true" 或 "false":

bool x=true;
string str=(string)x;

转换双精度与浮点型变量要尽可能地精确,仅可舍去小数部分的零位:

double x1=0.1;
double x2=0.123string str1=(string)x1;
string str2=(string)x2;

执行此代码后,str1 变量将存储字符串值 "0.1",而 str2 变量则将包含字符串值 "0.123"。

 

特殊字符输出

将某字符串变量初始化为一个值时,可赋值的字符串应写在双引号内,如此编译器才能将此字符串与程序代码进行区分。为了能将引号置入字符串中,您需要指明此处使用的这种符号并非其常规用途(作为区分代码与字符串的字符),而是作为字符串的一部分。为此,要在引号前加一个反斜杠 "\":

string str1="Simple text";
string str2="\"Text in quotes\"";
//--- output the results
Alert(str1);
Alert(str2);

由于反斜杠亦被视为一个特殊字符,在其前方必须另置一个反斜杠,才能实施字符串中反斜杠的输出:

string str="\\";
Alert(str);

执行此代码后,"\" 就成为了字符串仅剩的字符。

字符串还可以包含一个水平制表符,用 "\t" 表示:

string str="Column-1\tColumn-2\tColumn-3";
Alert(str);

这种情况下,str 变量将拥有包含 "Column-1        Column-2        Column-3" 的字符串。

文本还可以利用换行符显示,利用 "\n" 分成多行:

string str="Line-1\nLine-2\nLine-3";
Alert(str);

此处,作为执行 Alert() 函数的结果,您将有三行文本。

使用 Alert()MessageBox() 函数显示、以及写入一份文件时,您可以使用 "\t" 和 "\n"。但是,如果显示到某图表注释(Comment() 函数),则只有 "\n" 作为包装字符应用,而制表符"\t" 则被忽略。利用 Print() 函数完成输出后,"\n" 像以前一样应用(字符串的每个部分均在日志的独立行中输出),而 "\t" 则会像在日志文件中一样(利用 Print() 函数存储所有消息输出),被替换为一个空格。

 

基于模式的字符串格式化

格式化某字符串以供输出时,您可能需要将多个数值变量的值纳入其中。这可以通过添加字符串并将数值变量转换为字符串实现。但在这种情况下,构建一条消息的代码字符串会太长,而且如果程序有必要进行修改,也难于理解和编辑:

//--- initialize the variables
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- long addition of strings
string str="Variable1 = "+IntegerToString(Variable1)+", Variable2 = "+IntegerToString(Variable2)+", Variable3 = "+IntegerToString(Variable2);
//--- output the results
Alert(str);

同样的任务,利用 StringFormat() 函数解决起来,就简单多了。传递给此函数的第一个参数是一个消息模板,带有插入变量和设置输出格式的指定位置。接下来,是按其在模板中出现的顺序,枚举所有的变量:

//--- initialize the variables
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- simpler addition of strings
string str=StringFormat("Variable1 = %i, Variable2 = %i, Variable3 = %i",Variable1,Variable2,Variable3);
//--- output the results
Alert(str);

待插入变量的位置标有 "%" 且后接 "i",在上例中,它表示变量必须作为整数输出。更确切地说,"i" 代表字符整数变量 (char, short, int, color),而针对无符号整数变量 (uchar, bool, ushort, uint),我们则使用 "u"。至于 long、ulong 和 datetime 数据类型的变量,您要通过在类型前加 "I64" 来指定变量的大小:

string LongMin=StringFormat("%I64i",LONG_MIN);
string LongMax=StringFormat("%I64i",LONG_MAX);
string ULongMax=StringFormat("%I64u",ULONG_MAX);
string DateTimeMax=StringFormat("%I64u",DateMax);
//--- output the results
Alert("LongMin = "+LongMin);
Alert("LongMax = "+LongMax);
Alert("ULongMax = "+ULongMax);
Alert("DateTimeMax = "+DateTimeMax);
作为此代码的执行结果,您会看到一个带变量值的弹出窗口。

实数的格式用 "f" 表示:
double Percents=5.5;
//--- real number as a string
string str=StringFormat("Percents = %f",Percents);
//--- output the results
Alert(str);

执行此代码后,str 变量会存储下述字符串:"Percents = 5.500000"。默认的输出精确度为 6 个小数位。您也可以设置所需的小数位:

string str=StringFormat("Percents = %.2f",Percents);

为此,紧随小数位数后加一个表示小数符号的点,比如上例中的 2。这种情况下,str 变量将包含如下字符串:"Percents = 5.50"。此格式化选项与 DoubleToString() 函数完全相同。

您可以指定数字的总长度,方法是紧随 "%" 之后写入 "0" 和一个决定相关数字长度的数字,然后再指定小数位数(如有必要):

string str=StringFormat("Percents = %06.2f",Percents);

这里的总长度为 6 位,其中 1 位将用于小数点,还有另两位分别代表两个小数位。因此,str 变量中存储的字符串将是 "Percents = 005.50"。

如您需要在消息中输出百分比号 "%",则要在一行中输入两次,即 "%%",因为其中一次将被用于指示待插入值位置:

string str=StringFormat("Percents = %06.2f%%",Percents);

这种情况下,str 变量将包含 "Percents = 005.50%"。

您还可以决定输出整数变量时的数字长度:

int Variable=123;
//--- integer as a string with a set output length
string str=StringFormat("Variable = %05i",Variable);
//--- output the results
Alert(str);

执行此代码后,str 变量会存储下述字符串:"Variable = 00123"。

如果指定的位数小于此数字中的位数,则输出仍会正确完成:

string str=StringFormat("Variable = %02i",Variable); 

这里,str 变量将包含如下字符串:"Variable = 123",即尽管指定长度为 2,但输出数字仍将有 3 位。

实数可利用科学计数法(六个小数位的小数部分和幂)输出,为此我们采用 "e" 符号:

double Variable=123.456;
//--- real number as a string in scientific notation
string str=StringFormat("Variable = %e",Variable);
//--- output the results
Alert(str);

执行此代码后,str 变量将包含 "1.234560e+002"。您也可以使用大写的 "E",其效果与小写 "e" 类似,在格式化字符串中,要用大写 "E" 替换掉小写 "e"。

格式化实数,还有另一种方式 - 使用仅可以输出 6 位(不包括小数点)的 "g"。如果某数字的整数部分长度超过 6 位,则要利用科学计数法输出该数字:

double Variable1=12.3456789;
double Variable2=1234567.89;
//--- get real numbers as strings using "g"
string str1=StringFormat("Variable = %g",Variable1);
string str2=StringFormat("Variable = %g",Variable2);
//--- output the results
Alert(str1+" "+str2);

上例中,str1 变量将包含 "12.3457",而 str2 变量将包含 "1.23457e+006"。如您使用大写的 "G" 替换,效果也一样,唯一的差别是输出中的小写 "g" 也会被替换为大写的 "G"。

StringFormat() 函数允许您转换数字的表示格式,即将十进制的数字转换为八进制或十六进制。要将某数字转换为八进制,您需要使用 "o" 字符:

int Variable=17;
//--- real number as a string in the octal system
string str=StringFormat("Variable = %o",Variable);
//--- output the results
Alert(str);

执行此代码后,str 变量会如下存储字符串:"Variable = 21" (8*2+1=17)。

"x" 或 "X" 用于将数字转换为十六进制。这种情况下,如您使用小写的 "x",则十六进制数字将由小写字母构成;而如果使用大写的 "X",则是大写字母构成:

color Variable=clrBlue;
//--- real number as a string in the hexadecimal system
string str=StringFormat("Variable = %x",Variable);
//--- output the results
Alert(str);

执行此代码后,str 变量将包含 "Variable = ff0000"。

与之相似,您可以利用 "d" 字符将一个十六进制的数字转换回十进制:

int Variable=0x0000ff;
//--- real number as a string in the decimal system
string str=StringFormat("Variable = %d",Variable);
//--- output the results
Alert(str);

执行此代码后,str 变量将存储的字符串就是 "Variable = 255"。

"s" 字符可用于输出字符串变量:

string Variable="text";
//--- output the string variable
string str=StringFormat("Variable = %s",Variable);
//--- output the results
Alert(str);

执行上述代码后,包含 "Variable = text" 的字符串就会存储到 str 变量中。

鉴于负数会由于 "-" 号偏移,所以有时可能需要您对齐输出到某列中的数字。要对齐正数与负数,您要在字符串开头处 "%" 的后面加一个空格。这种情况下,与开头处带空格的正数不同的是,负数不带空格输出。

int Variable1=1;
int Variable2=-1;
//--- representation of numbers as aligned strings
string str1=StringFormat("Variable1=% 03i",Variable1);
string str2=StringFormat("Variable2=% 03i",Variable2);
//--- output the results
Alert(str1);
Alert(str2);

执行此代码后,str1 变量将包含字符串 "Variable1= 01"(带一个空格的字符串),而 str2 变量则会如下存储该字符串:"Variable2=-01"。

与 StringFormat() 类似的还有两个函数。那就是在操作方面完全一致的 PrintFormat() 和 printf()。它们与 StringFormat() 函数唯一的区别,就是它们会以一种类似 Print() 函数的方式将文本输出到日志。

事实上,StringFormat() 函数提供的功能要多得多,而上文提供的内容,只是代表解决输出数字格式化相关的大部分问题的最低需求。
 

不同语言消息

StringFormat() 函数赋予您一次机会,让您利用一种根据终端所设界面语言、以不同语言显示消息的非常实用的功能来强化您的程序。

您可以利用 TERMINAL_LANGUAGE 标识符调用 TerminalInfoString() 函数,以查看设定的界面语言。运行程序时,我们会根据界面语言准备一种格式字符串,再于程序中使用它。下面是一个已实施上述功能的 EA 交易的模板:

//--- variable for a format string
string FormatString;
//+------------------------------------------------------------------+
//| Handling the Init event                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- get the format string
   FormatString=GetFormatString();
//--- additional call in case you want to ensure that the Expert Advisor operates at least once at weekends
   OnTick();
   return(0);
  }
//+------------------------------------------------------------------+
//| Handling the Tick event                                          |
//+------------------------------------------------------------------+
void OnTick()
  {
   int Variable1,Variable2,Variable3;
   Variable1=MathRand()%10;        // Random number from 0 to 10
   Variable2=MathRand()%10;        // Another random number
   Variable3=Variable1+Variable2; // Sum of numbers
//--- output the results
   Alert(StringFormat(FormatString,Variable1,Variable2,Variable3));
  }
//+------------------------------------------------------------------+
//| Determining the format string                                    |
//+------------------------------------------------------------------+
string GetFormatString(void)
  {
   string Language=TerminalInfoString(TERMINAL_LANGUAGE);
//--- language check
   if(Language=="Russian") return("%i плюс %i равно %i");     // Russian
   if(Language=="Spanish") return("%i más %i es igual a %i"); // Spanish
//--- English - in all other cases
   return("%i plus %i equals %i");
  }

此 EA 交易会计算出两个随机数字的和,并输出其操作相关消息,比如 "1 plus 2 makes 3" (1 加 2 等于 3。)

关于字符串的输出,差不多就是这样。现在,我们要继续稍微复杂一些、但却更加有趣的字符串处理。

 

处理字符串的关键功能

如果字符串是通过程序属性窗口输入或是由文件读取,则可能包含不必要的空格。无论是用户的粗心大意还是出于便利需要,都可能出现这种情况。在通过任何方式使用字符串之前,最好都要删除左右两端的空格。为此,MQL5 提供个两个函数 - StringTrimLeft() (删除左端的空格)和 StringTrimRight() (删除右端的空格)、除了空格外,上述函数还可以移除制表符和新行符。处理字符串时,我们通常需要一次性删除两端的空格,所以实施函数会非常有用:

string Trim(string Str)
  {
   StringTrimLeft(Str);
   StringTrimRight(Str);
   return(Str);
  } 

输入实数时,用户可能通常都会用一个逗号来替代点号。因此,处理实数时,您要提供使用点号和逗号两种小数符号的可能性。要将某个字符串替换为另一个,我们使用 StringReplace() 函数:

string str="123,456";
//--- replace a comma with a dot
StringReplace(str,",",".");
double Value=StringToDouble(str);
//--- output the results
Alert(DoubleToString(Value));

如果您没有将 "," 替换为 ".",那么在将字符串转换为数字的过程中,数字的小数部分会被截掉。

某些情况下,也可能要求您将连续的空格替换为一个空格。为此,您首先要将制表符替换为一个空格,然后再将两个空格替换为一个,直到只剩下一个空格:

string str="Column-1 \t Column-2 \t Column-3";
//--- replace the tab character with a space
StringReplace(str,"\t"," ");
//--- get one space instead of the series of spaces
while(StringReplace(str,"  "," ")>0){}
//--- output the results
Alert(str);

StringReplace() 函数会返回已完成的替换数量,如有错误,则返回 -1。在此函数返回正值的情况下,如此继续此循环,直到每种情况中所有连续的空格都被替换为一个空格。此循环的主体中不包含任何代码。每次迭代检查循环条件时,我们都调用 StringReplace() 函数。

StringReplace() 函数允许我们替换不同长度的子字符串:

string str="Programming in MQL5!";
//--- replace the substring, output the results
StringReplace(str,"in MQL5","for MetaTrader 5");
Alert(str);
//--- reverse replacement, output the results
StringReplace(str,"for MetaTrader 5","in MQL5");
Alert(str);

执行此代码后,str 变量会在第一次替换后拥有包含 "Programming for MetaTrader 5" (MetaTrader 5 编程)的字符串;在第二次替换后,则拥有包含 "Programming in MQL5!" (在 MQL5 中编程)的字符串。

StringFind() 函数用于搜索子字符串。它会返回字符串内第一次出现子字符串的索引。传递给此函数的第一个参数,就是搜索执行的一个字符串。第二项参数决定目标子字符串,而第三个参数(可选)可决定确定搜索开始的位置。如未指定第三个参数,则此函数会将其作为 0 值处理,即搜索从字符串的开头处开始。我们一起到 "Programming in MQL5 for MetaTrader 5" 字符串中找到子字符串 "5" 的位置:

string str="Programming in MQL5 for MetaTrader 5";
//--- get the position of the character
int Pos=StringFind(str,"5");
//--- output the results
Alert(IntegerToString(Pos));

执行此代码之后,Pos 变量值将为 23。子字符串 "5" 共出现两次,但此函数仅返回第一次出现的位置。如您只通过简单地查看字符串来计算位置,则会得到 24。事实上,此函数是从零开始计数,而不是从 1 开始。如果未在字符串中找到目标子字符串,则此函数会返回 -1。

有时,您可能也需要查找子字符串最后一次出现的位置。为此,我们必须编写一个自定义函数——StringFindRev()。我们将从搜索子字符串的第一次出现开始,然后根据找到的位置移动搜索的起点,如此循环:

int StringFindRev(string Str,string Find)
  {
//--- the pos variable for the returned value
   int pos;
//--- auxiliary variable initialized to -1,
//--- in case the substring is not found in the string
   int tmp=-1;
//--- loop. It will be executed at least once
   do
     {
      //--- assign the last known position of the substring
      pos=tmp;
      //--- continue searching (using the third parameter of the function)
      tmp=StringFind(Str,Find,tmp+1);
     }
   while(tmp!=-1); // If the substring is not found in the remaining part of the string, the loop 
                   // is terminated and the pos variable stores the last
                   // known position
//--- return the position
   return(pos);
  }
我们尝试使用此函数:
string str="Programming in MQL5 for MetaTrader 5";
//--- call the function for searching for a position of the last occurrence of the character in the string
int pos=StringFindRev(str,"5");
//--- output the results
Alert(pos);

执行此代码后,Pos 变量的值将为 40。

StringSubstr() 函数用于从某个给定位置获取给定长度的一个子字符串。由位置 23 获取长度为 1 的子字符串:

string str="Programming in MQL5 for MetaTrader 5";
//--- get the substring of the given length from the given position
string str2=StringSubstr(str,23,1);
//--- output the results
Alert(str2);

结果数据为 "5"。

我们已经研究过了几个主要的函数,现在我们就用它们来编写一个用于从字符串中删除某给定字符列表的有用函数。此函数会收到源字符串,以及一个表示一系列要从源字符串中删除字符的字符串。

string TrimL(string Str,string List="\t\n ;")
  {
//--- variable for one character of the Str string
   string ch;
   int Len=StringLen(Str);
   int i=0;
//--- loop iteration over all characters of the Str string
   for(;i<Len;i++)
     {
      //--- the next character of the Str string
      ch=StringSubstr(Str,i,1);
      //--- if this character is not on the List list, the string should start from this position 
      if(StringFind(List,ch,0)==-1)
        {
         break; // terminate the loop
        }
     }
//--- get the substring and return it
   return(StringSubstr(Str,i));
  }

此函数默认会删除制表符与新行符,以及一个空格和分号 ";"。

用于删除右端的相同函数:

string TrimR(string Str,string List="\t\n ;")
  {
//--- variable for one character of the Str string
   string ch;
   int Len=StringLen(Str);
//--- characters in the string are numbered from 0, so the last character index is one less than the string length
   int i=Len-1;
//--- loop iteration over all characters of the Str string
   for(;i>=0;i--)
     {
      //--- the next character of the Str string
      ch=StringSubstr(Str,i,1);
      //--- if this character is not on the List list, the string should start from this position 
      if(StringFind(List,ch,0)==-1)
        {
         break; // terminate the loop
        }
     }
//--- get the substring and return it
   return(StringSubstr(Str,0,i+1));
  }

此函数默认会删除制表符与新行符,以及一个空格和分号 ";"。读取 CSV 文件即可显示出其用处。在这些文件内部,字符串右端可能会有大量的字段分隔符(通常为分号 ";")。

人认为 "А" 和 "а" 之类的大小写字母的含义没什么不同,但计算机却将其视为两种完全不同的字符。在利用 SymbolInfoDouble() 函数请求市场数据时,如果您用 "eurusd" 替代 "EURUSD",则此函数不会返回所需值。如果在属性窗口中输入交易品种名称,则非常有可能发生这种情况。要在 MQL5 中更改大小写,您可以利用 StringToLower() 函数(改为小写)和 StringToUpper() 函数(改为大写):

string str="EuRuSd";
string str1=str;
string str2=str;
//--- change the case of strings
StringToUpper(str1);
StringToLower(str2);
//--- output the results
Alert(str1," ",str2);

执行此代码后,str1 变量将存储包含 "EURUSD" 的字符串,而 str2 变量则将拥有包含 "eurusd" 的字符串。

如果您需要对比字符串,而无需考虑其大小写,那么,StringCompare() 函数最适合您。此函数的前两个参数,分别是待对比的两个字符串。第三个参数会决定两个字符串可否对比,考虑 (true) 或不考虑 (false) 其大小写:

int Result=StringCompare("eurusd","EURUSD",false);
Alert(Result); 

如果此函数返回 0,则字符串完全相同。如果第一个字符串小于第二个,则返回 -1;如果第一个字符串大于第二个,则返回 1。“大于”和“小于”是指字符串按字母顺序排列时的状态。字母 "b" 大于字母 "a":

int Result=StringCompare("a","b",true);
Alert(Result); 

这种情况下,此函数将返回 -1。

现在,在我们继续讲解其它函数之前,有必要快速地消化一下理论。
  

人类与计算机眼中的字符串

字符串给人类的感觉相当明确 - 就是一种由字符构成的文本。与人类相比,计算机在结构上就相对简单一些,只处理数字。计算机会将图像、字符串以及其它一切,都看作数字。而字符串就是一组数字,其中的一个字符对应一个数字甚或一个代码,另一个字符对应另一个代码,如此等等。这些代码就是所谓的 ASCII 码(“美国资讯交换标准码”的英文缩写)。下文我们还会用到 ASCII 这一术语,所指均为包含 256 个代码的扩展 ASCII。由此,我们可以说,计算机的“字母系统”由 256 个字符构成。就像不同的人群和语言有不同的字母系统一样,计算机也拥有各种各样的字符集 - 代码页。俄罗斯的计算机用户大都习惯于使用 Windows-1251——一种字符编码,其中包括拉丁和西里尔字符,以及数字、标点符号和其它符号。图 1 所示即为 Windows-1251 代码页:


图 1. Windows-1251 代码页。

前 32 个字符未显示,它们都是控制字符。它们未能如此显示,但却影响着其它字符的显示,比如制表符(代码 9)、换行符(代码 10)等。

中欧各种语言中用于表示文本的编码为 Windows-1250 (图 2):


图 2. Windows-1250 代码页。

请注意,从代码 192 开始,代码页 1251 带有俄语字母,而代码页 1250 则带有包含欧洲语言变音符号(带有决定音值方面细微变化的变音符号的字母)的字母。

256 个字符,数量很小。但如果文本需要用多种语言编写,比如俄语和法语(有大量的变音符号)、或英语和阿拉伯语(与其它语言中的字符有很大差异),困难就出现了。也有中文和日文之类的象形文字,都拥有成千上万的字符,这种情况下的困难更加明显。您可能需要利用某种其它方式,对未纳入其代码页的字符进行编码。如果您熟悉 HTML,则应该知道在 HTML 页面中插入非标准字符的可能性,比如代码 À 用于显示 À, Á 用于渲染 Á,如此等等。

最近,Unicode 编码的使用也越来越常见。这种编码方式中,一个字符并非编码为一个单字节(从 0 到 255 的数字),而是两个字节,由此总计有 65536 个字符。该字符集包含世界上现有的所有字母表字母,甚至还包括图 3 所示的常见象形文字(仍需在您的计算机上安装相应的字体):

图 3. 不同字母表的字母及象形文字  

图 3. 不同字母表的字母及象形文字

MQL5 中的字符串使用 Unicode 编码。换句话说,字符串中的字符可以用从 0 到 65535 之间的数字来表示。从 0 到 127 的代码字符,在 ASCII 与 Unicode 中完全相同。文本文件可包含利用 ASCII 或 Unicode 编码的文本,所以 MQL5 在处理 ASCII 与 Unicode 中的字符串的功能方面自然也就有所不同。

 

将字符串转换为数组,再转回字符串

处理字符串时,StringLen()、StringFind()、StringSubst() 和 StringReplace() 函数通常足以解决绝大多数的实际任务。但是也可能有些任务将字符串作为数字处理则更容易解决,比如加密、数据压缩、检验值的计算等。尽管此类任务不是每天都做,但不一定哪天,您就需要解决它们了。还有一些更重要的任务要求将字符串转换为数组,即将字符串参数传递给 Windows API (应用程序接口)函数。

要将字符串转换为 Unicode 数组,我们采用 StringToShortArray() 函数;如要转换为 ASCII 数组,则使用 StringToCharArray() 函数:

string str="MetaTrader 5";
//--- converting the string to a Unicode array
short sha[];
StringToShortArray(str,sha);
//--- converting the string to a ASCII array
uchar cha[];
StringToCharArray(str,cha);
//--- flag the difference
bool Dif=false;
//--- compare the arrays element by element
for(int i=0;i<StringLen(str);i++)
  {
   if(sha[i]!=cha[i])
     {
      Dif=true;
     }
  }
//--- output the results
if(Dif) Alert("Different");
else    Alert("Identical");

如果上例中使用 StringToShortArray() 与 StringToCharArray() 函数得到的数组完全相同,则会有弹窗显示 "Identical" (完全相同);如果不同,则弹出消息为 "Different" (不同)。至于 "MetaTrader 5" 字符串,数组是完全相同的,因为其字符串是由多达 127 个字符代码构成的。

字符代码超过 127 个的有些不同。StringToShortArray() 函数运行的结果,随时随地都一致;而 StringToCharArray() 函数的运行结果,则取决于操作系统的区域设置。

在 Windows 7 中,系统语言可在控制面板 - 语言和区域 - 更改显示语言中选择。

看看下面的示例。在代码页 1251 中,代码 192 对应着字母 "А" (俄语字母表的第一个字母);而相同的代码在 1250 编码中则对应着字母 "Ŕ" (捷克语字母表中最广为人知的字母之一)。使用 StringToShortArray() 函数时,字母 "А" 将始终为代码 1040,而字母 "Ŕ" 则为代码 340。如果系统被设置为俄语,那么在使用 StringToCharArray() 函数时,字母 "А" 将对应代码 192 (正确),而字母 "Ŕ" 将对应代码 82 (拉丁语 "R")。但如果系统被设置为捷克语,则字母 "А" 将对应代码 63 (问号),而字母 "Ŕ" 将对应代码 192 (正确)。包含变音符号的字符由类似的拉丁字母替代,同时不太常用的字符则由问号替代。

请注意字符串和结果数组的大小:

int StrLen=StringLen(str);
int shaLen=ArraySize(sha);
int chaLen=ArraySize(cha);
//--- output the lengths
Alert(StringFormat("%i, %i, %i",StrLen,shaLen,chaLen));

数组中的元素数量比字符串中的字符数量多一个。这与字符串末端用带有代码 0 的字符标注有关。该字符不显示于字符串中,但它对于计算机表示显示的字符串结束有重大意义。数据交换并非始终按照一次一个字节(字符)的速度、按照对应字符串的数量执行,但带有代码 0 的字符会让您在任何情况下都能确定字符串的结束。同样是零也可能导致问题,比如说加密或应用压缩算法时,特定字符也可以转换为带代码 0 的字符。这种情况下,如果您反向将数组转换为字符串,则字符串会不完整。这种类型的任务要求使用特别的技巧,但不在本文的讨论范围之内。

将数组逆转换为字符串,利用 ShortArrayToString()CharArrayToString() 函数有可能实现:

//--- convert an array of Unicode codes to a string
short sha[]={85,110,105,99,111,100,101};
string str1=ShortArrayToString(sha);
//--- convert an array of ASCII codes to a string
uchar cha[]={65,83,67,73,73};
string str2=CharArrayToString(cha);
//--- output the results
Alert(str1+" "+str2);

作为上述代码的结果,str1 变量会存储 "Unicode" 字符串,而 str2 会存储 "ASCII" 字符串。

还有两个类似的函数:ShortToString()CharToString()。它们可将 short 或 char 型的单变量转换为由一个字符构成的字符串。CharToString() 函数有极强的实用价值。一种允许您显示不同符号的图形对象——OBJ_ARROW,被固定在纵向的价格轴和横向的时间轴上,如此一来,符号就会随着您滚动图表而移动。用 OBJ_LABEL 搭配 Wingdings 字体,我们就能显示出屏幕上固定于坐标的不同符号,并由此创建各种信息面板。我们到 Wingdings 符号表中找到所需的符号,将其代码转换为字符串,再利用 OBJ_LABEL 图形对象进一步显示该字符串:

   ObjectCreate(0,"lbl",OBJ_LABEL,0,0,0);           // create the LABEL graphical object
   ObjectSetInteger(0,"lbl",OBJPROP_XDISTANCE,100);   // set the X-coordinate
   ObjectSetInteger(0,"lbl",OBJPROP_YDISTANCE,100);   // set the Y-coordinate
   ObjectSetInteger(0,"lbl",OBJPROP_FONTSIZE,20);     // set the size
   ObjectSetString(0,"lbl",OBJPROP_FONT,"Wingdings"); // set the Wingdings font
   string Icon=CharToString(37);                   // 37 - the bell
   ObjectSetString(0,"lbl",OBJPROP_TEXT,Icon);       // set the displayed text

执行此代码后,图表中会出现一个钟形图标。您滚动图表时,此钟形图标都会保持其位置。

StringGetCharacter()StringSetCharacter() 又是两个处理字符代码的函数。它们可处理 Unicode 代码。StringGetCharacter() 函数允许您在字符串的给定位置获取字符的代码:

string str="L5";
//--- get the Unicode code of the character at the given position in the string
ushort uch1=StringGetCharacter(str,0);
ushort uch2=StringGetCharacter(str,1);
//--- output the results
Alert(StringFormat("%i, %i",uch1,uch2));

执行此代码后,uch1 变量将存储值 76,而 uch2 将存储 53。

StringSetCharacter() 函数允许您更改给定位置字符的代码,以及在字符串尾部添加字符:

string str="MQ5";
//--- replace the character at the given position in the string with the Unicode character corresponding to the passed code
StringSetCharacter(str,2,76);
Alert(str);
//--- add the Unicode character corresponding to the passed code to the end of the string
StringSetCharacter(str,3,53);
Alert(str);

执行此代码后,替代 "MQ5" 的 str 变量会首先存储 "MQL",然后才是 "MQL5"。

 

调用 API 函数

有些 API 函数利用字符串参数作为其变量。例如,第三方应用 WinExec 的函数将一个 uchar 型数组用作其第一个参数:

#import "kernel32.dll"
int WinExec(uchar &Path[],int Flag);
#import 

我们尝试运行 notepad.exe (记事本)——一种标准的 Windows 程序。将指向记事本的路径转换为指向一个 uchar 型数组:

string PathName="C:\\WINDOWS\\notepad.exe";
uchar ucha[];
StringToCharArray(PathName,ucha);
int x=WinExec(ucha,1); 

此函数操作将导致记事本文本编辑器打开。

利用 Unicode 在窗口中显示消息的 MessageBoxW 函数,是 API 函数中使用字符串参数的又一实例。为此,我们会将 ushort 型数组作为其参数传递:

#import "user32.dll"
int MessageBoxW(int hWnd,ushort &szText[],ushort &szCaption[],int nType);
#import

现在利用此函数,在窗口中显示一条消息:

ushort arr[];
ushort capt[];
//--- convert
StringToShortArray("Programming in MQL5 for MetaTrader 5.",arr);
StringToShortArray("Message",capt);
//--- print the message
MessageBoxW(0,arr,capt,0);

执行此代码后,您就可以看到带有下述消息的一个窗口:"Programming in MQL5 for MetaTrader 5"。

要注意的是,上例中并没有必要使用 ushort 型数组,您只需将字符串作为函数参数传递即可:

#import "user32.dll"
int MessageBoxW(int hWnd,string szText,string szCaption,int nType);
#import
//+------------------------------------------------------------------+
//| Function for running the script                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   MessageBoxW(0,"Programming in MQL5 for MetaTrader 5","Message",0);
  }

此代码的结果将与上面一样。但是,想要显示正确的消息,您不能将 uchar 型数组作为函数参数使用:

#import "user32.dll"
int MessageBoxW(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Function for running the script                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- convert
   StringToCharArray("Programming in MQL5 for MetaTrader 5.",arr);
   StringToCharArray("Message",capt);
//--- print the message
   MessageBoxW(0,arr,capt,0);
  }

代码会被无错误执行,且会有一个弹窗出现,但消息却会失真。

至于有必要在 ASCII 编码中显示字符串的情况(比如您已经有一个 uchar 型数组),有一个类似的函数——MessageBoxA。要正确显示此消息,只有 uchar 型数组应被作为字符串参数传递给此函数。现在,导入此函数并调用它以显示消息:

#import "user32.dll"
int MessageBoxA(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Function for running the script                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- convert
   StringToCharArray("Programming in MQL5 for MetaTrader 5",arr);
   StringToCharArray("Message",capt);
//--- print the message
   MessageBoxA(0,arr,capt,0);
  }

我们又一次得到了正确的消息 "Programming in MQL5 for MetaTrader 5"。

基本上,对处理字符串的许多 WinAPI 函数有两种选择 - 处理 ASCII 字符串的选择,以及处理 Unicode 字符串的选择。

要调用函数,请在终端设置中启用 DLLs (Terminal - Main Menu - Tools - Options - Expert Advisors - Allow DLL imports),或是在运行某脚本、EA 交易或指标时,勾选属性窗口 Dependencies (依赖性)选项卡中的 "Allow DLL imports" (允许 DLL 导入)。指定恰当的脚本属性,以令其打开脚本属性窗口:

#property script_show_inputs

 

无限制的参数输入

用户在属性窗口中输入参数,并用分号分隔:

input string Lots="0.1; 0.2; 0.3; 0.5";

我们需要将此字符串转换为一个双精度型的变量数组。

在 MQL5 中,可利用 StringSplit() 函数来分割字符串。被传递到此函数的第一个参数是一个字符串,第二个参数是分隔符的 ASCII 代码,而第三个参数是一个将存储函数运行结果的数组。确定 ASCII 代码有一种非常简单的方法 - 您需要将所需字符放入单引号中:

int Code='A';
Alert(IntegerToString(Code)); 

此代码执行后,Code 变量将存储值 65 ,作为拉丁字符 "A" 的 ASCII 代码。

我们将此问题的这一解决方案表达为一个单独函数,如此则方便我们在必要时使用它。被传递给此函数的第一个参数会是一个字符串,第二个参数将是一个通过引用返回的数组。此函数的代码已提供如下,带有详细的注释,就不需要更多地阐释了:

int ParamsToArray(string Str,double &Params[])
  {
//--- delete spaces at the ends
   StringTrimLeft(Str);
   StringTrimRight(Str);
//--- if the string is empty
   if(StringLen(Str)==0)
     {
      ArrayFree(Params); // free the array
      return(0);         // function operation complete
     }
//--- auxiliary array
   string tmp[];
//--- split the string
   int size=StringSplit(Str,';',tmp);
//--- delete spaces at the ends for each element of the array
   for(int i=0;i<size;i++)
     {
      StringTrimLeft(tmp[i]);
      StringTrimRight(tmp[i]);
     }
//--- delete empty elements from the array (user could accidentally 
//--- put the separator two times in a row or at the end of the string)
   for(int i=size-1;i>=0;i--)
     {
      if(StringLen(tmp[i])==0)
        {
         ArrayCopy(tmp,tmp,i,i+1);
         size--; // array size reduced
        }
     }
//--- scale the array according to the new size
   ArrayResize(tmp,size);
//--- replace commas with dots
   for(int i=0;i<size;i++)
     {
      StringReplace(tmp[i],",",".");
     }
//--- prepare the array to be returned
   ArrayResize(Params,size);
//--- convert all elements to the double type and fill the array to be returned 
   for(int i=0;i<size;i++)
     {
      Params[i]=StringToDouble(tmp[i]);
     }
//--- the function returns the number of parameters
   return(size);
  }

 

将字符串转换为各种变量

ParamsToArray() 函数一直使用标准的 StringToDouble() 函数将字符串转换为双精度型变量的字符串。同一个函数亦用于转换为浮点类型。也存在用于转换为其它变量类型的标准函数。

StringToInteger() 函数可将字符串转换为整数变量:

string Str="12345.678";
//--- convert the string to an integer
long Val=StringToInteger(Str);
//--- inverse convert and output the results
Alert(IntegerToString(Val));

执行此代码后,Val 变量将存储值 12345。小数部分被截掉。

StringToTime() 函数可将一个时间的字符串表达式转换为相关的数值。如果您未指定时间,则默认值将为 "00:00":

string Str1="2012.11.02 22:00";
string Str2="2012.01.01";
//--- convert the string expression of time to the datetime type
datetime DateTime1=StringToTime(Str1);
datetime DateTime2=StringToTime(Str2);

StringToColor() 函数允许您将颜色名称(标准 web 颜色)转换为相关的数值,或是转换一个 RGB 分量的字符串:

string Str1="clrYellow";
string Str2="255,255,0";
color Color1=StringToColor(Str1);
color Color2=StringToColor(Str2); 

将字符串转换为日期和颜色类型,还有一种方法。可在为变量赋值时使用:

datetime DateTime=D'2012.11.02 22:00';
color Color=C'255,255,0'; 

"D" 写在日期字符串表达式的前面,而日期本身则放入单引号内。颜色字符串表达式的最前面是字母 "С",而 RGB 分量则放于单引号内,并由逗号隔开。

 

启用通知

用户输入启用某特定通知类型的连续字符。此法可用于启用各种通知组合。警示通知对应着 "а",声音通知对应着 "s",电邮通知对应着 "e",而推送通知则对应着 "p"。此外,您还可以将 1 或 0 添加到字符串,以表示信号检验所在的柱(在指标中有用)。此代码表达为一个函数,第一个参数是一个字符串,后接通过引用返回的 Shift 变量(柱数),以及对应着不同通知方法的布尔型变量。此代码提供了详细的注释:

void NotifyOnOff(string Str,int &Shift,bool &Alerts,bool &Sounds,bool &EMail,bool &Push)
  {
//--- Convert the string to lower case to allow the user
//--- to use both lowercase and uppercase characters.
   StringToLower(Str);
//--- search for characters in the string
   Alerts=(StringFind(Str,"a")!=-1);    // "a" found
   Sounds=(StringFind(Str,"s")!=-1);    // "s" found
   EMail=(StringFind(Str,"e")!=-1);     // "e" found
   Push=(StringFind(Str,"p")!=-1);      // "p" found
//--- search for zero
   if(StringFind(Str,"0")!=-1) Shift=0;  // "0" found in the string
   else                       Shift=1; // by default
  }

现在,属性窗口中已经不再有 5 个变量,1 个即已足够。

 

字符串缓冲区

我们还要重温三种标准函数:StringInit()StringFill()StringBufferLen()

StringInit() 函数将为一个字符串填入与指定数字完全相同的字符数:

string str;
StringInit(str,10,'|');
Alert(str); 

执行此代码后,str 变量会存储包含 "||||||||||" 的字符串。字符利用其 ASCII 代码指定,即需要放入单引号中的字符。

StringFill() 函数将为一个字符串填入完全相同的字符、且不更改字符串大小。继续前例:

StringFill(str,'/');
Alert(str); 

此后,str 变量将存储包含 "//////////" 的字符串。

现在,我们尝试为字符串填入一个带代码 0 的字符(字符串尾部):

StringFill(str,0); 
检查字符串大小:
int Length=StringLen(str);
Alert(IntegerToString(Length)); 

大小为 0,且带有代码 0 的字符位于字符串的起始处。检查缓冲区大小:

int BLength=StringBufferLen(str);
Alert(IntegerToString(BLength)); 

缓冲区大小非 0,且超过初始字符串的大小。分配给该字符串的内存绰绰有余。现在,向缓冲区大小范围内的字符串分配一个值时,将不再需要内存重新分配,且赋值亦将非常快速。要确保缓冲区大小方面不会减少,应将新值添加到字符串,而不是分配给它:

str+="a"; 

现在字符串长度为 1,而缓冲区大小则还是一样。如此一来,您就可以在一定程度上加快字符串的处理速度。在字符串的开头插入带代码 0 的字符,即可清除字符串:

StringSetCharacter(str,0,0); 

 

总结

有人可能会觉得本文的主题无关紧要,同 MQL5 语言的主用途(即开发 EA 交易和指标)仅有些微相关,。然而,我们却在这里花大篇幅,介绍了此语言所提供的处理字符串的丰富功能。在许多情况下,编制 EA 交易和指标程序时,您可能不必再处理字符串,但是说不定哪天也会用到它。读过本文后,您已做好了有需要时使用字符串的准备,而无需浪费时间学习这些函数了。所需操作,您都能驾轻就熟。

我们再简要重述一下文中提供的信息,并根据其用途、重要性和使用频率,将各个函数分类。

  1. StringLen()、StringFind()、StringSubstr()、StringReplace() 和 StringSplit() 为关键的必需函数,用于确定字符串长度、子字符串搜索、子字符串的获取和替换,以及字符串的分割。
     
  2. StringTrimLeft()、StringTrinRight()、StringToLower() 和 StringToUpper() 是非常有用的辅助函数,用于删除两端的空格和更改大小写。
     
  3. ColorToString()、DoubleToString()、EnumToString()、IntegerToString()、TimeToString() 和 StringFormat() 函数用于将数值变量转换为一个字符串。
     
  4. StringToColor()、StringToDouble()、StringToInteger()、StringToTime() 和 StringCompare() 函数用于将字符串转换为一个数值变量。
     
  5. 您还可以利用 StringAdd() 和 StringConcatenate() 函数,以一种节省空间的方式添加和组合字符串。
     
  6. ShortToString()、ShortArrayToString()、StringToShortArray() 以及 CharToString()、CharArrayToString() 和 StringToCharArray() 函数可将字符串作为数组处理,在处理要求非常复杂的字符串操作任务时有用。通过上述列表,我们可指出两个尤其重要的函数:

    • CharToString() 用于处理搭配 Wingdings 字体的图形对象,
    • CharArrayToString() 用于调用 API 函数时准备一个字符串参数。

  7. StringSetCharacter()、StringGetCharacter()、StringInit()、StringFill() 和 StringBufferLen() 则为次要函数。

 

随附文件

  1. IncStrFunctions.mqh 中包含 Trim()、StringFindRev()、TrimL()、TrimR()、ParamsToArray() 及 NotifyOnOff() 函数。
  2. eMultiLanguageMessage.mq5 是一个包含不同语言消息的 EA 交易示例。