Урок 30. Текстовые строки в Ардуино. Конвертирование данных в строки и наоборот. Класс String.

Язык C

В уроке рассказываю о текстовых строках в Ардуино, о классе String, о преобразовании различных типов данных в текстовые строки и об обратной операции - преобразовании строк в числа.

Предыдущий урок     Список уроков     Следующий урок

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

 

Текстовые строки в Ардуино.

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

 

В Ардуино признаком конца строки является число 0, в текстовом виде '\0'. При объявлении строковых переменных в некоторых случаях необходимо явно указывать признак конца строки, а в некоторых он формируется по умолчанию.

Способы объявления и инициализации текстовых строк.

char myStr1[10];

Объявлен символьный массив определенного размера. При заполнении его символами необходимо позаботиться о записи в конце строки байта со значением 0 – признака окончания строки.

char myStr2 [6] = {‘S’, ‘t’, ‘a’, ‘r’, ‘t’};

Объявлен массив и присвоено значение элементам. В конце строки компилятор прибавит признак конца строки автоматически.

char myStr3[6] = {‘S’, ‘t’, ‘a’, ‘r’, ‘t’, '/0’};

То же самое, только завершающий признак мы объявили явно.

char myStr4 [ ] = “Start”;

Массив инициализирован строковой константой. Компилятор автоматически задаст размер массива и добавит завершающий символ.

char myStr5 [6 ] = “Start”;

То же самое, только размер массива указан явно.

char myStr 6[20 ] = “Start”;

Можно явно указать больший размер массива, например, если он будет использован для строк разной длины.

  • Строковые константы объявляются внутри двойных кавычек (”Start”).
  • Отдельные символы задаются внутри одинарных ('S').

Длинные строки допускают объявление в таком виде:

char myStr7 [] = “Текстовая строка может быть”
“ объявлена”
“ таким образом”;

Ничего не мешает задавать массивы строк. Поскольку сами строки являются массивами, массивы строк будут двумерными массивами.

char * myStrArray = { “Сообщение 1”, “Сообщение 2”, “Сообщение 3”, “Сообщение 4”, “Сообщение 5”, “Сообщение 6”};

Массивы строк объявляют как массив указателей. Это связано с тем, что строки могут иметь разную длину, и приходится резервировать двумерный массив данных, рассчитанный на самую длинную строку. А указатели требуют одинаковое количество ячеек памяти.

Управляющие символы.

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

Код символа в HEX (DEC) Название Обозначение

Описание

0 (0) Конец строки \0 Признак конца строки
0D (13) Возврат каретки \r Перемещает курсор в крайнюю левую позицию
0A (10) Перевод строки \n Перемещает курсор на одну строку вниз

Для того чтобы вывести текст с новой строки необходимо использовать символы '\r' и '\n'.

Простая программа, демонстрирующая использование управляющих символов для печати с новой строки.

// управляющие символы 
void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  Serial.println("\r\n Begin \r\n next string"
  "\r\n 3 empty strings \r\n\r\n\r\n");  // вывод сообщения в нескольких строках
  delay(1000); 
}

На экране монитора последовательного порта увидим:

Сообщение
Часто управляющие символы '\r' и '\n' применяют для завершения команды в символьном виде, например AT команды. Такой способ управления будем использовать в следующем уроке для драйвера шагового двигателя.

 

Конвертирование различных типов данных Ардуино в текстовую строку.

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

  • при выводе данных на дисплей или индикаторы;
  • передаче данных на другие устройства, например компьютер;
  • некоторые электронные компоненты требуют обмена данными с помощью AT команд в символьном виде, например GSM модемы, WiFi модули и т.п.

Существует достаточно много способов для решения этой задачи. Я подробно опишу несколько из них, на мой взгляд, самых удачных.

Конвертирование данных в строку через встроенные функции классов ввода-вывода.

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

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

Преобразование Функция класса Sreial

Описание

int в DEC текст print(int d) Преобразует переменную int в строку с десятичным представлением числа
int в DEC текст print(int d, DEC) Преобразует переменную int в строку с десятичным представлением числа
int в HEX текст print(int d, HEX) Преобразует переменную int в строку с шестнадцатеричным представлением числа
int в OCT текст print(int d, OCT) Преобразует переменную int в строку с восьмеричным представлением числа
int в BIN текст print(int d, BIN) Преобразует переменную int в строку с двоичным представлением числа
float в текст print(float d) Преобразует переменную float в строку с двумя знаками после запятой
float в текст print(float d, N) Преобразует переменную float в строку с N знаками после запятой

