Освой Arduino играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Строка в C
Строка в C++
08.StringConstructors
08.StringCaseChanges
StringCharacters
08.StringIndexOf
08.StringStartsWithEndsWith
Строковые функции языка C
Сконвертировать строку в int
Сконвертировать int в строку
Сконвертировать строку в массив символов
Строка может быть в двойных кавычках (массив символов), одиночным символом в одинарных кавычках, экземпляром объекта String.
Для вывода в строке самих кавычек и некоторых других служебных символов используются экранирующий символ \. Например, \" используется для вывода кавычки, а \n для переноса на новую строку.
Serial.print("This\nis\na\ntest\n\nKitty said, \"How are you?\"\n");
Пока не проверял, но видел в сети вариант, как можно создать строку, которая содержит много текста с кавычками, при помощи магии, а именно конструкции R"=====( bla bla bla )====="; (для плат ESP8266).
const char MAIN_page[] = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>ESP8266</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<style>
.imageDiv{
padding: 4%;
}
</style>
<body>
<div style="width:100%;">
<div style="width:50%; margin: 0 auto;">
<h1>ESP8266 Double quotes escape Demo</h1>
</div>
</div>
</body>
</html>
)=====";
В языке C строка должна завершаться нулевым символом ('\0'), поэтому при создании строки нужно указывать на один символ больше. Иногда компилятор может догадаться самостоятельно добавлять нулевой символ, но в некоторых случаях он не поможет.
Существует несколько способов объявить строку в виде символьного массива.
//char name[] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
//char name[7] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
//char name[] = {'B', 'a', 'r', 's', 'i', 'k'};
//char name[6] = {'B', 'a', 'r', 's', 'i', 'k'};
//char name[16] = {'B', 'a', 'r', 's', 'i', 'k'}; // массив с запасом
void setup() {
// Попробуйте все варианты
char name[] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
Serial.begin(9600);
Serial.print(name);
}
void loop() {}
Иногда массив строк записывают как указатель на символ (первый символ в массиве):
char *message = "Hello Kitty";
Существуют родные строковые функции самого языка C. Язык C++ также может их использовать в целях совместимости.
Длина строки (без завершающего нулевого символа) вычисляется при помощи функции strlen() (string length).
char name[7] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
Serial.println(strlen(name)); // 6
Функция strcpy (string copy) копирует одну строку в другую.
char cat[7] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
char copyCat[7];
strcpy(copyCat, cat);
Serial.println(copyCat);
Похожая функция strncpy() позволяет указать лимит символов, чтобы предотвратить копирование лишних символов в строку, которая не сможет содержать лишнего.
char cat[7] = {'B', 'a', 'r', 's', 'i', 'k', '\0'};
char copyCat[4];
strncpy(copyCat, cat, 4);
Serial.println(copyCat); // Bars
"Кошачья" функция strcat (string concatenate) присоединяет одну строку к другой.
char cat[3] = {'t', 'y', '\0'};
char smallCat[7] = {'k', 'i', 't', '\0'};
strcat(smallCat, cat);
Serial.println(smallCat); // kitty
Функция strcmp() осуществляет лексикографическую проверку двух строк, оканчивающихся нулевыми символами, и возвращает целое число со следующим значением: меньше 0 - если первая строка меньше, чем вторая строка, 0 - если обе строки совпадают, больше 0 - если первая строка больше второй строки.
В примере сравним массив символов со строкой C++, так как имеется совместимость между ними, но можно сравнить и два строковых массива или две строки.
char cat[4] = {'c', 'a', 't', '\0'};
int result = strcmp(cat, "cat");
Serial.println(result);
Функция sprintf(), выполняет форматирование массивов символов.
Допустим, мы хотим вывести информацию на двух строках ЖК-дисплея:
char line1[17];
int tempC = 30;
sprint(line1, "Temp: %d C", tempC);
char line2[17];
int h = 12;
int m = 30;
int s = 5;
sprintf(line2, "Time: %2d:%02d:%02d", h, m, s);
Массив символов line1 — это строковый буфер, содержащий форматированный текст, который имеет ёмкость 17 символов, включая завершающий нулевой символ.
В первом параметре функции передаётся массив символов, в который должен быть записан результат. Следующий аргумент — строка формата, содержащая смесь простого текста, такого как "Temp:", и команд форматирования, например %d. В данном случае %d означает «десятичное целое со знаком». Остальные параметры будут подставлены в строку формата в порядке их следования на место команд форматирования.
Чтобы вывести время во вторую строку на жидкокристаллическом дисплее, её можно сформировать из отдельных значений часов, минут и секунд. В результате на экране мы увидим.
Time: 12:30:05
Команда sprintf() не только подставила числа в нужные места, но и добавила ведущий ноль перед цифрой 5. В примере между символами : находятся команды форматирования трёх компонентов времени. Часам соответствует команда %2d, которая выводит двузначное десятичное число. Команды форматирования для минут и секунд немного отличаются (%02d). Эти команды также выводят двузначные десятичные числа, но добавляют ведущий ноль, если это необходимо. Однако имейте в виду, что этот приём предназначен для значений типа int.
На языке C можно воспользоваться функциями atoi() ( ASCII to int) и atol() (ASCII to long) для конвертации массива символов в int и long.
char x[10] = "450";
int result = atoi(x);
В C++ можно использовать toInt(). В классе Arduino Serial есть функция parseInt().
Строка как отдельный тип относится уже к языку C++. Вам не нужно указывать массив отдельных символов, достаточно просто окружить строку двойными кавычками. У класса String есть несколько удобных функций для работы со строками.
Только помните, что класс String требует больше ресурсов, чем при работе с массивами символов. Проведём эксперимент. Напишем первый скетч с использованием String.
void setup() {
Serial.begin(9600);
String firstName = "Alexander";
String lastName = "Klimov";
String fullName = firstName + " " + lastName;
Serial.println(fullName);
}
void loop() {}
При компиляции вывелось следующее: Sketch uses 3212 bytes (9%) of program storage space. Maximum is 32256 bytes. Global variables use 216 bytes (10%) of dynamic memory, leaving 1832 bytes for local variables. Maximum is 2048 bytes.
Перепишем пример.
void setup() {
Serial.begin(9600);
char myName[16] = "Alexander ";
char lastName[] = "Klimov";
strcat(myName, lastName);
Serial.println(myName);
}
void loop() {}
На этот раз выводится Sketch uses 1618 bytes (5%) of program storage space. Maximum is 32256 bytes. Global variables use 210 bytes (10%) of dynamic memory, leaving 1838 bytes for local variables. Maximum is 2048 bytes.
Второй скетч использует 5% памяти вместо 9%, т.е. почти в два раза меньше. Для сложных проектов подобное использование строк может стать решающим фактором.
Функции из C могут работать со строками C++. Например, определим длину строки.
strlen("cat"); // вернёт 3
String val = "1234";
int result = val.toInt();
Обратная задача конвертации int в строку есть в примере 08.StringConstructors
Пример File | Examples | 08.Strings | StringConstructors - манипуляции со строками: объединить две строки, конвертировать символ в строку, конвертировать число в строку в разных системах счисления.
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// send an intro:
Serial.println("\n\nString Constructors:");
Serial.println();
}
void loop() {
// обычная строка
String stringOne = "Hello String";
Serial.println(stringOne); // prints "Hello String"
// конвертируем символ в строку:
stringOne = String('a');
Serial.println(stringOne); // prints "a"
// конвертируем строку в объект String:
String stringTwo = String("This is a string");
Serial.println(stringTwo); // prints "This is a string"
// соединяем две строки:
stringOne = String(stringTwo + " with more");
// prints "This is a string with more":
Serial.println(stringOne);
// число в строку:
stringOne = String(13);
Serial.println(stringOne); // prints "13"
// число строку, выбирая формат, например, десятичный:
stringOne = String(analogRead(A0), DEC);
// prints "453" or whatever the value of analogRead(A0) is
Serial.println(stringOne);
// шестнадцатеричный:
stringOne = String(45, HEX);
// prints "2d", which is the hexadecimal version of decimal 45:
Serial.println(stringOne);
// бинарный
stringOne = String(255, BIN);
// prints "11111111" which is the binary value of 255
Serial.println(stringOne);
// большое число типа long:
stringOne = String(millis(), DEC);
// prints "123456" or whatever the value of millis() is:
Serial.println(stringOne);
// число с плавающей точкой с тремя разрядами после запятой:
stringOne = String(5.698, 3);
Serial.println(stringOne);
// с округлением до двух знаков после запятой:
stringOne = String(5.698, 2);
Serial.println(stringOne);
// do nothing while true:
while (true);
}
Переводим строку в верхний или нижний регистр с помощью функций toUpperCase() и toLowerCase() в примере File | Examples | 08.Strings | StringCaseChanges.
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// send an intro:
Serial.println("\n\nString case changes:");
Serial.println();
}
void loop() {
// toUpperCase() преобразует все символы в верхний регистр:
String stringOne = "<html><head><body>";
Serial.println(stringOne);
stringOne.toUpperCase();
Serial.println(stringOne);
// toLowerCase() преобразует все символы в нижний регистр:
String stringTwo = "</BODY></HTML>";
Serial.println(stringTwo);
stringTwo.toLowerCase();
Serial.println(stringTwo);
// do nothing while true:
while (true);
}
Функция charAt() позволяет узнать символ в указанной позиции строки. Функция setCharAt() позволяет заменить символ в указанной позиции.
Возьмём пример File | Examples | 08.Strings | StringCharacters и немного переделаем его немного.
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
}
void loop() {
String reportString = "Земля держится на 3 китах";
Serial.println(reportString);
// получим символ в позиции 33:
char mostSignificantDigit = reportString.charAt(33);
String message = "Символ в указанной позиции: ";
Serial.println(message + mostSignificantDigit);
Serial.println();
// Меняем символ и на о
reportString.setCharAt(38, 'о');
Serial.println(reportString);
// do nothing while true:
while (true);
}
Этот пример показывает, что нужно быть осторожным при работе с символами, которые не входят в состав ANSI, например, русскими. Если вы посчитаете, то увидите, что символ '3' находится на 19 позиции. Старайтесь работать с английским текстом, чтобы избежать путаницы.
Пример показывает применение функций indexOf() и lastIndexOf(). Функции находят первое вхождение символа в строке с начала или конца строки. Также можно указать смещение, с которого следует начинать поиск вхождения.
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("\n\nString indexOf() and lastIndexOf() functions:");
Serial.println();
}
void loop() {
// indexOf() возвращает позицию (индекс) указанного символа
String stringOne = "<HTML><HEAD><BODY>";
int firstClosingBracket = stringOne.indexOf('>'); // ищем первую закрывающую угловую скобку
Serial.println("The index of > in the string " + stringOne + " is " + firstClosingBracket);
stringOne = "<HTML><HEAD><BODY>";
int secondOpeningBracket = firstClosingBracket + 1;
int secondClosingBracket = stringOne.indexOf('>', secondOpeningBracket); // вторая закрывающая скобка
Serial.println("The index of the second > in the string " + stringOne + " is " + secondClosingBracket);
// можно также использовать indexOf() для поиска строк:
stringOne = "<HTML><HEAD><BODY>";
int bodyTag = stringOne.indexOf("<BODY>");
Serial.println("The index of the body tag in the string " + stringOne + " is " + bodyTag);
stringOne = "<UL><LI>item<LI>item<LI>item</UL>";
int firstListItem = stringOne.indexOf("<LI>");
int secondListItem = stringOne.indexOf("<LI>", firstListItem + 1);
Serial.println("The index of the second list tag in the string " + stringOne + " is " + secondListItem);
// lastIndexOf() находит последнее вхождение символа или строки
int lastOpeningBracket = stringOne.lastIndexOf('<');
Serial.println("The index of the last < in the string " + stringOne + " is " + lastOpeningBracket);
int lastListItem = stringOne.lastIndexOf("<LI>");
Serial.println("The index of the last list tag in the string " + stringOne + " is " + lastListItem);
// lastIndexOf() может искать строку:
stringOne = "<p>Lorem ipsum dolor sit amet</p><p>Ipsem</p><p>Quod</p>";
int lastParagraph = stringOne.lastIndexOf("<p");
int secondLastGraf = stringOne.lastIndexOf("<p", lastParagraph - 1);
Serial.println("The index of the second to last paragraph tag " + stringOne + " is " + secondLastGraf);
// do nothing while true:
while (true);
}
Функции startsWith() и endsWith() позволяют узнать, начинается или заканчивается строка на заданный символ (подстроку).
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("\n\nString startsWith() and endsWith():");
Serial.println();
}
void loop() {
// startsWith() проверят, начинается ли строка со слов HTTP/1.1:
String stringOne = "HTTP/1.1 200 OK";
Serial.println(stringOne);
if (stringOne.startsWith("HTTP/1.1")) {
Serial.println("Server's using http version 1.1");
}
// также можно использовать startsWith() с небольшим смещением:
stringOne = "HTTP/1.1 200 OK";
if (stringOne.startsWith("200 OK", 9)) {
Serial.println("Got an OK from the server");
}
// endsWith() проверяет, заканчивается ли строка на заданный символ
String sensorReading = "sensor = ";
sensorReading += analogRead(A0);
Serial.print(sensorReading);
if (sensorReading.endsWith("0")) {
Serial.println(". This reading is divisible by ten");
} else {
Serial.println(". This reading is not divisible by ten");
}
// do nothing while true:
while (true);
}
Для конвертации строки в массив символов есть отдельная функция toCharArray(). Не забывайте, что строка также содержит завершающий нулевой символ, поэтому следует прибавлять единицу к длине строки.
String str = "Hello Kitty";
// Length (with one extra character for the null terminator)
int str_len = str.length() + 1;
// Prepare the character array (the buffer)
char char_array[str_len];
// Copy it over
str.toCharArray(char_array, str_len);
Arduino Reference: String() - официальная документация