English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Основы программирования на MQL5 - Строки

Основы программирования на MQL5 - Строки

MetaTrader 5Примеры | 30 ноября 2012, 12:01
15 643 16
Dmitry Fedoseev
Dmitry Fedoseev

Введение

Строки, точнее строковые переменные, предназначены для хранения в них символьных данных, попросту говоря, текста:

string str="Любой текст";

Язык MQL5 имеет очень широкий и удобный функционал для работы со строками. Чаще всего при программировании советников и индикаторов строки используются для формирования информационных сообщений. В индикаторах это могут быть сообщения о выполнении каких-либо условий (например, торговых сигналов), в экспертах - сообщения о результатах выполнения торговых действий. При запуске советника, скрипта или индикатора может выполняться проверка установленных пользователем параметров и уведомление пользователя в случае установки неподходящих параметров. Помимо уведомления, может возникать и подсказка, с рекомендацией по установке параметров. В общем, при программировании на MQL5, использование строк в первую очередь обеспечивает удобство для пользователя.

Кроме удобства, работа со строками неотъемлема при работе с файлами. Запись и чтение данных из файлов выполняется строковыми переменными. Конечно, существует и другой способ работы с файлами - бинарный, который обеспечивает чтение и запись числовых переменных и массивов. Однако, если объемы данных незначительны, лучше использовать текстовые файлы и строки. В этом случае функционирование программы становится более прозрачным для пользователя, а так же упрощается процесс разработки программы, поскольку обеспечивается непосредственный контроль данных. В текстовых файлах данные выглядят точно так же, как и внутри программы.

Использование строк может значительно расширить возможности программы по вводу данных (параметров) в случаях, когда требуемое количество параметров заранее неизвестно (например, размеры лотов для усредняющих доливок). В этом случае значения можно записать в одну строку через какой-нибудь разделитель, например, через точку с запятой:

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

Затем, при инициализации эксперта, строка разбивается и заполняется массив числовых значений. К сожалению, такие строковые параметры невозможно перебирать при оптимизации - то есть задавать начальное и конечное значение и шаг перебора. Возможно, в некоторых случаях, будет предпочтительнее использовать в окне свойств большое количество числовых переменных. Но их количество практически неограниченно и возникает вопрос удобства и цели (требуется ли возможность оптимизации).

Могут быть и другие случаи, когда оптимизация параметра гарантированно не потребуется, например включение уведомлений. Язык MQL5 поддерживает разнообразные способы уведомлений пользователя: звук, окно, отправка e-mail, push-уведомления. Можно для каждого из этих уведомлений сделать в окне свойств свой выключатель типа bool (потребуется как минимум, четыре переменных), а можно сократить количество переменных до одной строковой.

Если надо включить звук, вводится буква "s" (sound), если еще надо включить e-mail, дополнительно вводится буква "e". Таким образом, можно включить любую комбинацию уведомлений, используя всего одну переменную. Для советника количество внешних параметров не является существенным, здесь только стоит вопрос удобства для пользователя.

При разработке индикаторов же следует стремиться к сокращению количества внешних параметров. Очень вероятно, что индикатор будет вызываться из эксперта или другого индикатора при помощи функций iCustom() или IndicatorCreate(), а количество параметров у этих функций ограничено (у iCustom() - всего 64 параметра, у IndicatorCreate() - размер массива параметров 64 элемента). Поэтому использование строк может сослужить очень полезную службу.

В этой статье мы рассмотрим все стандартные функции языка для работы со строками и параллельно создадим несколько своих полезных функций.

 

Объявление строковой переменной

Как и все другие типы переменных, строковые переменные могут не только объявляться:

string str;
Но могут объявляться сразу же с присвоением значения (инициализироваться значением):
string str="Любой текст";

Ограничения по длине строки отсутствуют. Длинные строки, для удобства, могут быть записаны в нескольких строчках:

string str= "Длинная строка может "
            "быть записана в нескольких "
            "строчках";
//--- вывод результата
Alert(str);

При такой инициализации в переменной str будет находиться строка "Длинная строка может быть записана в нескольких строчках".

Здесь, немного забегая вперед, следует отметить, что значение строковой переменной объявленной без параметров не идентично пустой строке:

string str="";

В этом можно убедиться:

string str1;
string str2="";
//--- вывод результата сравнения
Alert(str1==str2);

При выполнении этого кода откроется окно с сообщением "false". Неинициализированная строковая переменная имеет значение NULL, и оно отличается от пустой строки "". Об этом следует помнить! При работе со строками очень часто приходится выполнять проверку, не является ли строка пустой. Поэтому следует или придерживаться правила инициализации всех строк пустой строкой "", или же выполнять две проверки: на не равенство "" и на не равенство NULL:

if(str!="" && str!=NULL)
  {
   //--- что-то делаем со строкой
  }

Первый способ предпочтительней, поскольку он упрощает условие проверки. 

Аналогичную проверку можно сделать, проверяя размер переменной. Для определения размера используется функция StringLen():

if(StringLen(str)!=0)
  { 
   //--- что-то делаем со строкой
  }


Сложение строк

Основное действие, которое чаще всего приходится выполнять со строками, это их сложение, то есть составление фраз из слов. Сложение выполняется знаком "+":

string str1,str2,str3,str4,str5;
//--- присвоение значений
str1="Программирование";
str2="на MQL5";
str3="для MetaTrader 5";
//--- сложение строк
str4=str1+" "+str2;
str5=str1+" "+str3;
//--- вывод результатов
Alert(str4);
Alert(str5);

После выполнения этого кода в переменной str4 будет фраза "Программирование на MQL5", а в переменной str5 фраза "Программирование для MetaTrader 5". В этом примере выполнялось сложение двух строк и присвоение полученной строки другой переменной.

Очень часто выполняется добавление дополнительной строки к основной строке: 

string str1,str2,str3;
//--- присвоение значений
str1="Программирование";
str2="на MQL5";
str3="для MetaTrader 5";
//--- сложение строк с добавлением к основной строке
str1=str1+" "+str2;
str1=str1+" "+str3;
//--- вывод результата
Alert(str1);

После выполнения этого кода в строке str1 будет фраза "Программирование на MQL5 для MetaTrader 5". В этом примере выполнялось добавление к основной строке str1 и присвоение полученного результата ей же. Такие же действия можно записать немного проще:

str1+=str2;
str1+=str3;
Или так:
str1+=str2+str3;

Знак "+" слева от знака "=" означает, что к переменной str1 выполняется добавление выражения, записанного правее знака "=".

Так же можно выполнить добавление строки в начало основной строки, это выполняется подобно предпоследнему примеру: к добавляемой строке прибавляется основная строка и полученная строка присваивается основной переменной:

string str1,str2,str3;
//--- присвоение значений
str1="Программирование";
str2="на MQL5";
str3="для MetaTrader 5";
//--- сложение строк с добавлением строки в начало
str3=str2+" "+str3;
str3=str1+" "+str3;
//--- вывод результата
Alert(str3);

Только теперь фраза "Программирование на MQL5 для MetaTrader 5" будет в переменной 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 без указания разделителя, результат будет одинаковый, как при использовании знака "+" так и при использовании знака ",":

//--- запись в первый файл
int h=FileOpen("1.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1","2","3");
FileClose(h);
//--- запись во второй файл
h=FileOpen("2.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1"+"2"+"3");
FileClose(h);
//--- запись в третий файл
h=FileOpen("3.txt",FILE_WRITE|FILE_ANSI|FILE_TXT);
FileWrite(h,"1","2","3");
FileClose(h);
//--- запись в четвертый файл
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(). По описанию этих функций в справочном руководстве, они позволяют производить соединение строк более экономично в плане занимаемой оперативной памяти и более быстро. Функция StringAdd() позволяет к одной строке производить добавление другой:

string str1,str2,str3;
//--- присвоение значений
str1="Программирование";
str2="на MQL5";
str3="для MetaTrader 5";
//--- вызов функции сложения строк
StringAdd(str1," ");
StringAdd(str1,str2);
StringAdd(str1," ");
StringAdd(str1,str3);
//--- вывод результата
Alert(str1);

После выполнения этого кода в переменной str1 будет строка "Программирование на MQL5 для MetaTrader 5".

Функция StringConcatenate() позволяет соединять сразу несколько строк. Первым параметром в функцию передается переменная той строки, к которой будут присоединены перечисленные далее строки, всего в функцию может передаваться 64 параметра:

string str1,str2,str3;
//--- присвоение значений
str1="Программирование";
str2="на MQL5";

str3="для MetaTrader 5";
//--- вызов одной функции для сложения нескольких строк
StringConcatenate(str1,str1," ",str2," ",str3);
//--- вывод результата
Alert(str1);

После выполнения этого кода в переменной str1 так же будет строка "Программирование на MQL5 для MetaTrader 5".

 

Преобразование различных переменных в строку

При формировании строки сообщения, очень часто возникает необходимость добавлять в нее значения числовых переменных. Для преобразования значений целочисленных переменных (char, uchar, bool, short, ushort, int, uint, color, long, ulong, datetime) в строку используется функция IntegerToString():

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

Если преобразовывать переменную типа bool, будет возвращаться строка "0" (false) или "1" (true), при преобразовании переменных типа color или datetime - строка с числовым выражением цвета или даты (например, "65535" для желтого цвета clrYellow или "1325376000" для даты 2012.01.01 00:00).

Для преобразования переменных вещественного типа (double, float) в строку используется функция 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(); // Текущее время 
string str1=IntegerToString(tm);
string str2=TimeToString(tm);

После выполнения этого кода в переменной str1 будет находиться строка с числовым выражением времени (количество секунд от 1-го января 1970-го года), а в переменной str2 - отформатированное время, например "2012.11.02 22:00" (год, месяц, число, часы, минуты).

При вызове функции TimeToString() имеется возможность указать формат отображения даты и времени. Возможны следующие варианты:

string str1="Дата и время с минутами: "+TimeToString(tm);
string str2="Только дата: "+TimeToString(tm,TIME_DATE);
string str3="Только время с минутами: "+TimeToString(tm,TIME_MINUTES);
string str4="Только время с секундами: "+TimeToString(tm,TIME_SECONDS);
string str5="Дата и время с секундами: "+TimeToString(tm,TIME_DATE|TIME_SECONDS);

В MQL5 имеется очень удобная функциональная возможность - создание перечислений, которые в окне свойств программы отображаются в виде выпадающих списков с набором вариантов. Значения таких переменных тоже можно преобразовывать в строку, для этого используется функция EnumToString(). Ниже расположен код скрипта с демонстрацией работы этой функции:

//+------------------------------------------------------------------+
//| Создание перечисления                                            |
//+------------------------------------------------------------------+
enum EMode
  {
   OFF=0,
   Mode1 = 1,
   Mode2 = 2,
   Mode3 = 3 
  };
//+------------------------------------------------------------------+
//| Запуск скрипта                                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
   EMode Value=1;
   //--- соединение строк
   string str="Значению "+IntegerToString(Value)+" соответствует пункт "+EnumToString(Value)+" перечисления EMode";
   //--- вывод результата
   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". Если цвет не является стандартным (не входит в набор веб-цветов и соответственно не имеет имени), то функция ColorToString() возвращает строку со значениями компонентов, независимо от значения второго параметра.

Существует еще один способ преобразования переменных, через приведение типов:

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

При преобразовании таким способом переменной типа bool, строка будет иметь значение " true" или "false": 

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

Преобразование переменных типа double и float выполняется с максимальной точностью, происходит только отбрасывание нулей в дробной части: 

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

После выполнения этого кода в переменой str1 сохранится строка "0.1", а в переменной str2 строка "0.123".

 

Вывод специальных символов

При инициализации строковой переменной значением, присваиваемая строка помещается в двойные кавычки, чтобы компилятор мог отличить строку от программного кода. Для того, что бы поместить кавычки внутрь строки, нужно указать, что в данном случае знак используется не по его прямому назначению (как знак отделяющий строку от кода), а как часть строки. Для этого, перед знаком кавычек необходимо поставить обратную черту "\":

string str1="Просто текст";
string str2="\"Текст в кавычках\"";
//--- вывод результатов
Alert(str1);
Alert(str2);

Поскольку обратная черта тоже является специальным символом, то для ее вывода в строку также необходимо поставить перед ней еще одну обратную черту:

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

После выполнения этого кода в строке будет один символ "\".

Еще строка может содержать знак горизонтальной табуляции, который указывается знаками "\t":

string str="Колонка-1\tКолонка-2\tКолонка-3";
Alert(str);

В этом случае в переменной str сохранится строка "Колонка-1        Колонка-2        Колонка-3".

При помощи знаков "\n" текст может выводиться с переносами, в несколько строк:

string str="Строка-1\nСтрока-2\nСтрока-3";
Alert(str);

В данном случае результатом выполнения функции Alert() будут три строки текста.