Например, конвертирование числа int в строку будет выглядеть так.

int x= 24562;
Serial.print(x);

Преобразование переменной float в строку можно выполнить так.

float x= 12.657;
Serial.print(x);

Преобразование будет выполнено при передаче данных на другое устройство. Функции класса Serial подробно описаны в уроке 12.

Если выводите данные на LCD дисплей с помощью библиотеки LiquidCristal, то для преобразования данных типа int можно использовать метод print (урок 23).

Преобразование Функция класса LiquidCristal Описание
int в DEC текст print(int d, DEC) Преобразует переменную int в строку с десятичным представлением числа
int в HEX текст print(int d, HEX) Преобразует переменную int в строку с шестнадцатеричным представлением числа
int в OCT текст print(int d, OCT) Преобразует переменную int в строку с восьмеричным представлением числа
int в BIN текст print(int d, BIN) Преобразует переменную int в строку с двоичным представлением числа

В библиотеке LiquidCristal нет функции для вывода данных типа float. Можно воспользоваться способом, предложенным в уроке 20 для библиотеки Led4Digits. Стандартная функция класса Led4Digits  позволяет отображать на LED индикаторах только целые числа, но добавив простые вычисления можно легко выводить данные с плавающей запятой.

 

Конвертирование целочисленных данных в строку через функции itoa, ltoa, ultoa.

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

itoa (int data, char* string, int radix);     // преобразование int

ltoa (long data, char* string, int radix);  // преобразование long

ultoa (unsigned long data, char* string, int radix);  // преобразование unsigned long

  • data – это конвертируемая переменная;
  • char* string – указатель на строку (имя массива);
  • radix – система исчисления результата в строке:
    • 10 для DEC;
    • 8 для OCT;
    • 16 для HEX;
    • 2 для BIN.

Например, конвертирование переменой x типа int в строку myStr1 можно сделать так.

itoa(x, myStr1, 10); // в десятичном виде
itoa(x, myStr1, 8); // в восьмеричном виде
itoa(x, myStr1, 16); // в шестнадцатеричном виде
itoa(x, myStr1, 2); // в двоичном виде

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

// проверка преобразования числа в текстовую строку
int x=0;  // переменная, которая выводится
char myStr[20];  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  // подготовка буфера строки
  for (int i=0; i<20; i++) {myStr[i]=' ';}  // заполнение пробелами
  myStr[18]='\r'; // возврат каретки
  myStr[19]='\n'; // перевод строки

  // преобразование переменной int x
  itoa(x, myStr, 10); // int -> DEC
  //itoa(x, myStr, 8); // int -> OCT
  //itoa(x, myStr, 16); // int -> HEX
  //itoa(x, myStr, 2); // int -> BIN

  // преобразование переменной long x 
  //ltoa(x, myStr, 10); // long -> DEC
  //ltoa(x, myStr, 8); // long -> OCT
  //ltoa(x, myStr, 16); // long -> HEX
  //ltoa(x, myStr, 2); // long -> BIN
 
  // преобразование переменной unsigned long x
  //ultoa(x, myStr, 10); // long -> DEC
  //ultoa(x, myStr, 8); // long -> OCT
  //ultoa(x, myStr, 16); // long -> HEX
  //ultoa(x, myStr, 2); // long -> BIN

  Serial.write(myStr, 20);
  x++; 
  delay(500); 
}

В цикле каждые 0,5 секунд происходит:

  • Текстовая строка myStr заполняется пробелами, в конце добавляются управляющие символы возврат каретки и перевод строки.
  • Переменная x конвертируется одной из функцией. Результат оказывается в буфере myStr.
  • Функция Serial.write(myStr, 20); передает через последовательный порт 20 байтов массива myStr в виде байтов.
  • Прибавляется 1 к переменной x.

Чтобы проверить нужную функцию необходимо освободить ее от признака комментарий. Я проверил все.

Для вывода чисел с плавающей запятой можно опять предложить метод из урока 20.

 Конвертирование данных в строку с помощью функции sprintf.

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

sprint это функция форматированного вывода. Ее широко используют в приложениях на компьютерах. Она дает самые широкие возможности для преобразования данных в строку. Но в системе Ардуино sprintf не поддерживает формат чисел с плавающей запятой.

int sprintf( char *string, const char *format , argument1, argument2 ... )

