2022-12-26 00:34:48 -05:00
|
|
|
/*
|
|
|
|
* Created by Brett on 26/12/22.
|
|
|
|
* Licensed under GNU General Public License V3.0
|
|
|
|
* See LICENSE file for license detail
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef BLT_STRING_H
|
|
|
|
#define BLT_STRING_H
|
|
|
|
|
2023-01-22 18:08:50 -05:00
|
|
|
#include <iostream>
|
2022-12-26 00:34:48 -05:00
|
|
|
#include <string>
|
2023-12-20 14:57:17 -05:00
|
|
|
#include <string_view>
|
2022-12-26 00:34:48 -05:00
|
|
|
#include <sstream>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
2023-11-30 19:56:45 -05:00
|
|
|
#include <optional>
|
2023-10-14 17:05:51 -04:00
|
|
|
#include <cctype>
|
2023-11-30 19:56:45 -05:00
|
|
|
#include <unordered_set>
|
2023-10-27 00:00:01 -04:00
|
|
|
#include <blt/compatibility.h>
|
2023-10-25 14:30:24 -04:00
|
|
|
|
2023-10-14 17:05:51 -04:00
|
|
|
namespace blt::string
|
|
|
|
{
|
2023-01-22 17:54:24 -05:00
|
|
|
|
2023-10-14 17:05:51 -04:00
|
|
|
class StringBuffer
|
|
|
|
{
|
2023-07-10 18:45:43 -04:00
|
|
|
private:
|
|
|
|
const size_t BLOCK_SIZE = 4096;
|
|
|
|
size_t front = 0;
|
|
|
|
size_t size = 0;
|
|
|
|
char* characterBuffer = nullptr;
|
|
|
|
|
|
|
|
void expand();
|
2023-10-14 17:05:51 -04:00
|
|
|
|
2023-07-10 18:45:43 -04:00
|
|
|
public:
|
|
|
|
void trim();
|
2023-10-14 17:05:51 -04:00
|
|
|
|
2023-07-10 18:55:20 -04:00
|
|
|
std::string str();
|
2023-07-10 18:45:43 -04:00
|
|
|
|
2023-10-14 17:05:51 -04:00
|
|
|
StringBuffer()
|
|
|
|
{
|
2023-07-10 18:45:43 -04:00
|
|
|
characterBuffer = static_cast<char*>(malloc(BLOCK_SIZE));
|
|
|
|
size = BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuffer& operator<<(char c);
|
2023-10-14 17:05:51 -04:00
|
|
|
|
|
|
|
StringBuffer& operator<<(const std::string& str)
|
|
|
|
{
|
2023-07-10 19:08:23 -04:00
|
|
|
for (char c : str)
|
2023-07-11 17:50:40 -04:00
|
|
|
*this << c;
|
2023-07-10 19:08:23 -04:00
|
|
|
return *this;
|
|
|
|
}
|
2023-07-10 18:45:43 -04:00
|
|
|
|
2023-07-10 18:54:04 -04:00
|
|
|
template<typename T>
|
2023-10-14 17:05:51 -04:00
|
|
|
inline StringBuffer& operator<<(T t)
|
|
|
|
{
|
2023-07-10 19:08:23 -04:00
|
|
|
*this << std::to_string(t);
|
2023-07-10 18:54:04 -04:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2023-10-14 17:05:51 -04:00
|
|
|
~StringBuffer()
|
|
|
|
{
|
2023-07-10 18:45:43 -04:00
|
|
|
free(characterBuffer);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool starts_with(std::string_view string, std::string_view search)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-11-02 16:02:40 -04:00
|
|
|
#ifdef BLT_USE_CPP20
|
|
|
|
return string.starts_with(search);
|
|
|
|
#else
|
2023-01-22 17:54:24 -05:00
|
|
|
if (search.length() > string.length())
|
|
|
|
return false;
|
2023-10-14 17:05:51 -04:00
|
|
|
for (unsigned int i = 0; i < search.length(); i++)
|
|
|
|
{
|
2023-12-20 14:57:17 -05:00
|
|
|
if (string[i] != search[i])
|
2023-01-22 17:54:24 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-11-02 16:02:40 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool starts_with(std::string_view string, char search)
|
2023-11-02 16:02:40 -04:00
|
|
|
{
|
|
|
|
#ifdef BLT_USE_CPP20
|
|
|
|
return string.starts_with(search);
|
|
|
|
#else
|
|
|
|
if (string.empty())
|
|
|
|
return false;
|
2023-12-20 14:57:17 -05:00
|
|
|
return string.front() == search;
|
2023-11-02 16:02:40 -04:00
|
|
|
#endif
|
2023-01-22 17:54:24 -05:00
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool ends_with(std::string_view string, std::string_view search)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-11-02 16:02:40 -04:00
|
|
|
#ifdef BLT_USE_CPP20
|
|
|
|
return string.ends_with(search);
|
|
|
|
#else
|
2023-01-22 17:54:24 -05:00
|
|
|
if (search.length() > string.length())
|
|
|
|
return false;
|
|
|
|
auto startPosition = string.length() - search.length();
|
2023-10-14 17:05:51 -04:00
|
|
|
for (unsigned int i = 0; i < search.length(); i++)
|
|
|
|
{
|
2023-12-20 14:57:17 -05:00
|
|
|
if (string[startPosition + i] != search[i])
|
2023-01-22 17:54:24 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-11-02 16:02:40 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool ends_with(std::string_view string, char search)
|
2023-11-02 16:02:40 -04:00
|
|
|
{
|
|
|
|
#ifdef BLT_USE_CPP20
|
|
|
|
return string.ends_with(search);
|
|
|
|
#else
|
|
|
|
if (string.empty())
|
|
|
|
return false;
|
2023-12-20 14:57:17 -05:00
|
|
|
return string.back() == search;
|
2023-11-02 16:02:40 -04:00
|
|
|
#endif
|
2023-01-22 17:54:24 -05:00
|
|
|
}
|
2023-01-22 18:08:50 -05:00
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline std::optional<std::vector<size_t>> containsAll(std::string_view string, const std::unordered_set<char>& search)
|
2023-11-30 19:56:45 -05:00
|
|
|
{
|
|
|
|
std::vector<size_t> pos;
|
|
|
|
for (size_t i = 0; i < string.length(); i++)
|
|
|
|
{
|
|
|
|
if (BLT_CONTAINS(search, string[i]))
|
|
|
|
pos.push_back(i);
|
|
|
|
}
|
|
|
|
if (!pos.empty())
|
|
|
|
return pos;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline size_t contains(std::string_view string, const std::unordered_set<char>& search)
|
2023-11-30 19:56:45 -05:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < string.length(); i++)
|
|
|
|
{
|
|
|
|
if (BLT_CONTAINS(search, string[i]))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool contains(std::string_view string, const char search)
|
2023-10-14 18:35:11 -04:00
|
|
|
{
|
2023-10-26 16:27:39 -04:00
|
|
|
#if __cplusplus >= 202002L
|
2023-10-14 18:36:32 -04:00
|
|
|
return std::ranges::any_of(string, [search](const char c) -> bool {
|
2023-10-14 18:35:11 -04:00
|
|
|
return c == search;
|
|
|
|
});
|
2023-10-26 16:27:39 -04:00
|
|
|
#else
|
2023-11-02 16:02:40 -04:00
|
|
|
for (const char c : string)
|
|
|
|
{
|
2023-10-26 16:27:39 -04:00
|
|
|
if (c == search)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
#endif
|
2023-10-14 18:35:11 -04:00
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool contains(std::string_view string, std::string_view search)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-22 18:08:50 -05:00
|
|
|
if (search.length() > string.length())
|
|
|
|
return false;
|
2023-10-14 17:05:51 -04:00
|
|
|
for (unsigned int i = 0; i < string.length(); i++)
|
|
|
|
{
|
2023-12-20 14:57:17 -05:00
|
|
|
if (string[i] == search[0])
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-22 18:08:50 -05:00
|
|
|
bool correct = true;
|
2023-10-14 17:05:51 -04:00
|
|
|
for (unsigned int j = 0; j < search.length(); j++)
|
|
|
|
{
|
2023-12-20 14:57:17 -05:00
|
|
|
if (string[i + j] != search[j])
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-22 18:08:50 -05:00
|
|
|
correct = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (correct)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2023-10-14 17:05:51 -04:00
|
|
|
|
2023-01-05 01:52:56 -05:00
|
|
|
/**
|
|
|
|
* Converts the string into lower case
|
|
|
|
* @param s string to lower case
|
|
|
|
* @return a string copy that is all lower case
|
|
|
|
*/
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string toLowerCase(std::string_view s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-10-25 14:30:24 -04:00
|
|
|
std::string str;
|
2023-01-05 01:52:56 -05:00
|
|
|
std::for_each(
|
|
|
|
s.begin(), s.end(), [&str](unsigned char ch) {
|
2023-10-25 14:30:24 -04:00
|
|
|
str += (char) std::tolower(ch);
|
2023-01-05 01:52:56 -05:00
|
|
|
}
|
|
|
|
);
|
2023-10-25 14:30:24 -04:00
|
|
|
return str;
|
2023-01-05 01:52:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the string into upper case
|
|
|
|
* @param s string to upper case
|
|
|
|
* @return a string copy that is all upper case
|
|
|
|
*/
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string toUpperCase(std::string_view s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-10-25 14:30:24 -04:00
|
|
|
std::string str;
|
2023-01-05 01:52:56 -05:00
|
|
|
std::for_each(
|
|
|
|
s.begin(), s.end(), [&str](unsigned char ch) {
|
2023-10-25 14:30:24 -04:00
|
|
|
str += (char) std::toupper(ch);
|
2023-01-05 01:52:56 -05:00
|
|
|
}
|
|
|
|
);
|
2023-10-25 14:30:24 -04:00
|
|
|
return str;
|
2023-01-05 01:52:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// taken from https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
|
|
|
|
// extended to return a vector
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::vector<std::string> split(std::string s, std::string_view delim)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
size_t pos = 0;
|
|
|
|
std::vector<std::string> tokens;
|
2023-10-14 17:05:51 -04:00
|
|
|
while ((pos = s.find(delim)) != std::string::npos)
|
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
auto token = s.substr(0, pos);
|
|
|
|
tokens.push_back(token);
|
|
|
|
s.erase(0, pos + delim.length());
|
|
|
|
}
|
2023-12-20 14:57:17 -05:00
|
|
|
tokens.push_back(std::move(s));
|
2023-01-05 01:52:56 -05:00
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::vector<std::string> split(std::string s, char delim)
|
2023-10-15 16:43:20 -04:00
|
|
|
{
|
|
|
|
size_t pos = 0;
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
while ((pos = s.find(delim)) != std::string::npos)
|
|
|
|
{
|
|
|
|
auto token = s.substr(0, pos);
|
|
|
|
tokens.push_back(token);
|
|
|
|
s.erase(0, pos + 1);
|
|
|
|
}
|
|
|
|
tokens.push_back(s);
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
2023-08-02 14:00:11 -04:00
|
|
|
// https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool replace(std::string& str, std::string_view from, std::string_view to)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-08-02 14:00:11 -04:00
|
|
|
size_t start_pos = str.find(from);
|
2023-10-14 17:05:51 -04:00
|
|
|
if (start_pos == std::string::npos)
|
2023-08-02 14:00:11 -04:00
|
|
|
return false;
|
|
|
|
str.replace(start_pos, from.length(), to);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-12-20 14:57:17 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR void replaceAll(std::string& str, std::string_view from, std::string_view to)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
|
|
|
if (from.empty())
|
2023-08-02 14:00:11 -04:00
|
|
|
return;
|
|
|
|
size_t start_pos = 0;
|
2023-10-14 17:05:51 -04:00
|
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
|
|
|
{
|
2023-08-02 14:00:11 -04:00
|
|
|
str.replace(start_pos, from.length(), to);
|
|
|
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-05 01:52:56 -05:00
|
|
|
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
|
|
|
// would've preferred to use boost lib but instructions said to avoid external libs
|
|
|
|
// trim from start (in place)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string& ltrim(std::string& s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
s.erase(
|
|
|
|
s.begin(), std::find_if(
|
|
|
|
s.begin(), s.end(), [](unsigned char ch) {
|
2023-01-24 17:56:48 -05:00
|
|
|
return !std::isblank(ch);
|
2022-12-26 00:34:48 -05:00
|
|
|
}
|
2023-01-05 01:52:56 -05:00
|
|
|
));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from end (in place)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string& rtrim(std::string& s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
s.erase(
|
|
|
|
std::find_if(
|
|
|
|
s.rbegin(), s.rend(), [](unsigned char ch) {
|
2023-01-24 17:56:48 -05:00
|
|
|
return !std::isblank(ch);
|
2022-12-26 00:34:48 -05:00
|
|
|
}
|
2023-01-05 01:52:56 -05:00
|
|
|
).base(), s.end());
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2024-01-08 22:08:48 -05:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string_view ltrim(std::string_view s)
|
|
|
|
{
|
|
|
|
size_t start_pos = 0;
|
|
|
|
for (auto c = s.begin(); c != s.end() && std::isblank(*c); ++c, start_pos++);
|
|
|
|
return s.substr(start_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string_view rtrim(std::string_view s)
|
|
|
|
{
|
|
|
|
size_t end_pos = 0;
|
|
|
|
for (auto c = s.rbegin(); c != s.rend() && std::isblank(*c); ++c, end_pos++);
|
|
|
|
return s.substr(0, s.size() - end_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string_view trim(std::string_view s)
|
|
|
|
{
|
|
|
|
size_t start_pos = 0;
|
|
|
|
for (auto c = s.begin(); c != s.end() && std::isblank(*c); ++c, start_pos++);
|
|
|
|
size_t end_pos = s.size();
|
|
|
|
for (auto c = s.rbegin(); c != s.rend() && std::isblank(*c); ++c, end_pos--);
|
|
|
|
return s.substr(start_pos, end_pos - start_pos);
|
|
|
|
}
|
|
|
|
|
2023-01-05 01:52:56 -05:00
|
|
|
// trim from both ends (in place)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string& trim(std::string& s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
ltrim(s);
|
|
|
|
rtrim(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from start (copying)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string ltrim_copy(std::string s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
ltrim(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from end (copying)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string rtrim_copy(std::string s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
rtrim(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from both ends (copying)
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR std::string trim_copy(std::string s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-01-05 01:52:56 -05:00
|
|
|
trim(s);
|
|
|
|
return s;
|
|
|
|
}
|
2023-07-10 18:45:43 -04:00
|
|
|
|
2023-10-25 14:30:24 -04:00
|
|
|
static inline BLT_CPP20_CONSTEXPR bool is_numeric(const std::string& s)
|
2023-10-14 17:05:51 -04:00
|
|
|
{
|
2023-10-26 16:27:39 -04:00
|
|
|
#if __cplusplus >= 202002L
|
2023-10-14 17:05:51 -04:00
|
|
|
return std::ranges::all_of(s, [](char c) -> bool {
|
|
|
|
return std::isdigit(c);
|
|
|
|
});
|
2023-10-26 16:27:39 -04:00
|
|
|
#else
|
2023-11-02 16:02:40 -04:00
|
|
|
for (const char c : s)
|
|
|
|
{
|
2023-10-26 16:27:39 -04:00
|
|
|
if (!std::isdigit(c))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
#endif
|
2023-10-14 17:05:51 -04:00
|
|
|
}
|
|
|
|
|
2022-12-26 00:34:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif //BLT_STRING_H
|