Набор инструментов для работы со строками
- Быстрые функции конвертации
- Парсинг, разбивание по разделителям
- Несколько классов-конвертеров данных в строку и обратно для использования в других библиотеках
- Кодирование и раскодирование 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(const uint8_t* str, uint16_t len);
Text(const char* str, int16_t len = 0, bool pgm = 0);
Text(const __FlashStringHelper* str, int16_t len = 0);
// ======== СИСТЕМА ========
operator bool(); // Статус строки, существует или нет
bool valid(); // Статус строки, существует или нет
bool pgm(); // Строка из Flash памяти
uint16_t length(); // Длина строки
uint16_t readLen(); // посчитать и вернуть длину строки (const)
void calcLen(); // пересчитать и запомнить длину строки (non-const)
Type type(); // Тип строки
const char* str(); // Получить указатель на строку. Всегда вернёт ненулевой указатель
const char* end(); // указатель на конец строки. Всегда вернёт ненулевой указатель
bool terminated(); // строка валидна и оканчивается \0
// ======== UNICODE ========
// Длина строки с учётом unicode символов
uint16_t lengthUnicode();
// получить позицию юникод символа в строке, если она содержит юникод
uint16_t posToUnicode(uint16_t pos);
// получить реальную позицию символа в строке, если она содержит юникод
uint16_t unicodeToPos(uint16_t pos);
// ======== ХЭШ ========
size_t hash(); // хэш строки size_t
uint32_t hash32(); // хэш строки 32 бит
// ======== PRINT ========
size_t printTo(Print& p); // Напечатать в Print (c учётом длины)
// ======== СРАВНЕНИЕ И ПОИСК ========
// сравнивается со всеми типами строк через ==
// Сравнить со строкой, начиная с индекса
bool compare(Text s, uint16_t from = 0);
// Сравнить со строкой, начиная с индекса, с указанием количества символов
bool compareN(Text s, uint16_t amount, uint16_t from = 0);
// Найти позицию символа в строке, начиная с индекса
int16_t indexOf(char sym, uint16_t from = 0);
// Найти позицию строки в строке
int16_t indexOf(Text txt, uint16_t from = 0);
// Найти позицию строки в строке
int16_t indexOfUnicode(Text txt, uint16_t from = 0);
// Найти позицию символа в строке с конца
int16_t lastIndexOf(char sym);
// Найти позицию строки в строке с конца
int16_t lastIndexOf(Text txt);
// Найти позицию строки в строке с конца
int16_t lastIndexOfUnicode(Text txt);
// найти символ и получить указатель на первое вхождение
const char* find(char sym, uint16_t from = 0);
// начинается со строки
bool startsWith(const Text& txt);
bool startsWith(char c);
// заканчивается строкой
bool endsWith(const Text& txt);
bool endsWith(char c);
// ======== РАЗДЕЛЕНИЕ И ПАРСИНГ ========
// вернёт новую строку с убранными пробельными символами с начала и конца
Text trim();
// Посчитать количество подстрок, разделённых символом (количество разделителей +1)
uint16_t count(char sym);
// Посчитать количество подстрок, разделённых строками (количество разделителей +1)
uint16_t count(Text txt);
// Разделить по символу-разделителю в массив любого типа
uint16_t split(T* arr, uint16_t len, char div);
uint16_t split(T** arr, uint16_t len, char div);
// Разделить по строке-разделителю в массив любого типа
uint16_t split(T* arr, uint16_t len, Text div);
uint16_t split(T** arr, uint16_t len, Text div);
// Получить подстроку из списка по индексу и символу-разделителю
Text getSub(uint16_t idx, char div);
// Получить подстроку из списка по индексу и строке-разделителю
Text getSub(uint16_t idx, Text div);
// выделить подстроку (начало, конец не включая). Отрицательные индексы работают с конца строки
Text substring(int16_t start, int16_t end = 0);
// выделить подстроку с содержанием юникода (начало, конец не включая). Отрицательные индексы работают с конца строки
Text substringUnicode(int16_t start, int16_t end = 0);
// Получить символ по индексу. Допускаются отрицательные
char charAt(int idx);
char operator[](int idx);
// ======== ВЫВОД. СТРОКИ ========
// получить const char* копию (Cstr конвертируется в const char*). Всегда валидна и терминирована. Если Text из PGM или не терминирован - будет создана временная копия
Cstr c_str();
// Получить как String строку
String toString(bool decodeUnicode = false);
// Вывести в String строку. Вернёт false при неудаче
bool toString(String& s, bool decodeUnicode = false);
// Добавить к String строке. Вернёт false при неудаче
bool addString(String& s, bool decodeUnicode = false);
// Вывести в char массив. Вернёт длину строки. terminate - завершить строку нулём
uint16_t toStr(char* buf, int16_t bufsize = -1, bool terminate = true);
// получить как строку, раскодировать unicode
String decodeUnicode();
// получить как строку, раскодировать urlencode
String decodeUrl();
// ======== ВЫВОД. B64 ========
// размер данных (байт), если они b64
size_t sizeB64();
// вывести в переменную из b64
bool decodeB64(void* var, size_t size);
// ======== ВЫВОД. ЧИСЛА ========
bool toBool(); // получить значение как bool
int toInt(); // получить значение как int
int16_t toInt16(); // получить значение как int16
int32_t toInt32(); // получить значение как int32
int64_t toInt64(); // получить значение как int64
uint32_t toInt32HEX(); // получить значение как uint 32 из HEX строки
float toFloat(); // получить значение как float
// также автоматически конвертируется и сравнивается с
char + unsigned
short + unsigned
int + unsigned
long + unsigned
long long + unsigned
float
double
String
// для ручного управления строкой
const char* _str; // указатель на строку
uint16_t _len; // длина
// конструктор
Text v0("-123456");
Text v1 = "-123456";
v1 = F("-123456");
String s("abcd");
Text v2(s);
v2 = s;
// сравнение
v2 == v1;
v2 == F("text");
v1 == -123456;
// авто конвертация
int v = v0;
// вывод в String
String s = v0;
s = v0.toString();
v0.toString(s);
// вывод в массив
char buf[20];
v1.toStr(buf);
// парсинг и разделение
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 arr[3];
list.split(arr, 3, '/');
Serial.println(arr[0]);
Serial.println(arr[1]);
Serial.println(arr[2]);
// парсить можно в любой тип
int arr2[3]; // float, byte...
list.split(arr2, 3, '/');
Serial.println(arr2[0]);
Serial.println(arr2[1]);
Serial.println(arr2[2]);
// так делать НЕЛЬЗЯ
Text t1(String("123")); // строка будет выгружена из памяти!
// t1.... программа сломается
String s;
Text t1(s);
s += String("123"); // адрес строки изменится!
// t1.... программа сломается
// в то же время вот так - можно
void foo(const Text& text) {
// String существует тут
print(text);
}
foo(String("123"));
Встроенный разделитель и хэш-функции позволяют очень просто и эффективно разбирать различные текстовые протоколы. Например пакет вида key=value
, где key
может отсылать к переменной в коде. Пакет можно разделить, ключ хешировать и опросить через switch для присвоения н ужной переменной:
Text txt("key1=1234");
int val = txt.getSub(1, '='); // значение в int
switch (txt.getSub(0, '=').hash()) { // хэш ключа
case su::SH("key1"):
var1 = val;
break;
case su::SH("key2"):
var2 = val;
break;
case su::SH("key3"):
var2 = val;
break;
}
или протокол вида name/index/value
, где name
- текстовый ключ, index
- порядковый номер:
Text txt("key/3/1234");
int val = txt.getSub(2, '/');
switch (txt.getSub(0, '/').hash()) {
case su::SH("key"):
switch(txt.getSub(1, '/').toInt16()) {
case 0: break;
case 1: break;
case 2: break;
//.....
}
break;
case su::SH("keykey"):
//...
break;
case su::SH("anotherKey"):
//...
break;
}
Часто бывает нужно передать 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
, поддерживает все остальные стандартные типы данных. Имеет буфер 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(long long + unsigned value, uint8_t base = DEC);
Value(double value, uint8_t dec = 2);
// аналогично с ручным размером буфера
su::ValueT<размер буфера>();
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.14
int 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 можно сделать так:
void setText(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
позволяет полностью этого избежать:
void setText(const Text& str) {
// и например прибавить к строке
str.addString(s);
}
Теперь эта функция так же умеет принимать строки в любом формате, но не создаёт их копии, и например прибавление к строке становится быстрым и безопасным.
Также Text удобен для вывода, например в классе, который хранит буфер и сам наполняет его данными и знает их длину:
class MyClass {
public:
Text get() {
return Text(buffer, len);
}
private:
char buffer[20];
int len;
};
MyClass s;
Serial.println(s.get());
Вариант с наследованием:
class MyClass : public Text {
public:
void foo() {
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_t length();
// размер буфера
uint16_t capacity();
// получить подстроку под индексом
const Text& get(uint16_t idx);
const Text& operator[](int idx);
TextList(Text list, char div);
TextList(Text list, Text div);
// количество построк
uint16_t length();
// получить подстроку под индексом
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, если найдена подстрока
bool parse();
// индекс подстроки (всегда > 0)
int index();
// получить текущую подстроку
const Text& get();
Пример:
// for
for (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);
// очистить
void clear();
// завершить нулём
void terminate();
// автоматически завершать нулём (умолч. false)
void terminateAuto(bool ter);
// обрезать с from до конца
void trunc(uint16_t from);
// прибавить
bool concat(const Value& txt);
bool concat(const char* str, uint16_t len);
bool concat(double val, uint8_t dec);
// присвоить
bool assign(const Value& txt);
bool assign(const char* str, uint16_t len);
bool assign(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(const char* str, char div = ';');
bool next(); // парсить следующую подстроку. Вернёт false, если парсинг закончен
uint8_t index(); // индекс текущей подстроки
const char* str(); // получить подстроку
Text get(); // получить подстроку как Text
char 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(const char* str, char div = ';');
Splitter spl(String& str, char div = ';'); // авто-размер (выделяется в heap)
Splitter spl(const char* str, char div = ';'); // авто-размер (выделяется в heap)
void setDiv(char div); // установить разделитель
void restore(); // восстановить строку (вернуть разделители)
uint8_t length(); // количество подстрок
const char* str(uint16_t idx); // получить подстроку по индексу
Text get(uint16_t idx); // получить подстроку по индексу как Text
char 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_t su::list::length(Text list, char div = ';');
// Получить индекс подстроки в списке
int16_t su::list::indexOf(Text list, Text str, char div = ';');
// Проверка содержит ли список подстроку
bool su::list::includes(Text list, Text str, char div = ';');
// Получить подстроку из списка по индексу
Text su::list::get(Text list, uint16_t idx, char div = ';');
// распарсить в массив указанного типа и размера. Вернёт количество записанных подстрок
template <typename T>
uint16_t su::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);
// установить разделитель
void setDiv(char div);
// получить размер списка
uint16_t length();
// получить индекс подстроки в списке или -1 если её нет
int16_t indexOf(Text str);
// проверить наличие подстроки в списке
bool includes(Text str);
// получить подстроку под индексом
Text get(uint16_t idx);
// распарсить в массив указанного типа и размера. Вернёт количество записанных подстрок
template <typename T>
uint16_t parse(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(const char* ru, char* qw);
// размер закодированных данных по размеру исходных
size_t encodedLen(size_t len);
// будущий размер декодированных данных по строке b64 и её длине
size_t decodedLen(const char* b64, size_t len);
// закодировать данные в String
size_t encode(String* b64, uint8_t* data, size_t len, bool pgm = false);
// закодировать данные в char[] (библиотека не добавляет '\0' в конец)
size_t encode(char* b64, uint8_t* data, size_t len, bool pgm = false);
// раскодировать данные из строки b64 длиной len в буфер data
size_t decode(uint8_t* data, const char* b64, size_t len);
// раскодировать данные из строки b64 длиной len в саму себя, добавит 0 на конце
size_t decode(char* b64, size_t len);
// раскодировать данные из строки b64 в буфер data
size_t decode(uint8_t* data, const String& b64);
Декодер строки, содержащей unicode символы вида \u0abc
. Также делает unescape символов \t\r\n
!
// декодировать строку.Зарезервировать строку на длину len. Иначе - по длине строки
String su::unicode::decode(const char* str, uint16_t len = 0);
// декодировать строку
String su::unicode::decode(const String& str);
// кодировать unicode символ по его коду. В массиве должно быть 5 ячеек
void su::unicode::encode(char* str, uint32_t c);
// кодировать unicode символ по его коду
String su::unicode::encode(uint32_t code);
// символ должен быть urlencoded
bool needsEncode(char c);
// закодировать в url. Можно указать len = 0, если неизвестна
void encode(const char* src, uint16_t len, String& dest);
// закодировать в url
void encode(const String& src, String& dest);
// закодировать в url
String encode(const String& src);
// раскодировать url
size_t decode(char* dest, const char* url, uint16_t len = 0);
// раскодировать url саму в себя
size_t decode(char* url, uint16_t len = 0);
// раскодировать url
void decode(const char* src, uint16_t len, String& dest);
// раскодировать url
String decode(const char* src, uint16_t len);
// раскодировать url
void decode(const String& src, String& dest);
// раскодировать url
String decode(const String& src);
// StringLength длина строки, выполняется на этапе компиляции
constexpr size_t su::SL(const char* str);
constexpr size_t operator"" _SL; // C++ 11
Примеры
int L1 = su:SL("text");
int L2 = "text"_SL;
Вместо сравнения строк можно сравнивать хэш этих строк, что делает программу компактнее, легче и в большинстве случаев быстрее. Функции, указанные ниже как "считается компилятором" - считаются компилятором, то есть строка даже не попадает в код программы - вместо неё подставляется хэш-число:
// считается компилятором
constexpr size_t su::SH(const char* str); // (String Hash) размер size_t
constexpr size_t SH32(const char* str); // (String Hash) размер 32 бит
constexpr size_t operator"" _h; // C++ 11
constexpr size_t operator"" _h32; // C++ 11
// считается в рантайме
size_t su::hash(const char* str, int16_t len = -1); // Размер зависит от платформы и соответствует size_t
uint32_t su::hash32(const char* str, int16_t len = -1); // Размер 32 бит
size_t su::hash_P(PGM_P str, int16_t len = -1); // PROGMEM строка, размер size_t
uint32_t su::hash32_P(PGM_P str, int16_t len = -1); // PROGMEM строка, размер 32 бит
На 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);
else if (!strcmp_P(buf, PSTR("12345"))) Serial.println(1);
else if (!strcmp_P(buf, PSTR("wrong text"))) Serial.println(2);
else if (!strcmp_P(buf, PSTR("some text"))) Serial.println(3);
else if (!strcmp_P(buf, PSTR("hello"))) Serial.println(4);
else if (!strcmp_P(buf, PSTR("some_text"))) Serial.println(5);
Способ с хэшем строки:
using su::SH;
using su::hash;
char buf[] = "some_text";
switch (hash(buf)) {
case SH("abcdef"): Serial.println(0); break;
case SH("12345"): Serial.println(1); break;
case SH("wrong text"): Serial.println(2); break;
case "some text"_h: Serial.println(3); break;
case "hello"_h: Serial.println(4); break;
case "some_text"_h: Serial.println(5); break;
}
Один расчёт хэша занимает чуть большее время, чем сравнение со строкой. Но итоговая конструкция из примера выполняется в 2 раза быстрее (на ESP).
SH("строки")
и"строки"_h
в данном примере вообще не попадают в код программы - вместо них подставляется их хэш
// Длина строки с русскими символами
uint16_t su::strlenRu(const char* str);
// Получить длину целого числа
uint8_t su::intLen(int32_t val);
// Получить длину float числа
uint8_t su::floatLen(double val, uint8_t dec);
// Преобразовать строку в целое число
template <typename T>
T su::strToInt(const char* str, uint8_t len = 0);
// Преобразовать PROGMEM строку в целое число
template <typename T>
T su::strToInt_P(const char* str, uint8_t len = 0);
// Преобразовать float в строку с указанием кол-ва знаков после точки
uint8_t su::floatToStr(double val, char* buf, uint8_t dec = 2);
// Преобразовать HEX строку в целое число. Опционально длина
uint32_t su::strToIntHex(const char* str, int8_t len = -1);
// Длина символа в количестве байт
uint8_t su::charSize(char sym);
// Конвертация числа в char* массив. Пишет от начала массива, добавляет '\0', вернёт длину строки
// для int64 макс. длина буфера - 22 символа, для int32 - 12
uint8_t su::uintToStr(uint32_t n, char* buf, uint8_t base = DEC);
uint8_t su::intToStr(int32_t n, char* buf, uint8_t base = DEC);
uint8_t su::uint64ToStr(uint64_t n, char* buf, uint8_t base = DEC);
uint8_t su::int64ToStr(int64_t n, char* buf, uint8_t base = DEC);
// конвертация из строки во float
float su::strToFloat(const char* s);
// конвертация из PROGEMEM строки во float
float su::strToFloat_P(PGM_P s);
// быстрый целочисленный логарифм 10 (длина числа в кол-ве символов)
uint8_t su::getLog10(uint32_t value);
uint8_t su::getLog10(int32_t value);
// быстрое возведение 10 в степень
uint32_t su::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
- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код