Функция возвращает число преобразованных символов. В случае ошибки возвращает число – 1.

  • argument – это переменные, которые необходимо преобразовать;
  • format – управляющая строка:

% [флаг] [ширина] тип_формата

Флаг и ширина - необязательные поля.

Тип формата Тип выходных данных
c Символ
s Символьная строка
d, i Целое десятичное число
u Целое без знаковое десятичное число
o Целое восьмеричное число
x Целое шестнадцатеричное число

Флаги.

Знак Действие
- Выравнивание результата влево
+ Выводит знак числа
Пробел Выводит знак пробел перед положительными числами
0 Заполняет поле 0

Ширина – минимальный размер поля для вывода символов. Если длина числа меньше, то добавляются пробелы. Если перед шириной стоит 0, то добавляются нули.

На примерах из таблицы все должно быть понятно.

int x= 125;  int y= 34;

Функция Выведет в myStr
sprintf(myStr2,"%d",x ); 125
sprintf(myStr2,"%5d",x );   125
sprintf(myStr2,"%05d",x ); 00125
sprintf(myStr2,"%+05d",x ); +00125
sprintf(myStr2,"Цикл %d закончен",x ); Цикл   125 закончен
sprintf(myStr2,"Цикл %d закончен y= %d",x,y ); Цикл   125 закончен y= 34
sprintf(myStr2,"%o",x ); 175
sprintf(myStr2,"%x",x ); 7d

Можете загрузить следующую программу и проверить работу sprintf в реальном контроллере Ардуино.

// проверка преобразования числа в текстовую строку
// с помощью sprintf
int x=0;  // переменная, которая выводится
char myStr[20];  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  // подготовка буфера строки
  for (int i=0; i<20; i++) {myStr[i]=' ';}  // заполнение пробелами
  myStr[18]='\r'; // возврат каретки
  myStr[19]='\n'; // перевод строки

  // преобразование переменной int x в строку
  sprintf(myStr,"%d",x ); // int -> DEC
  //sprintf(myStr,"%5d",x ); // int -> DEC
  //sprintf(myStr,"%05d",x ); // int -> DEC
  //sprintf(myStr,"%+05d",x ); // int -> DEC
  //sprintf(myStr,"Cycle %d is over",x );  // int -> DEC с текстом
  //sprintf(myStr,"%o",x ); // int -> OCT
  //sprintf(myStr,"%x",x ); // int -> HEX

  Serial.write(myStr, 20);
  x++; 
  delay(500); 
}

Конвертирование данных типа float в текстовую строку.

Самый простой способ преобразования float в текстовую строку – использование функции dtostrf.

char* dtostrf(double data, signed char width, unsigned char prec, char *string)

  • data – это конвертируемая переменная;
  • width – число значащих разрядов;
  • prec – число разрядов после запятой;
  • char* string – указатель на строку (имя массива).

float x= 12.728;
dtostrf(x, 2, 3, myStr3);  // выводим в строку myStr3 2 разряда до, 3 разряда после запятой

Вот программа для проверки такого способа.

// проверка преобразования числа в текстовую строку
// с помощью dtostrf
float x=0;  // переменная, которая выводится
char myStr[20];  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  // подготовка буфера строки
  for (int i=0; i<20; i++) {myStr[i]=' ';}  // заполнение пробелами
  myStr[18]='\r'; // возврат каретки
  myStr[19]='\n'; // перевод строки

  // преобразование переменной float x в строку
  dtostrf(x, 2, 3, myStr);
 
  Serial.write(myStr, 20);
  x+= 0.01; 
  delay(500); 
}

У меня работает.

 

Конвертирование текстовой строки в различные типы данных.

В следующем разделе речь идет об обратном преобразовании – текстовой строки в число.

Преобразование строки в данные с помощью функций atoi, atol, atof.

Хороший, удобный метод. Функции простые, имеют вид:

int atoi(const char* string);  // преобразование в int

long atol(const char* string);  // преобразование в long

double atof(const char* string);  // преобразование в float

В качестве аргумента функций указывается указатель на строку с числом в десятичном виде. Возвращают – конвертированное значение числа.

Например, преобразование строки myStr4 в переменную x типа int будет выглядеть так.

int x;
x= atoi(myStr4);

Для чисел с плавающей запятой.

float x;
x= atof(myStr4);

Вот программа проверки atoi и atol.

