OmniaFramework/src/ostd/string/String.cpp

661 lines
14 KiB
C++

#include "String.hpp"
#include <sstream>
#include <algorithm>
#include <iostream>
#include <bitset>
#include <boost/regex.hpp>
namespace ostd
{
String String::Tokens::next(void)
{
if (!hasNext()) return ""; //TODO: Error
return m_tokens[m_current_index++];
}
String String::Tokens::previous(void)
{
if (!hasPrevious()) return ""; //TODO: Error
return m_tokens[--m_current_index];
}
String& String::ltrim(void)
{
m_data.erase(m_data.begin(), std::find_if(m_data.begin(), m_data.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
return *this;
}
String& String::rtrim(void)
{
m_data.erase(std::find_if(m_data.rbegin(), m_data.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), m_data.end());
return *this;
}
String& String::trim(void)
{
return ltrim().rtrim();
}
String& String::toLower(void)
{
std::transform(m_data.begin(), m_data.end(), m_data.begin(), [](unsigned char c){ return std::tolower(c); });
return *this;
}
String& String::toUpper(void)
{
std::transform(m_data.begin(), m_data.end(), m_data.begin(), [](unsigned char c){ return std::toupper(c); });
return *this;
}
String& String::addPadding(uint32_t new_string_length, char c, ePaddingBehavior padding_behavior)
{
if (len() - 1 >= new_string_length) return *this;
uint32_t diff = new_string_length - len();
int32_t leftPadding = 0;
int32_t rightPadding = 0;
switch (padding_behavior)
{
case ePaddingBehavior::ForceEvenNegative:
if (diff % 2 != 0)
diff--;
leftPadding = rightPadding = (diff / 2);
break;
case ePaddingBehavior::ForceEvenPositive:
if (diff % 2 != 0)
diff++;
leftPadding = rightPadding = (diff / 2);
break;
case ePaddingBehavior::AllowOddExtraLeft:
if (diff % 2 != 0)
{
diff--;
leftPadding = rightPadding = (diff / 2);
leftPadding++;
}
else
leftPadding = rightPadding = (diff / 2);
break;
case ePaddingBehavior::AllowOddExtraRight:
if (diff % 2 != 0)
{
diff--;
leftPadding = rightPadding = (diff / 2);
rightPadding++;
}
else
leftPadding = rightPadding = (diff / 2);
break;
}
if (leftPadding < 0 || rightPadding < 0) return *this;
return addLeftPadding(len() + leftPadding, c).addRightPadding(len() + rightPadding, c);
}
String& String::addLeftPadding(uint32_t new_string_length, char c)
{
reverse();
while (len() < new_string_length)
m_data += c;
reverse();
return *this;
}
String& String::addRightPadding(uint32_t new_string_length, char c)
{
while (len() < new_string_length)
m_data += c;
return *this;
}
String& String::reverse(void)
{
std::reverse(m_data.begin(), m_data.end());
return *this;
}
String& String::replaceAll(const String& what, const String& with)
{
while (contains(what))
replaceFirst(what, with);
return *this;
}
String& String::replaceFirst(const String& what, const String& with)
{
int32_t index = indexOf(what);
if (index == -1) return *this;
m_data.replace(index, what.len(), with.cpp_str());
return *this;
}
String& String::regexReplace(const String& regex_pattern, const String& replace_with, bool case_insensitive)
{
try
{
boost::regex rgx(regex_pattern.cpp_str(), (case_insensitive ? boost::regex_constants::icase : boost::regex_constants::normal));
m_data = boost::regex_replace(m_data, rgx, replace_with.cpp_str());
return *this;
}
catch(const boost::regex_error& err)
{
std::cerr << err.what() << '\n'; //TODO: Better error handling
return *this;
}
return *this;
}
String& String::put(uint32_t index, char c)
{
if (index < m_data.length())
m_data[index] = c;
return *this;
}
String& String::substr(uint32_t start, int32_t end)
{
if (end < 0) m_data = m_data.substr(start);
else m_data = m_data.substr(start, end - start);
return *this;
}
String& String::fixedLength(uint32_t length, char fill_character, const String& truncate_indicator)
{
if (length < truncate_indicator.len()) return *this;
if (len() > length)
{
int32_t tr_len = truncate_indicator.len();
substr(0, length - tr_len);
return add(truncate_indicator);
}
else if (len() < length)
return addRightPadding(length, fill_character);
return *this;
}
String& String::addChar(char c)
{
m_data += c;
return *this;
}
String& String::add(const String& se)
{
m_data += se.cpp_str();
return *this;
}
String& String::add(uint8_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(int8_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(uint16_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(int16_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(uint32_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(int32_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(uint64_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(int64_t i)
{
m_data += std::to_string(i);
return *this;
}
String& String::add(float f, uint8_t precision)
{
std::stringstream stream;
if (precision != 0)
stream << std::fixed << std::setprecision(precision) << f;
else
stream << f;
cpp_string s = stream.str();
return add(s);
}
String& String::add(double f, uint8_t precision)
{
std::stringstream stream;
if (precision != 0)
stream << std::fixed << std::setprecision(precision) << f;
else
stream << f;
cpp_string s = stream.str();
return add(s);
}
//New String
String String::new_ltrim(void) const
{
String __str = m_data;
return __str.ltrim();
}
String String::new_rtrim(void) const
{
String __str = m_data;
return __str.rtrim();
}
String String::new_trim(void) const
{
String __str = m_data;
return __str.trim();
}
String String::new_toLower(void) const
{
String __str = m_data;
return __str.toLower();
}
String String::new_toUpper(void) const
{
String __str = m_data;
return __str.toUpper();
}
String String::new_addPadding(uint32_t new_string_length, char c, ePaddingBehavior padding_behavior) const
{
String __str = m_data;
return __str.addPadding(new_string_length, c, padding_behavior);
}
String String::new_addLeftPadding(uint32_t new_string_length, char c) const
{
String __str = m_data;
return __str.addLeftPadding(new_string_length, c);
}
String String::new_addRightPadding(uint32_t new_string_length, char c) const
{
String __str = m_data;
return __str.addRightPadding(new_string_length, c);
}
String String::new_reverse(void) const
{
String __str = m_data;
return __str.reverse();
}
String String::new_replaceAll(const String& what, const String& with) const
{
String __str = m_data;
return __str.replaceAll(what, with);
}
String String::new_replaceFirst(const String& what, const String& with) const
{
String __str = m_data;
return __str.replaceFirst(what, with);
}
String String::new_regexReplace(const String& regex_pattern, const String& replace_with, bool case_insensitive) const
{
String __str = m_data;
return __str.regexReplace(regex_pattern, replace_with, case_insensitive);
}
String String::new_put(uint32_t index, char c) const
{
String __str = m_data;
return __str.put(index, c);
}
String String::new_substr(uint32_t start, int32_t end) const
{
String __str = m_data;
return __str.substr(start, end);
}
String String::new_fixedLength(uint32_t length, char fill_character, const String& truncate_indicator) const
{
String __str = m_data;
return __str.fixedLength(length, fill_character, truncate_indicator);
}
String String::new_addChar(char c) const
{
String __str = m_data;
return __str.addChar(c);
}
String String::new_add(const String& se) const
{
String __str = m_data;
return __str.add(se);
}
String String::new_add(uint8_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(int8_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(uint16_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(int16_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(uint32_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(int32_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(uint64_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(int64_t i) const
{
String __str = m_data;
return __str.add(i);
}
String String::new_add(float f, uint8_t precision) const
{
String __str = m_data;
return __str.add(f, precision);
}
String String::new_add(double f, uint8_t precision) const
{
String __str = m_data;
return __str.add(f, precision);
}
//Utility
int64_t String::toInt(void) const
{
if (!isNumeric(false)) return 0;
ostd::String str = String(m_data).trim().toLower();
if (!str.isInt()) return 0;
int32_t base = 10;
if (str.cpp_str().rfind("0x", 0) == 0)
{
str = str.substr(2);
base = 16;
}
else if (str.cpp_str().rfind("0b", 0) == 0)
{
str = str.substr(2);
base = 2;
}
return strtoul(str.c_str(), NULL, base);
}
float String::toFloat(void) const
{
if (!isNumeric(true)) return 0;
return std::stof(m_data);
}
double String::toDouble(void) const
{
if (!isNumeric(true)) return 0;
return std::stod(m_data);
}
bool String::isNumeric(bool decimal) const
{
if (decimal)
{
std::istringstream iss(m_data);
double f;
iss >> std::noskipws >> f;
return iss.eof() && !iss.fail();
}
return isInt();
}
bool String::isInt(void) const
{
ostd::String str = String(m_data).trim().toLower();
bool isNumber = std::ranges::all_of(str.begin(), str.end(), [](char c){ return isdigit(c) != 0; });
return str.isHex() || str.isBin() || isNumber;
}
bool String::isHex(void) const
{
ostd::String hex = String(m_data).trim().toLower();
return hex.cpp_str().compare(0, 2, "0x") == 0 && hex.cpp_str().size() > 2 && hex.cpp_str().find_first_not_of("0123456789abcdef", 2) == std::string::npos;
}
bool String::isBin(void) const
{
ostd::String bin = String(m_data).trim().toLower();
return bin.cpp_str().compare(0, 2, "0b") == 0 && bin.cpp_str().size() > 2 && bin.cpp_str().find_first_not_of("01", 2) == std::string::npos;
}
bool String::contains(char c) const
{
return m_data.find(c) != std::string::npos;
}
bool String::contains(const String& str) const
{
return m_data.find(str.cpp_str()) != std::string::npos;
}
bool String::startsWith(const String& str) const
{
return m_data.starts_with(str);
}
bool String::endsWith(const String& str) const
{
return m_data.ends_with(str);
}
bool String::regexMatches(const String& regex_pattern, bool case_insensitive) const
{
try
{
boost::regex rgx(regex_pattern.cpp_str(), case_insensitive ? boost::regex_constants::icase : boost::regex_constants::normal);
return boost::regex_search(m_data, rgx);
}
catch (const boost::regex_error& err)
{
std::cerr << err.what() << '\n'; //TODO: Better error handling
return false;
}
}
uint32_t String::count(const String& str) const
{
Tokens tok = tokenize(str, false, true);
if (tok.count() < 1) return 0;
return tok.count() - 1;
}
int32_t String::indexOf(char c, uint32_t start) const
{
cpp_string cc = "";
cc += c;
int32_t pos = m_data.find(cc.c_str(), start);
if (pos == std::string::npos) return -1;
return pos;
}
int32_t String::indexOf(const String& str, uint32_t start) const
{
int32_t pos = m_data.find(str.c_str(), start);
if (pos == std::string::npos) return -1;
return pos;
}
int32_t String::lastIndexOf(char c) const
{
String se(m_data);
se.reverse();
int32_t pos = se.indexOf(c);
if (pos < 0) return -1;
return len() - pos - 1;
}
int32_t String::lastIndexOf(const String& str) const
{
String se(m_data);
se.reverse();
String se2(str);
int32_t pos = se.indexOf(se2.new_reverse());
if (pos < 0) return -1;
return len() - pos - str.len();
}
String::Tokens String::tokenize(const String& delimiter, bool trim_tokens, bool allow_white_space_only_tokens) const
{
Tokens tokens;
int32_t sindex = 0;
int32_t eindex = 0;
String __token = "";
while ((eindex = indexOf(delimiter, sindex)) != -1)
{
__token = new_substr(sindex, eindex);
if (trim_tokens)
{
__token.trim();
if (__token != "")
tokens.m_tokens.push_back(__token.cpp_str());
}
else
{
if (allow_white_space_only_tokens && __token != "")
tokens.m_tokens.push_back(__token.cpp_str());
else if (!allow_white_space_only_tokens && __token.new_trim() != "")
tokens.m_tokens.push_back(__token.cpp_str());
}
sindex = eindex + delimiter.len();
}
__token = new_substr(sindex);
if (trim_tokens)
{
__token.trim();
if (__token != "")
tokens.m_tokens.push_back(__token.cpp_str());
}
else
{
if (allow_white_space_only_tokens && __token != "")
tokens.m_tokens.push_back(__token.cpp_str());
else if (!allow_white_space_only_tokens && __token.new_trim() != "")
tokens.m_tokens.push_back(__token.cpp_str());
}
return tokens;
}
String String::getHexStr(uint64_t value, bool prefix, uint8_t nbytes)
{
union {
uint64_t val;
uint8_t bytes[8];
} __tmp_editor;
__tmp_editor.val = value;
if (nbytes < 1 || nbytes > 8) nbytes = 1;
std::ostringstream oss;
if (prefix) oss << "0x";
for (int8_t b = nbytes - 1; b >= 0; b--)
oss << std::setw(2) << std::setfill('0') << std::uppercase << std::hex << (int)__tmp_editor.bytes[b];
return oss.str();
}
String String::getBinStr(uint64_t value, bool prefix, uint8_t nbytes)
{
union {
uint64_t val;
uint8_t bytes[8];
} __tmp_editor;
__tmp_editor.val = value;
if (nbytes < 1 || nbytes > 8) nbytes = 1;
std::ostringstream oss;
if (prefix) oss << "0b ";
for (int8_t b = nbytes - 1; b >= 0; b--)
oss << std::bitset<8>((char)__tmp_editor.bytes[b]) << " ";
return oss.str();
}
String String::duplicateChar(unsigned char c, uint16_t count)
{
String str = "";
for (uint16_t i = 0; i < count; i++)
str = str += c;
return str;
}
String operator+(const cpp_string& str1, const String& str)
{
return String(str1) + str;
}
std::ostream& operator<<(std::ostream& out, const String& val)
{
out << val.cpp_str();
return out;
}
}