При выводе с использованием функций Alert() и MessageBox(), а так же при записи в файл действуют оба символа: "\t" и "\n". Однако, при выводе в комментарий графика (функция Comment()) действует только знак перехода на новую строку "\n", знак табуляции "\t" игнорируется. При выводе с использованием функции Print() знак "\n" так же действует (каждая часть строки выводится в отдельную строку журнала), а знак "\t" заменяется на пробел. В лог файле, в котором сохраняются все сообщения, выводимые функций Print(), знак "\t" так же заменяется на пробел.

 

Форматирование строк по шаблону

При формировании строки для вывода, может потребоваться включить в нее значения нескольких числовых переменных. Эту задачу можно решить сложением строк и преобразованием числовых переменных в строки, но в этом случае строка кода, формирующая сообщение, будет очень длинной и неудобной для понимания и редактирования, при выполнении доработок программы:

//--- инициализация переменных
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- длинное соединение строк
string str="Variable1 = "+IntegerToString(Variable1)+", Variable2 = "+IntegerToString(Variable2)+", Variable3 = "+IntegerToString(Variable2);
//--- вывод результата
Alert(str);

Эту же задачу можно решить значительно проще, при помощи функции StringFormat(). Первым параметров в эту функцию передается шаблон сообщения, в котором указаны места вставки переменных и определен формат их вывода. Далее перечисляются все переменные в том порядке, в каком они встречаются в шаблоне: 

//--- инициализация переменных
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- более простое соединение строк
string str=StringFormat("Variable1 = %i, Variable2 = %i, Variable3 = %i",Variable1,Variable2,Variable3);
//--- вывод результата
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);
//--- вывод результатов
Alert("LongMin = "+LongMin);
Alert("LongMax = "+LongMax);
Alert("ULongMax = "+ULongMax);
Alert("DateTimeMax = "+DateTimeMax);
В результате работы этого кода откроется окно сообщения со значениями переменных.

Формат вещественных чисел обозначается символом "f":
double Percents=5.5;
//--- вещественное число в виде строки
string str=StringFormat("Percents = %f",Percents);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Percents = 5.500000", по умолчанию выводится шесть знаков после запятой. Можно указать требуемое количество знаков:

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

Для этого ставится точка, означающая десятичный разделитель, и число знаков после него, в данном примере 2 знака. В этом случае в переменной str сохранится строка "Percents = 5.50". Такой вариант форматирования в точности заменяет функцию DoubleToString().

Можно указать общую длину числа, для этого сразу же после знака "%" ставится "0" и число определяющие длину числа, затем указывается количество знаков после запятой (если нужно):

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

Указана общая длина в 6 знаков, из которых один знак будет занят десятичным разделителем и еще два - знаками после запятой. Поэтому, в переменной str сохранится строка "Percents = 005.50".

Если в сообщении надо выводить знак процента "%", а он используется для указания мест вставки значений, необходимо вписать его два раза подряд "%%":

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

В этом случае в переменной str будет стока "Percents = 005.50%".

Так же можно определить длину числа и при выводе целочисленных переменных:

int Variable=123;
//--- целое число в виде строки с заданной длиной вывода
string str=StringFormat("Variable = %05i",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Variable = 00123".

Если указано меньшее количество знаков, чем составляет число, то оно все равно будет выведено правильно:

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

В этом случае в переменной str будет строка "Variable = 123", то есть выведено число из 3-ех знаков, несмотря на то, что указана длина 2.

Вещественные числа можно выводить в научном формате (мантисса с шестью знаками после десятичного разделителя и степень), для этого используется символ "e":

double Variable=123.456;
//--- вещественное число в виде строки в научной форме
string str=StringFormat("Variable = %e",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "1.234560e+002". Можно использовать заглавную "E" - ее действие аналогично строчной "e", только в отформатированной стоке вместо строчной "e" будет заглавная "E".

Существует еще один вариант форматирования вещественных чисел - "g", при этом выводится всего шесть знаков (без учета десятичного разделителя). Если длина целой части числа превышает шесть знаков, число выводится в научной форме:

double Variable1=12.3456789;
double Variable2=1234567.89;
//--- получение вещественных чисел в виде строк с использованием "g"
string str1=StringFormat("Variable = %g",Variable1);
string str2=StringFormat("Variable = %g",Variable2);
//--- вывод результата
Alert(str1+" "+str2);

В этом примере в переменной str1 сохранится строка " 12.3457", а в переменной str2 строка "1.23457e+006". При использовании заглавной "G" все будет работать точно так же, только вместо строчной "e" выводиться заглавная "E".

Функция StringFormat() может конвертировать форматы представления чисел - переводить числа из десятичной системы исчисления в восьмеричную или шестнадцатеричную. Для перевода в восьмеричную систему используется символ "o":

int Variable=17;
//--- вещественное число в виде строки в восьмеричной системе исчисления
string str=StringFormat("Variable = %o",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Variable = 21" (8*2+1=17).

Для перевода числа в шестнадцатеричную систему исчисления используется символ "x" или "X", при использовании строчной "x", шестнадцатеричное число будет состоять из строчных букв, при использовании заглавной "X" - из заглавных:

color Variable=clrBlue;
//--- вещественное число в виде строки в шестнадцатеричной системе исчисления
string str=StringFormat("Variable = %x",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Variable = ff0000".

Возможен и обратный перевод шестнадцатеричного числа в десятичное, для этого используется символ "d":

int Variable=0x0000ff;
//--- вещественное число в виде строки в десятичной системе исчисления
string str=StringFormat("Variable = %d",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Variable = 255".

Для вывода строковых переменных используется символ "s":

string Variable="текст";
//--- вывод строковой переменной
string str=StringFormat("Variable = %s",Variable);
//--- вывод результата
Alert(str);

После выполнения этого кода в переменной str сохранится строка "Variable = текст".

Иногда возникает необходимость выравнивать числа при выводе их в столбик, так как отрицательные числа сдвигаются из-за знака "-". Для смещения положительного числа, надо добавить пробел к началу строки, для этого, сразу же после знака "%" вводится пробел. В этом случае отрицательные числа будут выводиться без пробела, а положительные с пробелом в начале:

int Variable1=1;
int Variable2=-1;
//--- представление чисел в виде строк с выравниванием
string str1=StringFormat("Variable1=% 03i",Variable1);
string str2=StringFormat("Variable2=% 03i",Variable2);
//--- вывод результатов
Alert(str1);
Alert(str2);

После выполнения этого кода в переменной str1 сохранится строка "Variable1= 01" (с пробелом), а в переменной str2 строка "Variable2=-01".

Существует еще пара функций, подобных функции StringFormat(). Это функции PrintFormat() и printf(), которые абсолютно идентичны по своему действию, а от функции StringFormat() отличаются только тем, что выводят текст в журнал так же, как и функция Print().

Вообще у функции StringFormat() есть больше функциональных возможностей, здесь же изложен достаточный минимум для решения подавляющего большинства задач форматирования чисел для вывода.
 

Сообщения на разных языках

Функция StringFormat() позволяет наделить программу очень полезной возможностью - обеспечить вывод сообщений на разных языках, в зависимости от установленного в терминале языка интерфейса.

Какой выбран язык для интерфейса можно узнать, вызвав функцию TerminalInfoString() с идентификатором TERMINAL_LANGUAGE. При запуске программы готовим форматирующую строку в зависимости от языка интерфейса, и затем используем ее в программе. Ниже показан шаблон эксперта с такой возможностью:

//--- переменная для форматирующей строки
string FormatString;
//+------------------------------------------------------------------+
//| Обработка события Init                                           |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- получение форматирующей строки
   FormatString=GetFormatString();
//--- дополнительный вызов, на случай, чтобы в выходные эксперт отработал хотя бы один раз
   OnTick();
   return(0);
  }
//+------------------------------------------------------------------+
//| Обработка события Tick                                           |
//+------------------------------------------------------------------+
void OnTick()
  {
   int Variable1,Variable2,Variable3;
   Variable1=MathRand()%10;        // Случайное число от 0 до 10
   Variable2=MathRand()%10;        // Еще одно случайное число
   Variable3=Variable1+Variable2; // Сумма чисел
//--- вывод результата
   Alert(StringFormat(FormatString,Variable1,Variable2,Variable3));
  }
//+------------------------------------------------------------------+
//| Определение форматирующей строки                                 |
//+------------------------------------------------------------------+
string GetFormatString(void)
  {
   string Language=TerminalInfoString(TERMINAL_LANGUAGE);
//--- проверка языков
   if(Language=="Russian") return("%i плюс %i равно %i");     // русский
   if(Language=="Spanish") return("%i más %i es igual a %i"); // испанский
//--- Для всех остальных случаев - английский
   return("%i plus %i equals %i");
  }

Эксперт суммирует два случайных числа и выводит текстовое сообщение о своих действиях, например, "1 плюс 2 равно 3".

На этом мы закончили рассматривать все, что касается вывода строк. Теперь займемся немного более сложными манипуляциями со строками, но и более интересными.

 

Основные функции работы со строками

Если строка вводится через окно свойств программы или читается из файла, то в ней могут присутствовать лишние пробелы, они могут появиться или от случайной неаккуратности пользователя, или, может быть, ему было так удобней. Прежде чем выполнять над строкой какие-нибудь действия, желательно удалить пробелы с краев. Для этого в языке MQL5 существуют функции StringTrimLeft() (удаляет пробелы слева) и StringTrimRight() (удаляет пробелы справа). Эти функции удаляют не только пробелы, но и символы табуляции и новой строки. При работе со строками приходится часто выполнять удаление пробелов сразу с обеих сторон, поэтому будет полезно написать функцию, выполняющую это:

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

При вводе вещественных чисел, пользователь может вместо точки ввести запятую. Поэтому, при вводе вещественных чисел желательно обеспечивать возможность использования в качестве десятичного разделителя как точки, так и запятой. Для замены одной подстроки на другую используется функция StringReplace():

string str="123,456";
//--- замена запятой на точку
StringReplace(str,",",".");
double Value=StringToDouble(str);
//--- вывод результата
Alert(DoubleToString(Value));

Если замену "," на "." не выполнять, то при преобразовании строки в число, дробная часть числа будет отброшена.

В некоторых случаях может потребоваться заменить несколько подряд идущих пробелов на один пробел. Для этого сначала нужно заменить символы табуляции на пробел, а затем выполнить замену двух пробелов на один, пока не останется только по одному пробелу:

string str="Колонка-1 \t Колонка-2 \t Колонка-3";
//--- замена табуляции на пробел
StringReplace(str,"\t"," ");
//--- получение одного пробела вместо нескольких, идущих подряд
while(StringReplace(str,"  "," ")>0){}
//--- вывод результата
Alert(str);

Функция StringReplace() возвращает количество выполненных замен или -1 при ошибке. Поэтому, пока функция возвращает значение больше нуля, цикл продолжает выполняться, пока все пары пробелов не будут заменены и не останется только по одному пробелу в каждом месте. В теле цикла нет никакого кода, функция StringReplace() вызывается при проверке условия цикла на каждом повторении.

Функция StringReplace() позволят заменять подстроки различной длины:

string str="Программирование на MQL5!";
//--- замена подстроки, вывод результата
StringReplace(str,"на MQL5","для MetaTrader 5");
Alert(str);
//--- обратная замена, вывод результата
StringReplace(str,"для MetaTrader 5","на MQL5");
Alert(str);

При выполнении этого кода, после первой замены в переменной str будет строка "Программирование для MetaTrader 5", а после второй замены снова "Программирование на MQL5!".

Функция StringFind() используется для поиска подстроки и возвращает индекс первого вхождения подстроки в строку. Первым параметром в функцию передается строка, в которой выполняется поиск, второй параметр определяет искомую подстроку, а третий (не обязательный) может определять позицию, с которой начинается поиск. Если третий параметр не указан, то функция работает так, как будто его значение равно 0, то есть поиск выполняется с самого начала строки. Найдем позицию подстроки "5" в строке "Программирование для MQL5 для MetaTrader 5":

string str="Программирование на MQL5 для MetaTrader 5";
//--- получение позиции символа
int Pos=StringFind(str,"5");
//--- вывод результата
Alert(IntegerToString(Pos));

После выполнения этого кода в переменной Pos сохранится значение 23. Всего подстрока "5" встречается два раза, но функция вернула только позицию первого вхождения. Если отсчитать позицию просто глядя на строку, получается 24. Дело в том, что функция выполняет отсчет начиная с нуля, а не с единицы. Если искомая подстрока отсутствует в строке, функция возвращает -1.

Иногда может потребоваться найти позицию последнего вхождения подстроки. Для этих целей потребуется написать собственную функцию StringFindRev(). Произведем поиск первого вхождения подстроки, затем смещаем начало поиска в соответствии с найденной позицией и т.д. в цикле:

int StringFindRev(string Str,string Find)
  {
//--- переменная pos для возвращаемого значения
   int pos;
//--- спомогательная переменная, инициализируется значением -1,
//--- на случай, если в строке не будет найдена подстрока
   int tmp=-1;
//--- цикл. Будет выполнен как минимум один раз
   do
     {
      //--- присваиваем последнюю известную позицию подстроки
      pos=tmp;
      //--- ищем дальше (используется третий параметр функции)
      tmp=StringFind(Str,Find,tmp+1);
     }
   while(tmp!=-1); // Если в оставшейся части строки нет подстроки, цикл 
                   // завершается, а в переменной pos остается последняя
                   // известная позиция
//--- возврат позиции
   return(pos);
  }
Попробуем воспользоваться этой функцией:
string str="Программирование на MQL5 для MetaTrader 5";
//--- вызов функции поиска позиции последнего включения сивола в строке
int pos=StringFindRev(str,"5");
//--- вывод результата
Alert(pos);

После выполнения этого кода в переменной Pos сохранится значение 40.

Функция StringSubstr() применяется для извлечения подстроки с заданной позиции и заданной длины. Извлечем подстроку с позиции 23 длиной 1:

string str="Программирование на MQL5 для MetaTrader 5";
//--- получение подстроки с указанной позиции и длиной
string str2=StringSubstr(str,23,1);
//--- вывод результата
Alert(str2);

Получена цифра "5".

Теперь, разобравшись с основными функциями, используем их для написания полезной функции для удаления из строки заданного списка символов. В функцию передается исходная строка и строка, представляющая собой список символов, которые необходимо удалить из исходной.

string TrimL(string Str,string List="\t\n ;")
  {
//--- переменная для одной буквы строки Str
   string ch;
   int Len=StringLen(Str);
   int i=0;
//--- цикл по всем буквам строки Str
   for(;i<Len;i++)
     {
      //--- очередная буква строки Str
      ch=StringSubstr(Str,i,1);
      //--- если этой буквы нет в списке List, значит строка должна начинаться с этой позиции 
      if(StringFind(List,ch,0)==-1)
        {
         break; // завершаем работу цикла
        }
     }
//--- извлекаем подстроку и возвращаем ее
   return(StringSubstr(Str,i));
  }

По умолчанию функция удаляет знак табуляции, новой строки, пробел и точку с запятой ";".

Такая же функция для удаления с правой стороны:

string TrimR(string Str,string List="\t\n ;")
  {
//--- переменная для одной буквы строки Str
   string ch;
   int Len=StringLen(Str);
//--- буквы в строке пронумерованы от 0, поэтому последняя буква имеет индекс на 1 меньший чем длина строки
   int i=Len-1;
//--- цикл по всем буквам строки Str
   for(;i>=0;i--)
     {
      //--- очередная буква строки Str
      ch=StringSubstr(Str,i,1);
      //--- если этой буквы нет в списке List, значит строка должна начинаться с этой позиции 
      if(StringFind(List,ch,0)==-1)
        {
         break; // завершаем работу цикла
        }
     }
//--- извлекаем подстроку и возвращаем ее
   return(StringSubstr(Str,0,i+1));
  }

Эта функция также по умолчанию удаляет знак табуляции, новой строки, пробел и точку с запятой ";". Эта функция может оказать полезной при чтении из CSV файлов. Внутри таких файлов, с правой стороны строки, может находиться большое количество разделителей полей, чаще всего это точа с запятой ";".

Заглавная и строчная буква, например "А" и "а" для человека не являются различными по своему смыслу, однако, для компьютера это два совершенно разных символа. Если при запросе рыночных данных, используя функцию SymbolInfoDouble(), вместо, например, "EURUSD" указать символ "eurusd", то функция не вернет нужного нам значения. При вводе символа через окно свойств очень вероятна такая ошибка. Для изменения регистра в языке MQL5 существуют функции StringToLower() (перевод к нижнему регистру) и StringToUpper() (перевод к верхнему регистру):

string str="EuRuSd";
string str1=str;
string str2=str;
//--- изменение регистра строк
StringToUpper(str1);
StringToLower(str2);
//--- вывод результата
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 кодами (сокращение от American Standard Code for Information Interchange, Американский стандарт информационного обмена). Далее мы будем использовать название ASCII, подразумевая ее расширенную версию "Extended ASCII", которая содержит 256 кодов. Таким образом можно сказать, что компьютерная "азбука" состоит из 256 знаков. Как у разных народов и языков существуют свои азбуки, так и для компьютера существуют различные наборы соответствий кодов и знаков - кодовые страницы. Российские пользователи компьютера в основном имеют дело с кодовой страницей Windows-1251, в которую входят буквы латинского алфавита, русского алфавита, конечно же цифры, знаки препинания и некоторые другие символы. На рис. 1 приведена кодовая страница Windows-1251:


Рис. 1. Кодовая страница Windows-1251.

Символы до кода 32 не показаны - это управляющие символы. Сами по себе они не отображаются, но влияют на отображение других символов, например табуляция (код 9), перевод строки (код 10) и др.

В странах Центральной Европы используется кодировка Windows-1250 (рис. 2):


Рис. 2. Кодовая страница Windows-1250.

Обратите внимание, у кодовой страницы 1251 начиная с кода 192 располагаются буквы русского алфавита, а у кодовой страницы 1250 так называемые диакритические знаки (буквы с дополнительной меткой, определяющей небольшие различия в произношении звука), используемые в европейских языках.

256 символов это очень незначительное количество. Если необходимо писать текст на нескольких языках, например на русском и французском (имеет большое количество диакритических символов) или английском и арабском (буквы очень не похожи на буквы других языков), то возникают сложности. Существует еще и иероглифическое письмо, как в Китае, Японии, включающее в себя тысячи иероглифов, в этом случае сложности еще более очевидны. Возникает необходимость кодировать символы, не входящие в свою кодовую страницу, каким-нибудь другим способом. Кто знаком с языком HTML, должен знать о возможности вставки в html-страницы нестандартных символов, например код &Agrave; отображает символ À, &Aacute; отображает символ Á и т.д.

С некоторых пор получила широкое распространение кодировка символов Unicode, в которой один знак кодируется не одним байтом (число от 0 до 255), а двумя, и всего получается 65536 символов. В этот набор символов входят все буквы всех алфавитом мира и даже наиболее часто употребляемые иероглифы, рис. 3 (но все равно, на компьютере должны быть установлены соответствующие шрифты):

 

Рис. 3. Буквы различных алфавитов и иероглифы.

Строки в MQL5 закодированы посредством Unicode. То есть один символ в строке может быть представлен числом от 0 до 65535. Символы с кодами от 0 до 127 в кодировках ASCII и Unicode совпадают. Текстовые файлы могут содержать текст в кодировках ASCII или Unicode, соответственно в языке MQL5 имеется различный функционал для работы со строками ASCII и строками Unicode.

 

Конвертирование строки в массив и обратно

При работы со строками, для решения наибольшего количества практических задач, бывает достаточно функций StringLen(), StringFind(), StringSubst(), StringReplace(). Но могут быть задачи, которые намного проще решить работая со строками как с числами, например шифрование, сжатие данных, подсчет контрольных чисел. Хотя такие задачи не являются повседневно актуальными, не исключено, что когда-нибудь возникнет необходимость их решения. Существуют и более актуальные задачи для которых потребуется конвертирование строки в массив - это передача строковых параметров в функции Windows API (Application Programming Interfaces).

Для преобразования строки в массив кодов Unicode используется функция StringToShortArray(), а для преобразования в массив ASCII используется функция StringToCharArray():

string str="MetaTrader 5";
//--- конвертирование строки в массив кодов Unicode
short sha[];
StringToShortArray(str,sha);
//--- конвертирование строки в массив кодов ASCII
uchar cha[];
StringToCharArray(str,cha);
//--- флаг разницы
bool Dif=false;
//--- поэлементное сравнение массивов
for(int i=0;i<StringLen(str);i++)
  {
   if(sha[i]!=cha[i])
     {
      Dif=true;
     }
  }
//--- вывод результата
if(Dif) Alert("Есть различия");
else    Alert("Идентичны");

В результате выполнения этого примера, если массивы полученные функциями StringToShortArray() и StringToCharArray() идентичны, то открывается окно с сообщением "Идентичны", а если имеются различия, то с сообщением "Есть различия". Для строки "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);
//--- вывод длин
Alert(StringFormat("%i, %i, %i",StrLen,shaLen,chaLen));

В массивах на один элемент больше, чем знаков в строке. Это связано с тем, что конец строки обозначается знаком с кодом 0, этот знак не отображается в строке, но для компьютера он означает окончание отображаемой строки. Обмен данными не всегда происходит по одному байту (букве) в количестве соответствующем длине строки, но в любом случае, за счет знака с кодом 0 можно определить конец строки. Этот ноль, так же, может создавать и проблемы, например, при шифровании или применении алгоритма сжатия какая-нибудь из букв может быть преобразована к знаку с кодом 0. В этом случае, при обратном преобразовании массива в строку, строка будет неполной. Для решения подобных задач требуется применение особых хитростей, но это выходит за пределы данной статьи.

Обратное преобразование массива в строку выполняется функциями ShortArrayToString() и CharArrayToString():

//--- преобразование массива кодов Unicode в строку
short sha[]={85,110,105,99,111,100,101};
string str1=ShortArrayToString(sha);
//--- преобразование массива кодов ASCII в строку
uchar cha[]={65,83,67,73,73};
string str2=CharArrayToString(cha);
//--- вывод результатов
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);           // создание графического объекта LABEL
   ObjectSetInteger(0,"lbl",OBJPROP_XDISTANCE,100);   // установка координаты X
   ObjectSetInteger(0,"lbl",OBJPROP_YDISTANCE,100);   // установка координаты Y
   ObjectSetInteger(0,"lbl",OBJPROP_FONTSIZE,20);     // установка размера
   ObjectSetString(0,"lbl",OBJPROP_FONT,"Wingdings"); // Установка символьного шрифта
   string Icon=CharToString(37);                   // 37 - колокольчик
   ObjectSetString(0,"lbl",OBJPROP_TEXT,Icon);       // установка отображаемого текста

В результате работы этого кода, на графике будет отображен значок с изображением колокольчика. Значок будет оставаться на своем месте при прокрутке графика.

Еще пара функций из категории работы с кодами символов - это StringGetCharacter() и StringSetCharacter(). Эти функции работают с кодами Unicode. Функция StringGetCharacter() позволят получить код символа, стоящего в строке на заданной позиции:

string str="L5";
//--- получение кода Unicode для символа с указанной позиции строки
ushort uch1=StringGetCharacter(str,0);
ushort uch2=StringGetCharacter(str,1);
//--- вывод результата
Alert(StringFormat("%i, %i",uch1,uch2));

В результате работы этого кода в переменной uch1 сохранится значение 76, в uch2  значение 53.

Функция StringSetCharacter() позволяет заменить код символа в заданной позиции, а так же добавить символ в конец строки:

string str="MQ5";
//--- замена символа в указанной позиции строки на символ Unicode, который соответствует переданному коду
StringSetCharacter(str,2,76);
Alert(str);
//--- добавление в конец строки символа Unicode, который соответствует переданному коду
StringSetCharacter(str,3,53);
Alert(str);

При выполнении этого кода, сначала в переменной str вместо строки "MQ5" сохранится строка "MQL", а затем "MQL5".

 

Вызов функций API

Некоторые функции API используют строковые параметры в качестве своих переменных. Например, функция для запуска сторонних приложений WinExec использует в качестве своего первого параметра массив типа uchar:

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

Попробуем запустить стандартную программу Windows notepad.exe (блокнот). Конвертируем путь к блокноту в массив uchar:

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

В результате работы этой функции должен открыться текстовый редактор Notepad.

Другой пример использования строковых параметров в функциях API - это функция MessageBoxW, которая выводит сообщение в окошке с использованием кодировки Unicode. По этой причине, в качестве ее параметров будем передавать массивы типа ushort:

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

Теперь используем эту функцию, для вывода окошка с сообщением:

ushort arr[];
ushort capt[];
//--- конвертирование
StringToShortArray("Программирование на MQL5 для MetaTrader 5.",arr);
StringToShortArray("Сообщение",capt);
//--- вывод сообщения
MessageBoxW(0,arr,capt,0);

В результате выполнения этого кода появится окошко с сообщением "Программирование на MQL5 для MetaTrader 5".

Стоит отметить, что в этом примере использование массивов типа ushort не обязательно, и можно передавать в качестве параметров функции сразу строки:

#import "user32.dll"
int MessageBoxW(int hWnd,string szText,string szCaption,int nType);
#import
//+------------------------------------------------------------------+
//| Функция запуска скрипта                                          |
//+------------------------------------------------------------------+
void OnStart()
  {
   MessageBoxW(0,"Программирование на MQL5 для MetaTrader 5","Сообщение",0);
  }

В итоге мы получим тот же результат, что и выше. Однако использовать массивы типа uchar в качестве параметров этой функции для вывода корректного сообщения не получится:

#import "user32.dll"
int MessageBoxW(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Функция запуска скрипта                                          |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- конвертирование
   StringToCharArray("Программирование на MQL5 для MetaTrader 5.",arr);
   StringToCharArray("Сообщение",capt);
//--- вывод сообщения
   MessageBoxW(0,arr,capt,0);
  }

Код отработает без ошибок и появится окошко, но сообщение в нем будет искажено.

Для случаев, когда все же необходимо вывести строку в кодировке ASCII (например, если уже имеется массив типа uchar), существует функция близнец - MessageBoxA. Для корректного отображения, функция должна принимать в качестве строковых параметров только массивы типа uchar. Импортируем эту функцию и вызовем ее для вывода сообщения:

#import "user32.dll"
int MessageBoxA(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Функция запуска скрипта                                          |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- конвертирование
   StringToCharArray("Программирование на MQL5 для MetaTrader 5",arr);
   StringToCharArray("Сообщение",capt);
//--- вывод сообщения
   MessageBoxA(0,arr,capt,0);
  }

И снова получаем сообщение "Программирование на MQL5 для MetaTrader 5".

Вообще, для многих функций WinAPI, работающих со строками, существует 2 варианта - для работы со строками ASCII и для работы со строками Unicode.

Для вызова функций в настройках терминала должно быть разрешено использование dll (Терминал - Главное меню - Сервис - Настройки - Советники - Разрешить импорт DLL) или при запуске скрипта, советника или индикатора в окне свойств, во вкладке "Зависимости" нужно установить флажок "Разрешить импорт DLL". Для того, что бы у скрипта открывалось окно свойств, необходимо указать соответствующее свойство скрипта:

#property script_show_inputs

 

Ввод неограниченного количества параметров

В окне свойств пользователь вводит список параметров разделяя из точкой с запятой:

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

Необходимо преобразовать такую строку в массив переменных double.

Для разделения строки в массив в языке MQL5 существует функция StringSplit(). Первым параметром в функцию передается строка, вторым указывается ASCII код знака, являющегося разделителем, третьим параметром передается массив, в котором будет находиться результат работы функции. Для определения ASCII кода существует очень простой способ - нужно поместить букву в одинарный кавычки:

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

В результате выполнения этого кода в переменной Code сохранится значение 65 - ASCII код буквы "A" латинского алфавита. 

Оформим решение этой задачи в виде отдельной функции, чтобы ее можно было легко использовать по мере необходимости. Первым параметром в функцию будем передавать строку, вторым параметром по ссылке будет возвращаться массив. Код функции подробно прокомментирован и вряд ли нуждается в дополнительных объяснениях:

int ParamsToArray(string Str,double &Params[])
  {
//--- удаление пробелов по краям
   StringTrimLeft(Str);
   StringTrimRight(Str);
//--- если строка пустая
   if(StringLen(Str)==0)
     {
      ArrayFree(Params); // освобождение массива
      return(0);         // заверение работы функции
     }
//--- вспомогательный массив
   string tmp[];
//--- разделение строки
   int size=StringSplit(Str,';',tmp);
//--- удаление пробелов по краям для каждого элемента массива
   for(int i=0;i<size;i++)
     {
      StringTrimLeft(tmp[i]);
      StringTrimRight(tmp[i]);
     }
//--- удаление пустых элементов массива (пользователь мог случайно 
//--- ввести разделитель два раза подряд или в конец строки)
   for(int i=size-1;i>=0;i--)
     {
      if(StringLen(tmp[i])==0)
        {
         ArrayCopy(tmp,tmp,i,i+1);
         size--; // размер массива уменьшился
        }
     }
//--- масштабирование массива в соответствии с новым размером
   ArrayResize(tmp,size);
//--- замена запятых на точки
   for(int i=0;i<size;i++)
     {
      StringReplace(tmp[i],",",".");
     }
//--- подготовка возвращаемого массива
   ArrayResize(Params,size);
//--- конвертация всех элеменотв в double и заполнение возвращаемого массива 
   for(int i=0;i<size;i++)
     {
      Params[i]=StringToDouble(tmp[i]);
     }
//--- функция возвращает количество параметров
   return(size);
  }

 

Преобразование строки в различные переменные

В функции ParamsToArray() использовалась стандартная функция StringToDouble() для преобразования строки в переменную double. Так же эта функция используется для преобразования в тип float. Существуют стандартные функции для преобразования и в другие типы переменных.

Функция StringToInteger() преобразует строку в целочисленную переменную:

string Str="12345.678";
//--- конвертирование строки в целочисленное число
long Val=StringToInteger(Str);
//--- обратное конвертирование и вывод результата
Alert(IntegerToString(Val));

В результате работы этого кода в переменой Val сохранится значение 12345. Дробная часть просто отбрасывается.

Функция StringToTime() преобразует строковую запись времени в ее числовое значение. Можно не указывать время - значение по умолчанию будет "00:00":

string Str1="2012.11.02 22:00";
string Str2="2012.01.01";
//--- преобразование строковой записи времени в тип datetime
datetime DateTime1=StringToTime(Str1);
datetime DateTime2=StringToTime(Str2);

Функция StringToColor() позволяет преобразовать название цвета (стандартный веб-цвет) в его числовое значение, или же преобразовать строку компонентов 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-mail - "e", push - "p". Кроме этого, в строку может добавляться цифра 1 или 0, означающая бар, на котором проверяются сигналы (может пригодиться для использования в индикаторах). Код оформлен в виде функции, первым параметром передается строка, далее по ссылке возвращается переменная Shift (номер бара) и переменные типа bool, соответствующие различным способам уведомления. Код подробно прокомментирован:

void NotifyOnOff(string Str,int &Shift,bool &Alerts,bool &Sounds,bool &EMail,bool &Push)
  {
//--- Преобразуем строку к нижнему регистру, что бы у пользователя
//--- была возможность использовать как прописные буквы, так и заглавные.
   StringToLower(Str);
//--- поиск букв в строке
   Alerts=(StringFind(Str,"a")!=-1);    // есть буква "a"
   Sounds=(StringFind(Str,"s")!=-1);    // есть буква "s"
   EMail=(StringFind(Str,"e")!=-1);     // есть буква "e"
   Push=(StringFind(Str,"p")!=-1);      // есть буква "p"
//--- поиск нуля
   if(StringFind(Str,"0")!=-1) Shift=0;  // в строке есть "0"
   else                       Shift=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. Проверим размер буфера:

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

Размер буфера отличен от нуля и даже превышает размер начальной строки. Память для строки была выделена с запасом. Теперь, при присвоении строке значения в пределах длины буфера, не потребуется перераспределение оперативной памяти под строку и присвоение произойдет очень быстро. Для того, чтобы размер буфера не сокращался, необходимо не присваивать строке новое значение, а добавлять его:

str+="a"; 

Теперь длина строки 1, а размер буфера не изменился. Таким образом можно несколько ускорить обработку строк. Очистить строку можно, устанавливая в начало строки символ с кодом 0:

StringSetCharacter(str,0,0); 

 

Заключение

Казалось бы второстепенная тема, только посредственно касающаяся основного назначения языка MQL5 - программирования экспертов и индикаторов. Однако, получилось довольно объемная статья, так как в языке представлен довольно широкий функционал по работе со строками. Не исключено даже, что во многих случаях, при программировании экспертов и индикаторов, не придется работать со строками, но все же когда-нибудь это придется делать. После прочтения этой статьи вы будете в полной "боевой готовности", и в случае необходимости вам не придется отвлекаться на изучение функций, вы сможете просто взять и сделать то, что нужно.

Немного повторим изложенный в статье материал, разделив функции на категории в соответствии с их назначением, важностью и частотой использования.

  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 - пример эксперта с сообщениями на разных языках.
Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (16)
Anatoli Kazharski
Anatoli Kazharski | 30 янв. 2018 в 17:07

Добавляет разделитель групп разрядов в строку:

//+------------------------------------------------------------------+
//|                                        DelimiterGroupsDigits.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property script_show_inputs
//--- Внешние параметры
input double Value     =1000000;
input string Delimiter =" "; // Delimiter, by default: " ", example: 1 000 000.0
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart(void)
  {
   ::Print(DelimiterGroupsDigits(string(Value),Delimiter));
  }
//+------------------------------------------------------------------+
//| Разделитель групп разрядов                                       |
//+------------------------------------------------------------------+
string DelimiterGroupsDigits(const string value,const string delimiter=" ")
  {
   string res=value;
   string elements[];
//--- Убираем лишние символы по краям
   ::StringTrimLeft(res);
   ::StringTrimRight(res);
//--- Проверка на вещественное число
   ushort sep   =::StringGetCharacter(".",0);
   int    total =::StringSplit(res,sep,elements);
//--- Получили строку
   if(total>0)
     {
      string str[];
      int length=::StringLen(elements[0]);
      for(int k=0,i=length-1; i>=0; i--)
        {
         int size=::ArraySize(str);
         ::ArrayResize(str,size+1);
         str[size]=::StringSubstr(elements[0],i,1);
         k++;
         //--- Добавляем разделитель
         if(k>=3 && i>0)
           {
            int array_size=::ArraySize(str);
            ::ArrayResize(str,array_size+1);
            str[array_size]=delimiter;
            k=0;
           }
        }
      //--- Собираем строку
      res="";
      int elements_total=::ArraySize(str);
      for(int i=elements_total-1; i>=0; i--)
         ::StringAdd(res,str[i]);
      //--- Если вещественное число
      if(total>1)
         ::StringAdd(res,"."+elements[1]);
     }
//--- Возвращаем результат
   return(res);
  }
//+------------------------------------------------------------------+

//---

Результат:

1 000 000.0
10,000,000.0
100'000'000.0
10 000.545
fxsaber
fxsaber | 30 янв. 2018 в 17:25
Anatoli Kazharski:

Добавляет разделитель групп разрядов в строку:

Как-то сложно.
Anatoli Kazharski
Anatoli Kazharski | 30 янв. 2018 в 17:26
fxsaber:
Как-то сложно.
Попробуете оптимизировать?
fxsaber
fxsaber | 30 янв. 2018 в 18:28
Anatoli Kazharski:
Попробуете оптимизировать?
string ModToString( ulong &Num, const int Mod = 1000, const int Len = 3 )
{
  const string Res = ((bool)(Num / Mod) ? IntegerToString(Num % Mod, Len, '0') : (string)(Num % Mod));
  
  Num /= Mod;
  
  return(Res);
}

string NumToString( ulong Num, const string Delimeter = " " )
{
  string Res = ModToString(Num);

  while (Num)
    Res = ModToString(Num) + Delimeter + Res;

  return(Res);
}

string NumToString( double Num, const int digits = 8, const string Delimeter = NULL )
{
  const string PostFix = (Num < 0) ? "-" : NULL;
  
  Num = MathAbs(Num);
    
  return(PostFix + NumToString((ulong)Num, Delimeter) + StringSubstr(DoubleToString(Num - (long)Num, digits), 1));
}

void OnStart()
{
  Print(NumToString(1234567.89, 2, " "));
}
Anatoli Kazharski
Anatoli Kazharski | 30 янв. 2018 в 19:03
fxsaber:

Отлично! И в четыре раза быстрее:

void OnStart(void)
  {
   long count=10000000;
   uint start=GetTickCount();
   for(int i=0; i<count && !IsStopped(); i++)
      DelimiterGroupsDigits(string(Value),Delimiter);

   uint end=GetTickCount()-start;
   Print("01 > ms: ",end,"; res: ",DelimiterGroupsDigits(string(Value),Delimiter));
//---
   start=GetTickCount();
   for(int i=0; i<count && !IsStopped(); i++)
      NumToString(Value,2,Delimiter);

   end=GetTickCount()-start;
   Print("02 > ms: ",end,"; res: ",NumToString(Value,2,Delimiter));
  }

//---

2018.01.30 21:02:15.996 01 > ms: 19047; res: 1 000 000.0
2018.01.30 21:02:20.683 02 > ms: 4688; res: 1 000 000.00
Интервью с Александром Артаповым (ATC 2012) Интервью с Александром Артаповым (ATC 2012)
Эксперт Александра Артапова (artall) уже на второй неделе Чемпионата оказался на третьей позиции, торгуя на двух символах EURUSD и EURJPY. Затем он ненадолго покинул TOP-10 и после месяца борьбы за выживание вновь включился в схватку за $80 000. И как оказалось, в запасе у его эксперта есть еще тузы в рукаве.
Как стать поставщиком сигналов для MetaTrader 4 и MetaTrader 5 Как стать поставщиком сигналов для MetaTrader 4 и MetaTrader 5
Хотите раздавать свои торговые сигналы и получать за это деньги? Зарегистрируйтесь на сайте MQL5.com в качестве продавца, укажите свой торговый счет и предложите трейдерам подписку на копирование ваших сделок.
Интервью с Сергеем Абрамовым (ATC 2012) Интервью с Сергеем Абрамовым (ATC 2012)
Торговый робот Сергея Абрамова (26405) со второй недели Чемпионата держится в TOP-10, и тем не менее немало побеспокоил своего разработчика. Как выяснилось, в нем оказалась небольшая ошибка в блоке закрытия позиций.
Интервью с Хуаном Пабло Алонсо Эскобаром (ATC 2012) Интервью с Хуаном Пабло Алонсо Эскобаром (ATC 2012)
"Кто испытывает трудности с программированием и не смог принять участие в нынешнем Чемпионате, я могу сказать только одно - со временем все становится намного проще", - заявил Хуан Пабло Алонсо Эскобар (JPAlonso), герой нашего сегодняшнего интервью.