// проверка преобразования текстовой строки в число
// с помощью atoi
char myStr[]= "123";  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  Serial.println(atoi(myStr));  // преобразование строки в int
  //Serial.println(atol(myStr));  // преобразование строки в long
  delay(1000);   
}

А в этой программе я проверил работу atof.

// проверка преобразования текстовой строки в число
// с помощью atof
char myStr[]= "123.456";  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  Serial.println(atof(myStr),3);  // преобразование строки в float
  delay(1000);   
}

Конвертирование текстовой строки в числа с помощью функции sscanf.

Функция является обратной функцией для sprintf с такими же недостатками и достоинствами. Но она позволяет конвертировать числа в восьмеричном и шестнадцатеричном форматах. Тип float эта функция на Ардуино не поддерживает.

int sscanf( char *string, const char *format , address1, address2 ... )

Все аргументы и форматы такие же, как у sprintf. Только указываются адреса переменных (address1, address2 ... ).

Конвертирование строки myStr5 в переменную x типа int будет выглядеть так.

int x;
sscanf(myStr5,"%d", &x);  // для десятичных чисел
sscanf(myStr5,"%o", &x);  // для восьмеричных чисел
sscanf(myStr5,"%x", &x);  // для шестнадцатеричных чисел

Вот скетч проверки функции для целочисленных операций.

// проверка преобразования текстовой строки в число
// с помощью sscanf
int x;
char myStr[]= "123";  // текстовый массив

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {
  sscanf(myStr,"%d", &x);
  Serial.println(x);  // преобразование строки в int
  delay(1000);   
}

 

Класс String Ардуино.

В Ардуино существует класс String. Он предоставляет более широкие возможности для работы с текстовыми строками.

Надо четко различать:

  • char myStr [ ] = “Start”;  - строка символов, т.е. массив типа char с завершающим признаком в конце;
  • String MyStr = Start”;  - экземпляр класса String.

Принято имена текстовых строк начинать как обычные переменные с маленькой буквы (myStr), а экземпляры String – с большой буквы (MyStr).

Экземпляры класса String занимают больше памяти, медленнее обрабатываются, но зато они позволяют расширять , объединять строки, производить поиск, замену символов и многое другое. Пользоваться текстовыми строками или классом String – решать Вам. В оптимальных по ресурсам приложениях лучше использовать строки.

Несколько основных функций класса String.

Я опишу только минимум функций, необходимых для работы с классом String.

String()

Конструктор, создает экземпляр класса String. Объект типа String может быть создан из разных типов данных:

String MyStr1 = "Start";  // инициализация строковой константы
String MyStr2 =  String('d');  // преобразование символа в объект String
String MyStr3 =  String("Start");  // преобразование строковой константы в объект String
String MyStr4 =  String(MyStr1 + " in 10 sec");  // конкатенация двух строк
String MyStr5 =  String(67);  // использование целочисленной константы
String MyStr6 =  String(analogRead(1), DEC);  // использование int с основанием 10
String MyStr7 =  String(346, BIN);  // использование int с основанием 2
String MyStr8 =  String(89, HEX);  // использование int с основанием 16
String MyStr9 =  String(millis(), DEC);  // использование long с основанием системы счисления

При создании объекта из числа, сформированная строка будет содержать ASCII (символьное) представление числа. По умолчанию используется десятичная система счисления, но можно указать другую. Т.е. функция String может осуществлять преобразование целочисленных данных в текстовую строку.

toCharArray(*buf, length)

Копирует текст экземпляра класса String в указанный массив.

MyStr.toCharArray(*buf, length)

  • buf – указатель на массив;
  • length – количество символов.

MyStr.toCharArray(myStr, 10);  // копируем 10 символов в массив myStr

int length()

Функция возвращает длину строки String в символах без учета завершающего признака нуля.

int len =MyStr.length();  // получаем длину строки MyStr

long toInt()

Функция преобразовывает объект String в целое число.

int x = MyStr.toInt();  // конвертирование строки MyStr в int

 

Конвертирование данных в строку String.

Для целочисленных форматов все очень просто.

String MyStr;
int x;
 MyStr= String(x, DEC);  // для десятичных чисел
MyStr= String(x, HEX);  // для шестнадцатеричных чисел
MyStr= String(x, BIN);  // для двоичных чисел

Программа для проверки.

// проверка преобразования числа в String
int x=0;  // переменная, которая выводится
String MyStr;

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {

  MyStr= String(x, DEC);  // int -> DEC
  //MyStr= String(x, HEX);  // int -> HEX
  //MyStr= String(x, BIN);  // int -> BIN

  Serial.println(MyStr); 
  x++; 
  delay(500); 
}

