Набор инструментов для работы со строками
- Быстрые функции конвертации
- Парсинг, разбивание по разделителям
- Несколько классов-конвертеров данных в строку и обратно для использования в других библиотеках
- Кодирование и раскодирование base64, url, unicode, йцукен/qwerty-раскладки
Совместима со всеми Arduino платформами (используются Arduino-функции)
Класс-обёртка для всех типов строк. Может быть создана в конструкторе из:
"const char"- строкиchar[]- строкиF("f-строки")PROGMEM- строкиString- строки
Особенности:
- Хранит тип и длину строки
- Позволяет печататься, конвертироваться в любой целочисленный формат и сравниваться с переменными всех стандартных типов, а также сравниваться с любыми другими строками
- Вывод в подстроки разными способами, поиск и разделение
- Не может изменять исходную строку, все операции только "для чтения"
- Не создаёт копию строки и работает с оригинальной строкой, т.е. оригинальная строка должна быть в памяти на время существования Text
- Если создана из String строки, то оригинальная String строка не должна меняться в процессе работы экземпляра Text
// ====== КОНСТРУКТОР ======Text(String& str);
Text(const String& str);
Text(constuint8_t* str, uint16_t len);
Text(constchar* str, int16_t len = 0, bool pgm = 0);
Text(const __FlashStringHelper* str, int16_t len = 0);
// ======== СИСТЕМА ========operatorbool(); // Статус строки, существует или нетboolvalid(); // Статус строки, существует или нетboolpgm(); // Строка из Flash памятиuint16_tlength(); // Длина строкиuint16_treadLen(); // посчитать и вернуть длину строки (const)voidcalcLen(); // пересчитать и запомнить длину строки (non-const)
Type type(); // Тип строкиconstuint8_t* bytes(); // Получить указатель на строку. Всегда вернёт указатель, отличный от nullptr!constchar* str(); // Получить указатель на строку. Всегда вернёт ненулевой указательconstchar* end(); // указатель на конец строки. Всегда вернёт ненулевой указательboolterminated(); // строка валидна и оканчивается \0// ======== UNICODE ========// Длина строки с учётом unicode символовuint16_tlengthUnicode();
// получить позицию юникод символа в строке, если она содержит юникодuint16_tposToUnicode(uint16_t pos);
// получить реальную позицию символа в строке, если она содержит юникодuint16_tunicodeToPos(uint16_t pos);
// ======== ХЭШ ========size_thash(); // хэш строки size_tuint32_thash32(); // хэш строки 32 бит// ======== PRINT ========size_tprintTo(Print& p); // Напечатать в Print (c учётом длины)// ======== СРАВНЕНИЕ И ПОИСК ========// сравнивается со всеми типами строк через ==// Сравнить со строкой, начиная с индексаboolcompare(Text s, uint16_t from = 0);
// Сравнить со строкой, начиная с индекса, с указанием количества символовboolcompareN(Text s, uint16_t amount, uint16_t from = 0);
// Найти позицию символа в строке, начиная с индексаint16_tindexOf(char sym, uint16_t from = 0);
// Найти позицию строки в строкеint16_tindexOf(Text txt, uint16_t from = 0);
// Найти позицию строки в строкеint16_tindexOfUnicode(Text txt, uint16_t from = 0);
// Найти позицию символа в строке с концаint16_tlastIndexOf(char sym);
// Найти позицию строки в строке с концаint16_tlastIndexOf(Text txt);
// Найти позицию строки в строке с концаint16_tlastIndexOfUnicode(Text txt);
// найти символ и получить указатель на первое вхождениеconstchar* find(char sym, uint16_t from = 0);
// начинается со строкиboolstartsWith(const Text& txt);
boolstartsWith(char c);
// заканчивается строкойboolendsWith(const Text& txt);
boolendsWith(char c);
// ======== РАЗДЕЛЕНИЕ И ПАРСИНГ ========// вернёт новую строку с убранными пробельными символами с начала и конца
Text trim();
// Посчитать количество подстрок, разделённых символом (количество разделителей +1)uint16_tcount(char sym);
// Посчитать количество подстрок, разделённых строками (количество разделителей +1)uint16_tcount(Text txt);
// Разделить по символу-разделителю в массив любого типаuint16_tsplit(T* arr, uint16_t len, char div);
uint16_tsplit(T** arr, uint16_t len, char div);
// Разделить по строке-разделителю в массив любого типаuint16_tsplit(T* arr, uint16_t len, Text div);
uint16_tsplit(T** arr, uint16_t len, Text div);
// Получить подстроку из списка по индексу и разделителю
Text getSub(uint16_t idx, char div);
Text getSub(uint16_t idx, Text div);
// Найти индекс подстроки по разделителю. Вернёт -1 если не найденоintfindSub(Text sub, char div);
intfindSub(Text sub, Text div);
// выделить подстроку (начало, конец не включая). Отрицательные индексы работают с конца строки
Text substring(int16_t start, int16_t end = 0);
// выделить подстроку с содержанием юникода (начало, конец не включая). Отрицательные индексы работают с конца строки
Text substringUnicode(int16_t start, int16_t end = 0);
// Получить символ по индексу. Допускаются отрицательныеcharcharAt(int idx);
charoperator[](int idx);
// ======== ВЫВОД. СТРОКИ ========// получить const char* копию (Cstr конвертируется в const char*). Всегда валидна и терминирована. Если Text из PGM или не терминирован - будет создана временная копия
Cstr c_str();
// Получить как String строку
String toString(bool decodeUnicode = false);
// Вывести в String строку. Вернёт false при неудачеbooltoString(String& s, bool decodeUnicode = false);
// Добавить к String строке. Вернёт false при неудачеbooladdString(String& s, bool decodeUnicode = false);
// Вывести в char массив. Вернёт длину строки. terminate - завершить строку нулёмuint16_ttoStr(char* buf, int16_t bufsize = -1, bool terminate = true);
// получить как строку, раскодировать unicode
String decodeUnicode();
// получить как строку, раскодировать urlencode
String decodeUrl();
// ======== ВЫВОД. B64 ========// размер данных (байт), если они b64size_tsizeB64();
// вывести в переменную из b64booldecodeB64(void* var, size_t size);
// ======== ВЫВОД. ЧИСЛА ========booltoBool(); // получить значение как boolinttoInt(); // получить значение как intint16_ttoInt16(); // получить значение как int16int32_ttoInt32(); // получить значение как int32int64_ttoInt64(); // получить значение как int64uint32_ttoInt32HEX(); // получить значение как uint 32 из HEX строкиfloattoFloat(); // получить значение как float// также автоматически конвертируется и сравнивается сchar + unsignedshort + unsignedint + unsignedlong + unsignedlonglong + unsignedboolfloatdouble
String
// для ручного управления строкойconstchar* _str; // указатель на строкуuint16_t _len; // длинаText может конвертироваться во все стандартные целочисленные типы вручную и автоматически (implicit и explicit):
Text t("1234");
int v = t;
(int)t;
t.toInt();Text может сравниваться со всеми стандартными типами (автоматически преобразуется):
Text t("1234");
t == 1234;
t >= 1234;
t == "1234";
t == F("1234");Приведение Text к bool проверяет валидность строки (корректная или нет), это конструкции вида:
Text t;
(bool)t;
!t;
!!t;
if (t);Для конвертации в bool как значение нужно использовать метод преобразования toBool(), а лучше - сравнение (учитывает валидность):
Text t;
t.toBool();
if (t == true);
if (t == false);Если строка невалидна - сравнение с bool вернёт false независимо от значения.
Text можно вывести в новую String-строку:
t.toString(); // вернёт String
String s = t; // запишется в s
t.toString(s); // запишется в s
t.addString(s); // прибавится к sМожно вывести в строку с декодированием:
String s = t.decodeUnicode(); // декодировать UCN символы (вида \uxxx\uxxx)
String s = t.decodeUrl(); // декодировать urlencoded символы (вида %AB)Text можно вывести в массив char (можно указать размер буфера и терминировать ли строку):
char buf[20];
t.toStr(buf);Часто бывает нужно передать Text строку в функцию, которая принимает const char*. Метод str() вернёт указатель на начало строки, но случаях, когда Text создан
- Из PROGMEM строки
- Из обычной строки с указанием размера, меньшего чем размер оригинальной строки (например любой элемент JSON при парсинге библиотекой GSON)
Такая "строка" не будет являться корректной, потому что имеет несоответствующую длину или область памяти! Для корректной передачи используйте метод c_str(): он создаст временную корректную копию строки в тех случаях, когда это нужно (PGM или неполная длина). Например foo(text.c_str()). Примечание: этот указатель является временным - нельзя передавать строку в функции, которые запоминают указатель на строку вместо того, чтобы скопировать её себе! Примеры: WiFi.begin(text.c_str()) - можно, функция копирует строку себе. Для библиотеки PubSubClient - client.setServer(text.c_str()) - нельзя, нужно выводить в char буфер или String строку и оставить существовать в области определения клиента. Как это понять: изучать исходник!
Text хранит указатель на строку, поэтому строка должна существовать в памяти на время существования Text:
// 1. так делать НЕЛЬЗЯ
Text t1(String("123")); // строка будет выгружена из памяти!// t1.... программа сломается// 2. так делать НЕЛЬЗЯ
String s;
Text t1(s);
s += String("123"); // адрес строки изменится!// t1.... программа сломается// 3. так делать МОЖНОvoidfoo(const Text& text) {
// String существует тут// text...
}
foo(String("123"));Text list("abc/123/def");
Serial.println(list.getSub(0, '/')); // abc
Serial.println(list.getSub(2, '/')); // def
Serial.println(list.substring(4, 6)); // 123// парсинг в массив строк Text
Text arr[3];
list.split(arr, 3, '/');
Serial.println(arr[0]);
Serial.println(arr[1]);
Serial.println(arr[2]);
// парсинг в массив чиселint arr2[3]; // float, long...
list.split(arr2, 3, '/');
Serial.println(arr2[0]);
Serial.println(arr2[1]);
Serial.println(arr2[2]);Встроенный разделитель и хэш-функции позволяют очень просто и эффективно разбирать различные текстовые протоколы. Например пакет вида key=value, где key может отсылать к переменной в коде. Пакет можно разделить, ключ хешировать и опросить через switch для присвоения н ужной переменной:
Text txt("key1=1234");
int val = txt.getSub(1, '='); // значение в intswitch (txt.getSub(0, '=').hash()) { // хэш ключаcaseSH("key1"):
var1 = val;
break;
caseSH("key2"):
var2 = val;
break;
caseSH("key3"):
var2 = val;
break;
}или протокол вида name/index/value, где name - текстовый ключ, index - порядковый номер:
Text txt("key/3/1234");
int val = txt.getSub(2, '/');
switch (txt.getSub(0, '/').hash()) {
caseSH("key"):
switch(txt.getSub(1, '/').toInt16()) {
case0: break;
case1: break;
case2: break;
//.....
}
break;
caseSH("keykey"):
//...break;
caseSH("anotherKey"):
//...break;
}Добавка к Text, поддерживает все остальные стандартные типы данных. Имеет буфер 22 байта, при создании конвертирует число в него:
Value(bool value);
Value(char + unsigned value, uint8_t base = DEC);
Value(short + unsigned value, uint8_t base = DEC);
Value(int + unsigned value, uint8_t base = DEC);
Value(long + unsigned value, uint8_t base = DEC);
Value(longlong + unsigned value, uint8_t base = DEC);
Value(double value, uint8_t dec = 2);Value v0("-123456"); // все строки также можно
Value v1(123);
Value v2(3.14);
Value v3((uint64_t)12345678987654321);
// конвертируется из числа в текст
v1 = 10;
v1 = 3.14;
v1 = 12345654321ull;
Serial.println(v0); // печатается в Serial
Serial.println(v1 == v2); // сравнивается// сравнивается с любыми строками
Text s("123");
String ss = "123";
Serial.println(s == "123");
Serial.println(s == F("123"));
Serial.println(s == ss);
// конвертируется в любой типfloat f = v2; // f == 3.14int i = v1; // i = 123// выводится в String
String S;
v0.toString(s);
// выводится в char[]char buf[v1.length() + 1]; // +1 для '\0'
v1.toStr(buf);
Textавтоматически сравнивается и конвертируется во все типы, кромеbool. ИспользуйtoBool(). Преобразование к bool показывает существование строки
На базе Text построены следующие библиотеки:
- Строки любого типа
- Без аллокаций, что чрезвычайно критично при сборке String
- Без создания десятка перегруженных функций
Например нужна функция, принимающая строку в любом виде. В ванильном фреймворке Arduino можно сделать так:
voidsetText(const String& str) {
// и например прибавить к строке
s += str;
}Такая функция сможет принимать любые строки:
setText("const literal");
setText(F("F-string"));
char str[] = "buffer string";
setText(str);
String s = "Arduino String";
setText(s);Но эта строка будет продублирована в конструкторе String, и самое страшное - в динамической памяти! Таким образом при прибавлении к условно-глобальной String в этой области определения случится переаллокация и фрагментирование памяти. Text позволяет полностью этого избежать:
voidsetText(const Text& str) {
// и например прибавить к строке
str.addString(s);
}Теперь эта функция так же умеет принимать строки в любом формате, но не создаёт их копии, и например прибавление к строке становится быстрым и безопасным.
Также Text удобен для вывода, например в классе, который хранит буфер и сам наполняет его данными и знает их длину:
classMyClass {
public:
Text get() {
returnText(buffer, len);
}
private:char buffer[20];
int len;
};
MyClass s;
Serial.println(s.get());Вариант с наследованием:
classMyClass : publicText {
public:voidfoo() {
Text::_str = buffer;
Text::_len = somelen;
}
private:char buffer[20];
};
MyClass s;
Serial.println(s);Если вместо Text использовать Value - функция сможет принимать также любые численные данные.
Разделитель Text списков на Text подстроки.
TextListT<int16_t cap>(Text list, char div);
TextListT<int16_t cap>(Text list, Text div);
// количество построкuint16_tlength();
// размер буфераuint16_tcapacity();
// получить подстроку под индексомconst Text& get(uint16_t idx);
const Text& operator[](int idx);TextList(Text list, char div);
TextList(Text list, Text div);
// количество построкuint16_tlength();
// получить подстроку под индексомconst Text& get(uint16_t idx);
const Text& operator[](int idx);"Потоковый" разделитель Text строки на подстроки для работы в цикле
TextParser(const Text& txt, char div);
TextParser(const Text& txt, const Text& div);
// парсить. Вернёт true, если найдена подстрокаboolparse();
// индекс подстроки (всегда > 0)intindex();
// получить текущую подстрокуconst Text& get();Пример:
// forfor (TextParser p("123;456", ';'); p.parse();) {
Serial.println(p);
}
// while
TextParser p("123;456", ';');
while (p.parse()) {
Serial.println(p);
}Пример с вложенными подстроками с разными разделителями:
Text t("123;456\nabc;def;ghk\n333;444");
for (TextParser row(t, '\n'); row.parse();) {
for (TextParser col(row, ';'); col.parse();) {
Serial.print(col);
Serial.print(',');
}
Serial.println();
}
// вывод:// 123,456,// abc,def,ghk,// 333,444,Статический стринг билдер на базе Text, замена mString
template <uint16_t cap> StringStatic;
StringExt(char* buf, uint16_t capacity, uint16_t len = 0);
// очиститьvoidclear();
// завершить нулёмvoidterminate();
// автоматически завершать нулём (умолч. false)voidterminateAuto(bool ter);
// обрезать с from до концаvoidtrunc(uint16_t from);
// прибавитьboolconcat(const Value& txt);
boolconcat(constchar* str, uint16_t len);
boolconcat(double val, uint8_t dec);
// присвоитьboolassign(const Value& txt);
boolassign(constchar* str, uint16_t len);
boolassign(double val, uint8_t dec);
// можно приравнивать и прибавлять любой тип через =, +=, +Пример:
StringStatic<50> s;
// char str[20];// StringExt s(str, 20);
s = F("abc");
s += "def";
s += 12345;
s += 'a';
Serial.println(s);
Serial.println(s.length());
s.clear();
s = s + 123 + "abc" + F("FSTR") + 3.14;
Serial.println(s);Можно дописать существующую строку:
char str[20] = "hello"; // len 5
StringExt s(str, 20, 5);
s += F(" world!");
Serial.println(s);Развернуть
Разделение строки на подстроки по разделителю в цикле. Изменяет исходную строку, но после завершения возвращает разделители на место.
su::Parser p(String& str, char div = ';');
su::Parser p(constchar* str, char div = ';');
boolnext(); // парсить следующую подстроку. Вернёт false, если парсинг законченuint8_tindex(); // индекс текущей подстрокиconstchar* str(); // получить подстроку
Text get(); // получить подстроку как Textchar buf[] = "123;456;abc";
su::Parser p(buf);
while (p.next()) {
Serial.print(p.index());
Serial.print(": ");
Serial.println(p.get());
}Разделение строки на подстроки по разделителю в цикле. Изменяет исходную строку! После удаления объекта строка восстанавливается, либо вручную вызвать restore()
SplitterT<макс. подстрок> spl(String& str, char div = ';');
SplitterT<макс. подстрок> spl(constchar* str, char div = ';');
Splitter spl(String& str, char div = ';'); // авто-размер (выделяется в heap)
Splitter spl(constchar* str, char div = ';'); // авто-размер (выделяется в heap)voidsetDiv(char div); // установить разделительvoidrestore(); // восстановить строку (вернуть разделители)uint8_tlength(); // количество подстрокconstchar* str(uint16_t idx); // получить подстроку по индексу
Text get(uint16_t idx); // получить подстроку по индексу как Textchar buf[] = "123;456;abc";
Splitter spl(buf);
for (uint8_t i = 0; i < spl.length(); i++) {
Serial.print(i);
Serial.print(": ");
Serial.println(spl[i]);
}
spl.restore();// Получить количество подстрок в спискеuint16_tsu::list::length(Text list, char div = ';');
// Получить индекс подстроки в спискеint16_tsu::list::indexOf(Text list, Text str, char div = ';');
// Проверка содержит ли список подстрокуboolsu::list::includes(Text list, Text str, char div = ';');
// Получить подстроку из списка по индексу
Text su::list::get(Text list, uint16_t idx, char div = ';');
// распарсить в массив указанного типа и размера. Вернёт количество записанных подстрокtemplate <typename T>
uint16_tsu::list::parse(Text list, T* buf, uint16_t len, char div = ';');Serial.println(su::list::length("123;456;333")); // 3
Serial.println(su::list::includes("123;456;333", "456")); // true
Serial.println(su::list::indexOf("123;456;333", "333")); // 2
Serial.println(su::list::get("123;456;333", 1)); // 456// распарсить в массивfloat arr[3];
su::list::parse(F("3.14;2.54;15.15"), arr, 3);Получение подстрок по разделителям без модификации исходной строки, работает также с PROGMEM строками.
List(Text);
// установить разделительvoidsetDiv(char div);
// получить размер спискаuint16_tlength();
// получить индекс подстроки в списке или -1 если её нетint16_tindexOf(Text str);
// проверить наличие подстроки в спискеboolincludes(Text str);
// получить подстроку под индексом
Text get(uint16_t idx);
// распарсить в массив указанного типа и размера. Вернёт количество записанных подстрокtemplate <typename T>
uint16_tparse(T* buf, uint16_t len);su::List list(F("123;456;333"));
Serial.print("len: ");
Serial.println(list.length()); // 3
Serial.print("index 2: ");
Serial.println(list[2]); // 333
Serial.print("index of '456':");
Serial.println(list.indexOf(F("456"))); // 1
Serial.print("index of '789':");
Serial.println(list.indexOf("789")); // -1// переписать в массивint arr[3];
list.parse(arr, 3);// строка, в которую можно делать print/println
su::PrintString prs;
prs += "как обычный String";
prs.print(10);
prs.println("hello");
prs.print(F("print!"));
Serial.println(prs);// Изменить раскладку (RU в QWERTY) - String
String su::toQwerty(const String& ru);
// Изменить раскладку (RU в QWERTY) - char* (qw длина как ru + 1, функция добавит '\0')char* su::toQwerty(constchar* ru, char* qw);// закодировать в b64charencodeByte(uint8_t n);
// размер закодированных данных по размеру исходныхsize_tencodedLen(size_t len);
// закодировать в char[encodedLen()] (не добавляет '\0' в конец)size_tencode(char* b64, constvoid* data, size_t len, bool pgm = false);
// закодировать в Stringsize_tencode(String* b64, constvoid* data, size_t len, bool pgm = false);
size_tencode(String& b64, constvoid* data, size_t len, bool pgm = false);
// раскодировать из b64uint8_tdecodeChar(char b);
// будущий размер декодированных данных по строке b64 и её длинеsize_tdecodedLen(constvoid* b64);
size_tdecodedLen(constvoid* b64, size_t len);
// раскодировать данные из строки b64 длиной len в буфер datasize_tdecode(void* data, constvoid* b64);
size_tdecode(void* data, constvoid* b64, size_t len);
// раскодировать данные из строки b64 в буфер datasize_tdecode(void* data, const String& b64);
// раскодировать данные из строки b64 длиной len в саму себяsize_tdecode(void* b64);
size_tdecode(void* b64, size_t len);Декодер строки, содержащей unicode символы вида \u0abc. Также делает unescape символов \t\r\n!
// декодировать строку с unicode символами саму в себя (не добавляет '\0' в конец)size_tdecodeSelf(char* str);
size_tdecodeSelf(char* str, size_t len);
// декодировать строку с unicode символами
String decode(constchar* str);
String decode(constchar* str, size_t len);
// декодировать строку с unicode символами
String decode(const String& str);
// кодировать unicode символ по его коду. В массиве должно быть 5 ячеекuint8_tencode(char* str, uint32_t c, bool terminate = true);
// кодировать unicode символ по его коду
String encode(uint32_t code);// символ должен быть urlencodedboolneedsEncode(char c);
// длина urlencoded строкиsize_tencodedLen(constchar* str);
size_tencodedLen(constchar* str, size_t len);
// закодировать в char[encodedLen()] (не добавляет '\0' в конец)size_tencode(char* url, constchar* str);
size_tencode(char* url, constchar* str, size_t len);
// закодировать в Stringvoidencode(String* url, constchar* str);
voidencode(String* url, constchar* str, size_t len);
voidencode(String& url, constchar* str);
voidencode(String& url, constchar* str, size_t len);
String encode(constchar* str);
String encode(constchar* str, size_t len);
String encode(const String& str);
// длина urldecoded строкиsize_tdecodedLen(constchar* url);
size_tdecodedLen(constchar* url, size_t len);
// раскодировать url (не добавляет '\0' в конец)size_tdecode(char* str, constchar* url);
size_tdecode(char* str, constchar* url, size_t len);
// раскодировать в Stringsize_tdecode(String* str, constchar* url);
size_tdecode(String* str, constchar* url, size_t len);
size_tdecode(String& str, constchar* url);
size_tdecode(String& str, constchar* url, size_t len);
String decode(constchar* url);
String decode(constchar* url, size_t len);
String decode(const String& url);
// раскодировать url саму в себяsize_tdecodeSelf(char* url);
size_tdecodeSelf(char* url, size_t len);// StringLength длина строки, выполняется на этапе компиляцииconstexprsize_tSL(constchar* str);
constexprsize_toperator"" _SL; // C++ 11Примеры
intL1 = SL("text");
intL2 = "text"_SL;Вместо сравнения строк можно сравнивать хэш этих строк, что делает программу компактнее, легче и в большинстве случаев быстрее. Функции, указанные ниже как "считается компилятором" - считаются компилятором, то есть строка даже не попадает в код программы - вместо неё подставляется хэш-число:
// считается компиляторомconstexprsize_tSH(constchar* str); // (String Hash) размер size_tconstexprsize_tSH32(constchar* str); // (String Hash) размер 32 битconstexprsize_toperator"" _h; // C++ 11constexprsize_toperator"" _h32; // C++ 11// считается в рантаймеsize_tsu::hash(constchar* str, int16_t len = -1); // Размер зависит от платформы и соответствует size_tuint32_tsu::hash32(constchar* str, int16_t len = -1); // Размер 32 битsize_tsu::hash_P(PGM_P str, int16_t len = -1); // PROGMEM строка, размер size_tuint32_tsu::hash32_P(PGM_P str, int16_t len = -1); // PROGMEM строка, размер 32 битНовый макрос (начиная с библиотеки v1.4.30)
H(key); // равносильно SH("key") и "key"_hНа ESP-платах
_SH,SH,hashиhash_Pпо умолчанию являются 32-битными!
По проведённому тесту 32-битная версия хэша имеет 7 коллизий из 234450 английских слов, 16-битная версия - 170723 коллизий (что есть 73% - чисто статистическое количество коллизий из расчёта 16 бит - 65536 значений)
Определить, какая строка была "получена". Классический способ со сравнением строк из PROGMEM:
char buf[] = "some_text";
if (!strcmp_P(buf, PSTR("abcdef"))) Serial.println(0);
elseif (!strcmp_P(buf, PSTR("12345"))) Serial.println(1);
elseif (!strcmp_P(buf, PSTR("wrong text"))) Serial.println(2);
elseif (!strcmp_P(buf, PSTR("some text"))) Serial.println(3);
elseif (!strcmp_P(buf, PSTR("hello"))) Serial.println(4);
elseif (!strcmp_P(buf, PSTR("some_text"))) Serial.println(5);Способ с хэшем строки:
char buf[] = "some_text";
switch (hash(buf)) {
caseSH("abcdef"): Serial.println(0); break;
caseSH("12345"): Serial.println(1); break;
caseSH("wrong text"): Serial.println(2); break;
case"some text"_h: Serial.println(3); break;
case"hello"_h: Serial.println(4); break;
caseH(some_text): Serial.println(5); break;
}Один расчёт хэша занимает чуть большее время, чем сравнение со строкой. Но итоговая конструкция из примера выполняется в 2 раза быстрее (на ESP).
SH("строки")и"строки"_hв данном примере вообще не попадают в код программы - вместо них подставляется их хэш
// Длина строки с русскими символамиuint16_tsu::strlenRu(constchar* str);
// Получить длину целого числаuint8_tsu::intLen(int32_t val);
// Получить длину float числаuint8_tsu::floatLen(double val, uint8_t dec);
// Преобразовать строку в целое числоtemplate <typename T>
T su::strToInt(constchar* str, uint8_t len = 0);
// Преобразовать PROGMEM строку в целое числоtemplate <typename T>
T su::strToInt_P(constchar* str, uint8_t len = 0);
// Преобразовать float в строку с указанием кол-ва знаков после точкиuint8_tsu::floatToStr(double val, char* buf, uint8_t dec = 2);
// Преобразовать HEX строку в целое число. Опционально длинаuint32_tsu::strToIntHex(constchar* str, int8_t len = -1);
// Длина символа в количестве байтuint8_tsu::charSize(char sym);
// Конвертация числа в char* массив. Пишет от начала массива, добавляет '\0', вернёт длину строки// для int64 макс. длина буфера - 22 символа, для int32 - 12uint8_tsu::uintToStr(uint32_t n, char* buf, uint8_t base = DEC);
uint8_tsu::intToStr(int32_t n, char* buf, uint8_t base = DEC);
uint8_tsu::uint64ToStr(uint64_t n, char* buf, uint8_t base = DEC);
uint8_tsu::int64ToStr(int64_t n, char* buf, uint8_t base = DEC);
// конвертация из строки во floatfloatsu::strToFloat(constchar* s);
// конвертация из PROGEMEM строки во floatfloatsu::strToFloat_P(PGM_P s);
// быстрый целочисленный логарифм 10 (длина числа в кол-ве символов)uint8_tsu::getLog10(uint32_t value);
uint8_tsu::getLog10(int32_t value);
// быстрое возведение 10 в степеньuint32_tsu::getPow10(uint8_t value);- v1.0
- v1.1.0 - оптимизация, добавлены фичи, исправлены уязвимости
- v1.2 - добавлены хэш-функции
- v1.2.x - мелкие исправления и улучшения
- v1.3 - оптимизация const, добавлены фичи, сравнения
- v1.3.1 - добавлен substring
- v1.3.2 - поддержка ESP8266 версий 2.x
- v1.3.5 - uintToStr: HEX теперь в нижнем регистре как у си-функций
- v1.3.6 - в Text добавлены toInt32HEX(), count(), split() и getSub(). Добавлен парсер TextList
- v1.3.7 - исправлены варнинги на AVR
- v1.4.0 - AnyText переименован в Text, пространство имён sutil - в su, добавлены функции в Text, результат конвертации и substring приведены к Си и JS функциям
- 1.4.1
- оптимизирован split
- можно split-разделять в массив указателей на переменные
- добавлен TextParser
- добавлены стринг билдеры StringExt и StringStatic
- 1.4.3 - Оптимизация сравнения, добавлено constexpr измерение длины строки
- 1.4.7 - исправлен баг split для esp32
- 1.4.9 - оптимизация, добавлены короткие функции хеширования
- 1.4.10 - в Text добавлены decodeUrl и decodeUnicode
- 1.4.12 - в Text добавлены инструменты для Unicode (substring, indexOf)
- 1.4.15 - мелкие улучшения, частично "убран" префикс su, в Text добавлен корректный вывод в си-строки с временным буфером
- Библиотеку можно найти по названию StringUtils и установить через менеджер библиотек в:
- Arduino IDE
- Arduino IDE v2
- PlatformIO
- Скачать библиотеку .zip архивом для ручной установки:
- Распаковать и положить в C:\Program Files (x86)\Arduino\libraries (Windows x64)
- Распаковать и положить в C:\Program Files\Arduino\libraries (Windows x32)
- Распаковать и положить в Документы/Arduino/libraries/
- (Arduino IDE) автоматическая установка из .zip: Скетч/Подключить библиотеку/Добавить .ZIP библиотеку… и указать скачанный архив
- Читай более подробную инструкцию по установке библиотек здесь
- Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
- Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
- Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru
Библиотека открыта для доработки и ваших Pull Request'ов!
При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
- Версия библиотеки
- Какой используется МК
- Версия SDK (для ESP)
- Версия Arduino IDE
- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код