Строки: таблица символов ASCII и её использование

Antoniuk Oleg | 26 апреля, 2007

Введение

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


Что такое ASCII ?

ASCII - Американский стандарт кодирования для передачи информации (American Standard Code for Information Interchange). Этот стандарт основан на английском алфавите. Коды ASCII представляют текст в компьютерах, коммуникационном оборудовании и других устройствах, которые работают с текстом. ASCII был создан в 1963 году, но впервые опубликован как стандарт в 1967 году. Последние изменения были внесены в 1986 году. Более детальную информацию про ASCII вы можете почитать здесь: https://en.wikipedia.org/wiki/ASCII. Далее мы рассмотрим как можно полностью вывести ASCII средствами MQL4, но для начала давайте рассмотрим основы работы со строками.



Основы построения библиотеки

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

//+------------------------------------------------------------------+
//|                                          StringExpereriment1.mq4 |
//|         Copyright © 2007, Antonio Banderass. All rights reserved |
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Antonio Banderass. All rights reserved"
#property link      "banderassa@ukr.net"
//+------------------------------------------------------------------+
//| start                                                            |
//+------------------------------------------------------------------+
int start()
  {
   string s1 = "Just_a_string", s2, symbol = "s";
   int lenght = StringLen(s1);
   MessageBox(s1);
   for(int x = 0; x < lenght; x++)
     {
       symbol = StringSetChar(symbol, 0, StringGetChar(s1, x));
       s2 = s2 + symbol + " ";
     }
   MessageBox(s2);
   return(0);
  }
//+------------------------------------------------------------------+

Разберем значение каждой строки отдельно.

string s1 = "Just_a_string", s2, symbol = "s";

Определяем три переменные типа string:

  • s1 - начальная строка, которую мы хотим обработать;
  • s2 - строка, в которую будет выведен результат;
  • symbol - строка, которая используется для временного хранения каждого символа.

Обратите внимание на то, что она инициализируется одним символом. Если этого не сделать, то в результате получим строку, в которой нет первого символа. Дело в том, что стандартная функция языка MQL4 StringSetChar() изменяет уже созданные символы, поэтому требуется хотя бы один символ для нормальной работы.

int lenght = StringLen(s1);
Определяем переменную целого типа для хранения длины строки. Для этого сразу же вызываем стандартную функцию для определения длины строки StringLen(), которая имеет единственный параметр - строку, длину которой требуется узнать.

MessageBox(s1);
Выводим строку до обработки.

for(int x = 0; x < lenght; x++)
Определяем цикл, в котором будет производиться посимвольная обработка. Обратите внимание, что счетчик инициализируется нулем, так как символы в строке индексируются с нуля, так же как и в массивах. В условии выполнения цикла используется оператор сравнения "меньше", так как последний символ имеет позицию lenght - 1.

symbol = StringSetChar(symbol, 0, StringGetChar(s1, x));

В этой строке используется две стандартные функции: StringSetChar() и StringGetChar(). Первая позволяет заменить один из символов строки, а вторая получить код символа в указанной позиции. Функция StringSetChar() имеет три параметра:

  • строку, в которой требуется произвести замену символа;
  • позиция символа, который следует заменить (помните, что символы индексируются с нуля, как в массивах);
  • код символа, которым следует произвести замену.

Функция возвращает результат в виде уже измененной строки. Еще одна важная функция - StringGetChar. Она имеет два параметра:

  • строку, в которой содержится символ, код которого нужно узнать;
  • позиция символа, код которого следует узнать.

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

s2 = s2 + symbol + " ";
Мы можем без проблем связывать строки (конкатенация) с помощью операций сложения (+). Здесь при каждой итерации цикла мы добавляем к результирующей строке очередной символ и пробел.
MessageBox(s2);
Выводим результат. Обратите внимание, что мы считываем каждый символ, начиная с первого, но можно поступить и наоборот. В таком случае получим меньше кода и переменных. Если при обработке строки для вас не имеет значения, с какой стороны начинать, то используйте следующий вариант:
//+------------------------------------------------------------------+
//|                                            StringExperiment2.mq4 |
//|         Copyright © 2007, Antonio Banderass. All rights reserved |
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Antonio Banderass. All rights reserved"
#property link      "banderassa@ukr.net"
//+------------------------------------------------------------------+
//| start                                                            |
//+------------------------------------------------------------------+
int start()
  {
   string s1 = "Just_a_string", s2, symbol = "s";
   int lenght = StringLen(s1) - 1;
   MessageBox(s1);
   while(lenght >= 0)
     {
       symbol = StringSetChar(symbol, 0, StringGetChar(s1, lenght));
       s2 = s2 + symbol + " ";
       lenght--;
     }
   MessageBox(s2);
   return(0);
  }
//+------------------------------------------------------------------+

Как видно, теперь вместо цикла for используется while, что позволяет избавиться от счетчика x. Для этих целей используется переменная lenght. В дальнейшем мы будем использовать один из этих двух шаблонов для написания функций в зависимости от того, имеет ли значение, в какой последовательности производить обработку. В нашем случае получим строку, в которой символы переставлены наоборот, то есть здесь последовательность обработки имеет большое значение.


Выводим все символы ASCII

Теперь давайте попробуем вывести все символов ASCII. Вспомните функции StringSetChar() и StringGetChar(), которые соответственно вставляют в указанную позицию символ из ASCII по его коду и возвращают код по символу. StringSetChar() имеет третий параметр int value. Это и есть код из таблицы символов ASCII. Давайте напишем специальный скрипт, чтобы определить код каждого символа:
//+------------------------------------------------------------------+
//|                                            StringExperiment3.mq4 |
//|         Copyright © 2007, Antonio Banderass. All rights reserved |
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Antonio Banderass. All rights reserved"
#property link      "banderassa@ukr.net"
//+------------------------------------------------------------------+
//| start                                                            |
//+------------------------------------------------------------------+
int start()
  {
   string s, symbol = "s";
   for(int x = 0; x < 256; x++)
    {
      symbol = StringSetChar(symbol, 0, x);
      s = s + x + " = " + symbol + " \t";
      if(x % 10 == 0)
          s = s + " \n";
    }
   MessageBox(s);
   return(0);
  }
//+------------------------------------------------------------------+

Скрипт использует встроенные функции MQL4, а также строковые константы новой строки и табуляции для наглядного представления таблицы. Теперь скомпилируйте и запустите его. Вы должны увидеть таблицу символов ASCII:



Рассмотрите ее повнимательнее. Вы заметите абсолютно все символы, которые только могут понадобиться, от цифр и букв до специальных символов, некоторые из которых вы наверняка увидите впервые. Сначала идет код, а потом после знака "=" сам символ. Я выделил некоторые важные наборы символов для большей наглядности:




Обратите внимание на расположение букв. Они расположены в алфавитном порядке. Эту особенность мы скоро используем для написания некоторых функций, например, для перевода букв строки из верхнего регистра в нижний и наоборот. А теперь давайте займемся новыми функциями и в конце на их основе создадим библиотеку.



StringUpperCase и StringLowerCase

Это две очень популярные функции для перевода строки в верхний или нижний регистр. Их реализация основана на том факте, что все коды символов букв верхнего регистра на 32 больше букв нижнего регистра. Это можно увидеть, если смотреть по таблице, что мы получили. На практике, если попробовать узнать коды символов 'А','Я','а', 'я', например, с помощью такого кода

string s = "AZaz";
MessageBox("A = " + StringGetChar(s, 0));
MessageBox("Z = " + StringGetChar(s, 1));
MessageBox("a = " + StringGetChar(s, 2));
MessageBox("z = " + StringGetChar(s, 3));

то получим результаты: А = 192, Я = 223, а = 224 и я = 255 соответственно. Поэтому следует учесть и эту особенность. Рассмотрим исходный код функций:

//+------------------------------------------------------------------+
//| StringUpperCase                                                  |
//+------------------------------------------------------------------+
string StringUpperCase(string str)
  {
   string s = str;
   int lenght = StringLen(str) - 1, symbol;
   while(lenght >= 0)
     {
       symbol = StringGetChar(s, lenght);
       if((symbol > 96 && symbol < 123) || (symbol > 223 && symbol < 256))
           s = StringSetChar(s, lenght, symbol - 32);
       else 
           if(symbol > -33 && symbol < 0)
               s = StringSetChar(s, lenght, symbol + 224);
       lenght--;
     }
   return(s);
  }
//+------------------------------------------------------------------+
//| StringLowerCase                                                  |
//+------------------------------------------------------------------+
string StringLowerCase(string str)
  {
   string s = str;
   int lenght = StringLen(str) - 1, symbol;
   while(lenght >= 0)
     {
       symbol = StringGetChar(s, lenght);
       if((symbol > 64 && symbol < 91) || (symbol > 191 && symbol < 224))
           s = StringSetChar(s, lenght, symbol + 32);
       else 
           if(symbol > -65 && symbol < -32)
               s = StringSetChar(s, lenght, symbol + 288);
       lenght--;
     }
   return(s);
  }
Так как последовательность обработки не имеет никакого значения, используем цикл while. Применять функции очень просто, единственный параметр - строка которую следует привести к нужному регистру:

  string s1 = "UPPER_REGISTER_STRING";
  string s2 = "lower_register_string";
  string s3 = "БОЛЬШИЕ_БУКВЫ";
  string s4 = "маленькие_буквы";
 
  MessageBox(StringLowerCase(s1)); // upper_register_string
  MessageBox(StringUpperCase(s2)); // LOWER_REGISTER_STRING
  MessageBox(StringLowerCase(s3)); // большие_буквы
  MessageBox(StringUpperCase(s4)); // МАЛЕНЬКИЕ_БУКВЫ


StringCompare

В MQL4 сравнение строк реализовано на уровне операторов с помощью "==". Интересно, что сравнение является регистрозависимым, то есть строки "STRING" и "string" - разные:

if("STRING" == "string") // FALSE
      MessageBox("TRUE");
   else
      MessageBox("FALSE");

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

//+------------------------------------------------------------------+
//| StringCompare                                                    |
//+------------------------------------------------------------------+
bool StringCompare(string s1, string s2)
  {
    return(StringLowerCase(s1) == StringLowerCase(s2));
  }

Пример использования:

if(StringCompare("STRING", "string")) // TRUE
      MessageBox("TRUE");
   else
      MessageBox("FALSE");

StringIsDigit

Эта функция проверяет содержимое строки. Если строка состоит только из цифр, то возвращает true, иначе false. Реализация довольно проста и основана на том, что символы цифр расположены в ряд и имеют коды от 48 до 58.

//+------------------------------------------------------------------+
//| StringIsDigit                                                    |
//+------------------------------------------------------------------+
bool StringIsDigit(string str)
  {
   bool result = true;
   string s = str;
   int lenght = StringLen(str) - 1, symbol;
   while(lenght > 0)
     {
       symbol = StringGetChar(s, lenght);
       if(!(symbol > 47 && symbol < 58))
         {
           result = false;
           break;
         }
       lenght--;
     }
   return(result);
  }

Пример использования:

if(StringIsDigit("1234567890")) // TRUE
      MessageBox("TRUE");
  else
      MessageBox("FALSE");
 
  if(StringIsDigit("1234notdigit")) // FALSE
      MessageBox("TRUE");
  else
      MessageBox("FALSE");

StringIsAlpha

Эта функция позволяет определить подобно предыдущей, состоит ли строка только из букв. Ее реализация аналогична:

//+------------------------------------------------------------------+
//| StringIsAlpha                                                    |
//+------------------------------------------------------------------+
bool StringIsAlpha(string str)
  {
   bool result = false;
   string s = str;
   int lenght = StringLen(str) - 1, symbol;
   while(lenght > 0)
     {
       symbol = StringGetChar(s, lenght);
       if((symbol > 96  && symbol < 123) || (symbol > 64 && symbol < 91) ||
          (symbol > 191 && symbol < 256) || (symbol > -65 && symbol < 0))
 
         {
           result = true;
           break;
         }
       lenght--;
     }
   return(result);
  }

Пример использования:

if(StringIsAlpha("thereAreSomeLetters")) // TRUE
      MessageBox("TRUE");
  else
      MessageBox("FALSE");
 
  if(StringIsAlpha("thereAreSomeDigits12345")) // FALSE
      MessageBox("TRUE");
  else
      MessageBox("FALSE");

Создание библиотеки

Теперь давайте соберем все эти функции в одну библиотеку. Для этого в редакторе MetaEditor 4 кликаем File->New->Library->Далее. В поле name пишем stringProcess и нажимаем на кнопку Готово. Потом вставляем код всех функций, приведенных выше, затем сохраняем. Осталось создать файл с прототипами функций, для этого File->New->Include(*. MQH)->Далее. В поле name пишем stringProcess, -> Готово. Теперь нужно вставить прототипы всех новых функций, а также указать директиву для импорта:

//+------------------------------------------------------------------+ 
//|                                                stringProcess.mqh |
//|                               Antonio Banderass Copyright © 2007 | 
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+ 
#property copyright "Antonio Banderass Copyright © 2007"
#property link      "banderassa@ukr.net"
//+--- 
#import "stringProcess.ex4"
//+------------------------------------------------------------------+
//| prototypes                                                       | 
//+------------------------------------------------------------------+
string StringUpperCase(string str); 
string StringLowerCase(string str);
bool StringCompare(string s1, string s2);
bool StringIsDigit(string str);
bool StringIsAlpha(string str);

Использование библиотеки stringProcess

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

//+------------------------------------------------------------------+
//|                                     stringProcessLibraryTest.mq4 |
//|                               Antonio Banderass Copyright © 2007 |
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Antonio Banderass Copyright © 2007"
#property link      "banderassa@ukr.net"
 
#include <stringProcess.mqh>
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
   if(StringIsDigit("1234567890")) // TRUE
       MessageBox("TRUE");
   else
       MessageBox("FALSE");
   if(StringIsDigit("1234notdigit")) // FALSE
       MessageBox("TRUE");
   else
       MessageBox("FALSE");
   return(0);
  }

Заключение

Итак, вы узнали что такое таблица символов ASCII и каким образом можно использовать особенности ее строения для реализации новых функций. Вы написали новые функции, которые являются довольно популярными в других языках программирования, но которых нет в MQL4. На основе их вы создали небольшую библиотеку для обработки строк. Я думаю, что эта библиотека будет использоваться не при самой торговле, но при выводе результатов. Например, если вы занимаетесь разработкой собственной системы отчетов для своего эксперта, то некоторые функции будут довольно полезными. Кроме того, наверняка найдется много областей применения, о которых я не подозреваю. Удачи вам и профитов.