Для плавающей запятой надо использовать функцию dtostrf(). С помощью нее получить строку-массив, а затем занести ее в объект String.

float x=2.789;
String MyStr;
char myStr8[10];
dtostrf(x, 2, 3, myStr8);  // выводим в строку myStr8 2 разряда до, 3 разряда после запятой
MyStr = myStr8;     

Программа для проверки преобразования float.

// проверка преобразования числа в String
float x=0;  // переменная, которая выводится
String MyStr;
char myStr[10];

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() {

  dtostrf(x, 2, 3, myStr);  // выводим в строку myStr 2 разряда до, 3 разряда после запятой
  MyStr = myStr;
 
  Serial.println(MyStr); 
  x += 0.01; 
  delay(500); 
}

 

Конвертирование строки String в различные типы данных.

Для целых чисел используем функцию toInt().

String MyStr = "123";
int x = MyStr.toInt();

Проверяем.

// проверка преобразования String в число
// с помощью toInt
String MyStr = "123";

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() { 
  Serial.println(MyStr.toInt());  // преобразование строки в int
  delay(1000);   
}

Для плавающей запятой.

Получим данные из объекта String в массив и выполним преобразование функцией atof().

String MyStr = "34.123";
char myStr8[10];
MyStr.toCharArray(myStr8, MyStr.length());    // копирование String в массив myStr8
float x = atof(myStr8);  //  преобразование в float

Программа для проверки.

// проверка преобразования String в число
// с помощью toInt
String MyStr = "34.123";
char myStr[10];

void setup() {
  Serial.begin(9600); // скорость 9600
}

void loop() { 
  MyStr.toCharArray(myStr, MyStr.length()); 
  Serial.println(atof(myStr));  //
  delay(1000);   
}

 

Я привел достаточно много различных способов преобразования данных в строки и наоборот. Выбирайте, придумывайте свои.

Предыдущий урок     Список уроков     Следующий урок

12 комментариев на «Урок 30. Текстовые строки в Ардуино. Конвертирование данных в строки и наоборот. Класс String.»

  1. Как работает такое объявление строки?
    void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    }

    void loop() {
    // put your main code here, to run repeatedly:
    String dataString = «»;
    for (int i=0 ; i<10 ; i++) {
    dataString += "hh";
    Serial.println (dataString);
    delay(500);
    }
    }

  2. sprintf(myStr2,»Цикл %d закончен y= %d»,x );
    Кажется здесь закралась ошибка — не хватает аргумента у
    sprintf(myStr2,»Цикл %d закончен y= %d»,x,у);

  3. а есть примеры как посчитать длину строки в массиве указателей.в среде ардуино.

    char * myStrArray = { “Сообщение 1”, “Сообщение 2”, “Сообщение 3”, “Сообщение 4”, “Сообщение 5”, “Сообщение 6”};
    например количество символов в “Сообщение 2”.
    где почитать. очень я туп, не разберусь.

  4. char myStr[10]= «12342»; //

    char nom= (myStr[3]); // nom = «2»
    int x =atol(nom); // почему х = ноль

    или так int x =atol(myStr[3]); // ?

  5. Здравствуйте! В общем не получается преобразовать строку «1B» в десятичное целое число… Подскажите рабочий вариант!

    • int x;
      char myStr[]= «1B»; // текстовый массив

      void setup() {
      Serial.begin(9600);
      }

      void loop() {
      sscanf(myStr,»%x», &x);
      Serial.println(x); // преобразование строки в int
      delay(1000);
      }

      У меня работает.

  6. Спасибо! работает.. Но не получается другое:
    Допустим, есть строка: String MyStr = «41 05 1B «;
    (использую MyStr.substring(6,8);…)
    Надо взять из нее 1B, напечатать в десятичном виде…

  7. Ответ на мой вопрос, преобразуем «1С»:
    String bb = «41 05 1C «;
    String MyStr = «-«;
    char myStr[10];
    int x;

    void setup() {
    Serial.begin(9600);
    }

    void loop() {
    MyStr = bb.substring(6,8);
    MyStr.toCharArray(myStr, MyStr.length()+1);
    sscanf( myStr , «%x» , &x);
    Serial.println(x); //
    delay(1000);

    1. Надо было поставить +1 ( MyStr.length()+1 )
    2. Сперва Строку преобразовать в массив.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *