在上一篇文章中,介绍了outputString和inputString,其中所运用到的字符串类型String也是C++驱动层中功能强大且重要的一个类。这个类移植自Arduino的WString。这篇文章将会展示WString的易用性,并且从编译输出后程序大小的角度比较WSting和C++ std::string的区别,并且在后边还会有具体的移植步骤,最后还会附上全部的代码。
一、WString简单介绍
WString是Arduino自带的一个字符串类型,功能强大,可以实现字符串的拼接、拷贝、转换、比较等功能,是不可缺少的一项利器。熟悉Arduino的小伙伴应该对它已经非常熟悉了,下面再通过一段Arduino的代码来展示一下其功能。 关于WString的更多功能和用法可以浏览Arduino的官方文档
void setup() {
Serial.begin(115200);
String str1="hello ",str2="world";
String str=str1+str2;
Serial.print("str=str1+str2=");
Serial.println(str);
String str3="hello ";
if(str3==str1)
{
Serial.println("str3==str1");
}
int a=12345;
String str4="a=";
Serial.println(str4+a);
String str5="54321";
a=str5.toInt();
Serial.println(a);
}
void loop() {
}
二、为什么不用std::string
有的小伙伴可能会问,C++已经有了功能强大的std::string,可以在项目中直接使用为什么还要移植WString? 我们先来看看Arduino下使用std::string和WString生成的代码容量: 因为Arduino原生支持的板子不支持std::string,所以这里使用的板子是ESP32 首先是使用std::string:
#include<string>
void setup() {
std::string str = "hello wrold";
}
void loop() {
}
编译后输出为:项目使用了 249084 字节,占用了 (19%) 程序存储空间。最大为 1310720 字节。 全局变量使用了13412字节,(4%)的动态内存,余留314268字节局部变量。最大为327680字节。 再来看看使用WString:
void setup() {
String str = "hello wrold";
}
void loop() {
}
编译后输出为:项目使用了 197852 字节,占用了 (15%) 程序存储空间。最大为 1310720 字节。 全局变量使用了13084字节,(3%)的动态内存,余留314596字节局部变量。最大为327680字节。 通过比较后发现,使用std::string比使用WString多出了5k多的内存,这个代码的膨胀还是非常惊人的。
我们再来看看在Keil环境(处理器为STM32ZET6)下编译的代码的内存占用情况: 首先是std::string:
#include "User.h"
#include <string>
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
systick_init();
adc_init();
std::string str = "hello wrold";
while (1)
{
}
}
编译后输出为:Program Size: Code=15392 RO-data=1920 RW-data=148 ZI-data=63868
WString:
#include "User.h"
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
systick_init();
adc_init();
String str = "hello wrold";
while (1)
{
}
}
编译后输出为:Program Size: Code=2960 RO-data=336 RW-data=40 ZI-data=63832 对比两段代码的输出,不难发现std::string不管在Arduino下,还是在Keil下,内存膨胀都是非常严重的。 所以为了更高效率地运行字符串,一个高效、简洁地string是十分重要的。而目前网络上已经有了不少开源的String类,所以我们不需要再重复造轮子,移植就完事了!
三、移植WString
1.准备工作: 在移植之前,首先将之前几篇文章的代码全部放到相应的文件夹中。之前的文件我全都在文件头部加上了注释对文件进行解释和说明,这里我再进行一些总结: 每个文章最后附带的代码中,对应的文件名我都加到了代码的头部,例如my_usart.h: 在我专栏的博客下边附带的代码中: 文件名为 my_xxx.h、my_xxx.c、headfile.h、resources.h 都是C语言接口层的代码,放在项目文件夹 /Libraries/base_hardware/ 中。 上期讲到的outputStream和inputSream相关.h、.cpp文件放在 /Libraries/peripheral/ioBase/ 中 headfile.h是C语言接口层头文件包含文件,里面全部是 #include"my_xxx.h",各种自己建立一个包含一下头文件就好了,没啥技术含量就不发了 ∪・ω・∪ 本期文件附带的所有代码放在 /Libraries/other_support/ 中。 关于我的项目文件夹的结构和Keil文件管理请参考之前的文章:从新建工程开始使用C++开发单片机(以STM32为例):二、工程模板 工程文件整理好后就可以开始下一步的移植工作了。
2.文件移植: 接下来的步骤需要搜寻许多藏的很深的文件,并对其进行修改。如果需要一步一步自己移植的小伙伴请仔细阅读下面的内容;如果想要直接抱走代码的伙伴,我也把修改后的代码放在了文章的最后边。 首先,找到Arduino下WString.h和WString.cpp文件,这两个文件都在Arduino安装目录下的 /hardware/arduino/avr/cores/ 文件夹下可以找到。 我们将这两个文件复制到项目文件夹下的 /Libraries/other_support/ 中: 移动进去后,将WString.h中不相关的头文件去掉: 接着保存文件后,再Keil中尝试编译。编译过后,我们发现Keil报错输出了很多 “ultoa” is undefined 这类的错误: 这些 undefined 的函数是stdlib中的函数,而Keil的stdlib进行了删减,所有没有定义这些函数。如果在VSCode中,通过函数查找函数声明就可以在完整的stdlib.h中找到这些函数的声明。不过这对我们的移植工作没有帮助。 而在ESP8266和ESP32的Libraries,我们可以找到这样两个文件:stdlib_noniso.h和stdlib_noniso.c,乐鑫对他们的ESP芯片重写了那些缺少的函数,正好我们可以搬过来使用。这两个文件在 C:\Users\你的用户名\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6(或是其他版本)\cores\esp32 中可以找到。 这样所有需要的文件我们都有了,将他们都放在**/Libraries/other_support/中后,在WString.cpp中包含stdlib_noniso.h**重写尝试编译。 PS:考虑到有的小伙伴可能没有安装Aduino 或没有安装Arduinp ESP32/8266的开发环境,我会将修改好后的文件放在文章下边。
编译之后,我们还会发现,报出了许多 xxx_P 未定义的错误,这些xxx_P的定义就在我们之前去掉的 <avr/pgmspace.h>中,我们可以在Arduino安装目录下的 \hardware\tools\avr\avr\include\avr 中找到这个文件。因为这个文件包含了很多Arduino AVR单片机很多底层的东西,所以我们不会移植这些文件,只是找到相应的宏定义,将报错的部分一一修改回去就可以了: strcpy_P 改为 strcpy strlen_P 改为 strlen PGM_P 改为 const char *
再次编译过后,之剩只一个错误:Error: L6218E: Undefined symbol utoa (referred from wstring.o). 查看了stdlib_noniso.h和stdlib_noniso.c后会发现,utoa声明在了stdlib_noniso.h中,而在stdlib_noniso.c中没有进行定义。所以我们要在stdlib_noniso.c中添加utoa函数:
char *utoa(unsigned int val, char *s, int radix)
{
return ultoa(val, s, radix);
}
因为val时unsigned int类型,而ultoa的第一个参数时unsigned long 类型,所以将val传给ultoa不会因为强制类型转换而值溢出。 最后再进行一次编译,发现已经 0 Error(s) 0 Waring(s) 了!到此为止,我们的移植就算是完成了。最后让我们来测试一下效果, 测试代码用的还是开头WString简单介绍中的代码
#include "User.h"
HardwareUART Serial(UART_1);
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
systick_init();
adc_init();
Serial.begin(115200);
String str1 = "hello ", str2 = "world";
String str = str1 + str2;
Serial.print("str=str1+str2=");
Serial.println(str);
String str3 = "hello ";
if (str3 == str1)
{
Serial.println("str3==str1");
}
int a = 12345;
String str4 = "a=";
Serial.println(str4 + a);
String str5 = "54321";
a = str5.toInt();
Serial.println(a);
while (1)
{
}
}
和前面的输出一模一样呢! 3.关于代码版权的问题: 有的伙伴可能会担心搬运这么多代码,会不会收到律师函(划掉)?对于这个问题,大家可以放心地去学习和使用,本文所移植的所有文件都是允许修改和传播的。大家再使用的时候,保留源文件的版权声明就好了! 附:相应文件版权声明:
四、总结
在移植WString前,我也自己写过一个简单的String类并在项目中使用过,但用起来还是不如WString舒服。移植过程中遇到的许多困难在上面的步骤中都有展现过,虽然移植的过程看起来就是复制一下文件,修改一些代码就可以,但移植之前的准备工作中花了不少的精力去研究Arduino的继承结构,才能和自己的程序有比较好的兼容性。在上一篇文章和这篇文章中已经大量地使用了HardwareUART类来做数据的输出,有了String类地支持,我相信很多小伙伴已经可以在这基础上搓出一个自己的串口类。后面的文章我将会对HardwareUART的功能和设计进行解释。欢迎大家关注我哦~
本期表情包赞助:鹅厂QQ聊天框:
附:代码
#ifndef STDLIB_NONISO_H
#define STDLIB_NONISO_H
#ifdef __cplusplus
extern "C" {
#endif
int atoi(const char *s);
long atol(const char* s);
double atof(const char* s);
char* itoa (int val, char *s, int radix);
char* ltoa (long val, char *s, int radix);
char* utoa (unsigned int val, char *s, int radix);
char* ultoa (unsigned long val, char *s, int radix);
char* dtostrf (double val, signed char width, unsigned char prec, char *s);
#ifdef __cplusplus
}
#endif
#endif
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "stdlib_noniso.h"
void reverse(char *begin, char *end)
{
char *is = begin;
char *ie = end - 1;
while (is < ie)
{
char tmp = *ie;
*ie = *is;
*is = tmp;
++is;
--ie;
}
}
char *ltoa(long value, char *result, int base)
{
if (base < 2 || base > 16)
{
*result = 0;
return result;
}
char *out = result;
long quotient = abs(value);
do
{
const long tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while (quotient);
if (value < 0)
*out++ = '-';
reverse(result, out);
*out = 0;
return result;
}
char *ultoa(unsigned long value, char *result, int base)
{
if (base < 2 || base > 16)
{
*result = 0;
return result;
}
char *out = result;
unsigned long quotient = value;
do
{
const unsigned long tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while (quotient);
reverse(result, out);
*out = 0;
return result;
}
char *dtostrf(double number, signed char width, unsigned char prec, char *s)
{
bool negative = false;
if (isnan(number))
{
strcpy(s, "nan");
return s;
}
if (isinf(number))
{
strcpy(s, "inf");
return s;
}
char *out = s;
int fillme = width;
if (prec > 0)
{
fillme -= (prec + 1);
}
if (number < 0.0)
{
negative = true;
fillme--;
number = -number;
}
double rounding = 2.0;
for (uint8_t i = 0; i < prec; ++i)
rounding *= 10.0;
rounding = 1.0 / rounding;
number += rounding;
double tenpow = 1.0;
int digitcount = 1;
while (number >= 10.0 * tenpow)
{
tenpow *= 10.0;
digitcount++;
}
number /= tenpow;
fillme -= digitcount;
while (fillme-- > 0)
{
*out++ = ' ';
}
if (negative)
*out++ = '-';
digitcount += prec;
int8_t digit = 0;
while (digitcount-- > 0)
{
digit = (int8_t)number;
if (digit > 9)
digit = 9;
*out++ = (char)('0' | digit);
if ((digitcount == prec) && (prec > 0))
{
*out++ = '.';
}
number -= digit;
number *= 10.0;
}
*out = 0;
return s;
}
char *itoa(int val, char *s, int radix)
{
return ltoa(val, s, radix);
}
char *utoa(unsigned int val, char *s, int radix)
{
return ultoa(val, s, radix);
}
#ifndef String_class_h
#define String_class_h
#ifdef __cplusplus
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
class StringSumHelper;
class String
{
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const {}
public:
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String(String &&rval);
String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base=10);
explicit String(int, unsigned char base=10);
explicit String(unsigned int, unsigned char base=10);
explicit String(long, unsigned char base=10);
explicit String(unsigned long, unsigned char base=10);
explicit String(float, unsigned char decimalPlaces=2);
explicit String(double, unsigned char decimalPlaces=2);
~String(void);
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {return len;}
String & operator = (const String &rhs);
String & operator = (const char *cstr);
String & operator = (const __FlashStringHelper *str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & operator = (String &&rval);
String & operator = (StringSumHelper &&rval);
#endif
unsigned char concat(const String &str);
unsigned char concat(const char *cstr);
unsigned char concat(char c);
unsigned char concat(unsigned char c);
unsigned char concat(int num);
unsigned char concat(unsigned int num);
unsigned char concat(long num);
unsigned char concat(unsigned long num);
unsigned char concat(float num);
unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str);
String & operator += (const String &rhs) {concat(rhs); return (*this);}
String & operator += (const char *cstr) {concat(cstr); return (*this);}
String & operator += (char c) {concat(c); return (*this);}
String & operator += (unsigned char num) {concat(num); return (*this);}
String & operator += (int num) {concat(num); return (*this);}
String & operator += (unsigned int num) {concat(num); return (*this);}
String & operator += (long num) {concat(num); return (*this);}
String & operator += (unsigned long num) {concat(num); return (*this);}
String & operator += (float num) {concat(num); return (*this);}
String & operator += (double num) {concat(num); return (*this);}
String & operator += (const __FlashStringHelper *str){concat(str); return (*this);}
friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, float num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, double num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs);
operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }
int compareTo(const String &s) const;
unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) const;
unsigned char operator == (const String &rhs) const {return equals(rhs);}
unsigned char operator == (const char *cstr) const {return equals(cstr);}
unsigned char operator != (const String &rhs) const {return !equals(rhs);}
unsigned char operator != (const char *cstr) const {return !equals(cstr);}
unsigned char operator < (const String &rhs) const;
unsigned char operator > (const String &rhs) const;
unsigned char operator <= (const String &rhs) const;
unsigned char operator >= (const String &rhs) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char startsWith( const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c);
char operator [] (unsigned int index) const;
char& operator [] (unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
{ getBytes((unsigned char *)buf, bufsize, index); }
const char* c_str() const { return buffer; }
char* begin() { return buffer; }
char* end() { return buffer + length(); }
const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
int indexOf( char ch ) const;
int indexOf( char ch, unsigned int fromIndex ) const;
int indexOf( const String &str ) const;
int indexOf( const String &str, unsigned int fromIndex ) const;
int lastIndexOf( char ch ) const;
int lastIndexOf( char ch, unsigned int fromIndex ) const;
int lastIndexOf( const String &str ) const;
int lastIndexOf( const String &str, unsigned int fromIndex ) const;
String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); };
String substring( unsigned int beginIndex, unsigned int endIndex ) const;
void replace(char find, char replace);
void replace(const String& find, const String& replace);
void remove(unsigned int index);
void remove(unsigned int index, unsigned int count);
void toLowerCase(void);
void toUpperCase(void);
void trim(void);
long toInt(void) const;
float toFloat(void) const;
double toDouble(void) const;
protected:
char *buffer;
unsigned int capacity;
unsigned int len;
protected:
void init(void);
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void move(String &rhs);
#endif
};
class StringSumHelper : public String
{
public:
StringSumHelper(const String &s) : String(s) {}
StringSumHelper(const char *p) : String(p) {}
StringSumHelper(char c) : String(c) {}
StringSumHelper(unsigned char num) : String(num) {}
StringSumHelper(int num) : String(num) {}
StringSumHelper(unsigned int num) : String(num) {}
StringSumHelper(long num) : String(num) {}
StringSumHelper(unsigned long num) : String(num) {}
StringSumHelper(float num) : String(num) {}
StringSumHelper(double num) : String(num) {}
};
#endif
#endif
#include "WString.h"
#include "stdlib_noniso.h"
String::String(const char *cstr)
{
init();
if (cstr) copy(cstr, strlen(cstr));
}
String::String(const String &value)
{
init();
*this = value;
}
String::String(const __FlashStringHelper *pstr)
{
init();
*this = pstr;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String::String(String &&rval)
{
init();
move(rval);
}
String::String(StringSumHelper &&rval)
{
init();
move(rval);
}
#endif
String::String(char c)
{
init();
char buf[2];
buf[0] = c;
buf[1] = 0;
*this = buf;
}
String::String(unsigned char value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned char)];
utoa(value, buf, base);
*this = buf;
}
String::String(int value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(int)];
itoa(value, buf, base);
*this = buf;
}
String::String(unsigned int value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned int)];
utoa(value, buf, base);
*this = buf;
}
String::String(long value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(long)];
ltoa(value, buf, base);
*this = buf;
}
String::String(unsigned long value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned long)];
ultoa(value, buf, base);
*this = buf;
}
String::String(float value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
String::String(double value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
String::~String()
{
if (buffer) free(buffer);
}
inline void String::init(void)
{
buffer = NULL;
capacity = 0;
len = 0;
}
void String::invalidate(void)
{
if (buffer) free(buffer);
buffer = NULL;
capacity = len = 0;
}
unsigned char String::reserve(unsigned int size)
{
if (buffer && capacity >= size) return 1;
if (changeBuffer(size)) {
if (len == 0) buffer[0] = 0;
return 1;
}
return 0;
}
unsigned char String::changeBuffer(unsigned int maxStrLen)
{
char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
if (newbuffer) {
buffer = newbuffer;
capacity = maxStrLen;
return 1;
}
return 0;
}
String & String::copy(const char *cstr, unsigned int length)
{
if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy(buffer, cstr);
return *this;
}
String & String::copy(const __FlashStringHelper *pstr, unsigned int length)
{
if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy(buffer, (const char *)pstr);
return *this;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void String::move(String &rhs)
{
if (buffer) {
if (rhs && capacity >= rhs.len) {
strcpy(buffer, rhs.buffer);
len = rhs.len;
rhs.len = 0;
return;
} else {
free(buffer);
}
}
buffer = rhs.buffer;
capacity = rhs.capacity;
len = rhs.len;
rhs.buffer = NULL;
rhs.capacity = 0;
rhs.len = 0;
}
#endif
String & String::operator = (const String &rhs)
{
if (this == &rhs) return *this;
if (rhs.buffer) copy(rhs.buffer, rhs.len);
else invalidate();
return *this;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & String::operator = (String &&rval)
{
if (this != &rval) move(rval);
return *this;
}
String & String::operator = (StringSumHelper &&rval)
{
if (this != &rval) move(rval);
return *this;
}
#endif
String & String::operator = (const char *cstr)
{
if (cstr) copy(cstr, strlen(cstr));
else invalidate();
return *this;
}
String & String::operator = (const __FlashStringHelper *pstr)
{
if (pstr) copy(pstr, strlen((const char *)pstr));
else invalidate();
return *this;
}
unsigned char String::concat(const String &s)
{
return concat(s.buffer, s.len);
}
unsigned char String::concat(const char *cstr, unsigned int length)
{
unsigned int newlen = len + length;
if (!cstr) return 0;
if (length == 0) return 1;
if (!reserve(newlen)) return 0;
strcpy(buffer + len, cstr);
len = newlen;
return 1;
}
unsigned char String::concat(const char *cstr)
{
if (!cstr) return 0;
return concat(cstr, strlen(cstr));
}
unsigned char String::concat(char c)
{
char buf[2];
buf[0] = c;
buf[1] = 0;
return concat(buf, 1);
}
unsigned char String::concat(unsigned char num)
{
char buf[1 + 3 * sizeof(unsigned char)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(int num)
{
char buf[2 + 3 * sizeof(int)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(unsigned int num)
{
char buf[1 + 3 * sizeof(unsigned int)];
utoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(long num)
{
char buf[2 + 3 * sizeof(long)];
ltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(unsigned long num)
{
char buf[1 + 3 * sizeof(unsigned long)];
ultoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(float num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(double num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(const __FlashStringHelper * str)
{
if (!str) return 0;
int length = strlen((const char *) str);
if (length == 0) return 1;
unsigned int newlen = len + length;
if (!reserve(newlen)) return 0;
strcpy(buffer + len, (const char *) str);
len = newlen;
return 1;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs.buffer, rhs.len)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, char c)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(c)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, int num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, long num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, float num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, double num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs)) a.invalidate();
return a;
}
int String::compareTo(const String &s) const
{
if (!buffer || !s.buffer) {
if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer;
if (buffer && len > 0) return *(unsigned char *)buffer;
return 0;
}
return strcmp(buffer, s.buffer);
}
unsigned char String::equals(const String &s2) const
{
return (len == s2.len && compareTo(s2) == 0);
}
unsigned char String::equals(const char *cstr) const
{
if (len == 0) return (cstr == NULL || *cstr == 0);
if (cstr == NULL) return buffer[0] == 0;
return strcmp(buffer, cstr) == 0;
}
unsigned char String::operator<(const String &rhs) const
{
return compareTo(rhs) < 0;
}
unsigned char String::operator>(const String &rhs) const
{
return compareTo(rhs) > 0;
}
unsigned char String::operator<=(const String &rhs) const
{
return compareTo(rhs) <= 0;
}
unsigned char String::operator>=(const String &rhs) const
{
return compareTo(rhs) >= 0;
}
unsigned char String::equalsIgnoreCase( const String &s2 ) const
{
if (this == &s2) return 1;
if (len != s2.len) return 0;
if (len == 0) return 1;
const char *p1 = buffer;
const char *p2 = s2.buffer;
while (*p1) {
if (tolower(*p1++) != tolower(*p2++)) return 0;
}
return 1;
}
unsigned char String::startsWith( const String &s2 ) const
{
if (len < s2.len) return 0;
return startsWith(s2, 0);
}
unsigned char String::startsWith( const String &s2, unsigned int offset ) const
{
if (offset > len - s2.len || !buffer || !s2.buffer) return 0;
return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0;
}
unsigned char String::endsWith( const String &s2 ) const
{
if ( len < s2.len || !buffer || !s2.buffer) return 0;
return strcmp(&buffer[len - s2.len], s2.buffer) == 0;
}
char String::charAt(unsigned int loc) const
{
return operator[](loc);
}
void String::setCharAt(unsigned int loc, char c)
{
if (loc < len) buffer[loc] = c;
}
char & String::operator[](unsigned int index)
{
static char dummy_writable_char;
if (index >= len || !buffer) {
dummy_writable_char = 0;
return dummy_writable_char;
}
return buffer[index];
}
char String::operator[]( unsigned int index ) const
{
if (index >= len || !buffer) return 0;
return buffer[index];
}
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
{
if (!bufsize || !buf) return;
if (index >= len) {
buf[0] = 0;
return;
}
unsigned int n = bufsize - 1;
if (n > len - index) n = len - index;
strncpy((char *)buf, buffer + index, n);
buf[n] = 0;
}
int String::indexOf(char c) const
{
return indexOf(c, 0);
}
int String::indexOf( char ch, unsigned int fromIndex ) const
{
if (fromIndex >= len) return -1;
const char* temp = strchr(buffer + fromIndex, ch);
if (temp == NULL) return -1;
return temp - buffer;
}
int String::indexOf(const String &s2) const
{
return indexOf(s2, 0);
}
int String::indexOf(const String &s2, unsigned int fromIndex) const
{
if (fromIndex >= len) return -1;
const char *found = strstr(buffer + fromIndex, s2.buffer);
if (found == NULL) return -1;
return found - buffer;
}
int String::lastIndexOf( char theChar ) const
{
return lastIndexOf(theChar, len - 1);
}
int String::lastIndexOf(char ch, unsigned int fromIndex) const
{
if (fromIndex >= len) return -1;
char tempchar = buffer[fromIndex + 1];
buffer[fromIndex + 1] = '\0';
char* temp = strrchr( buffer, ch );
buffer[fromIndex + 1] = tempchar;
if (temp == NULL) return -1;
return temp - buffer;
}
int String::lastIndexOf(const String &s2) const
{
return lastIndexOf(s2, len - s2.len);
}
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const
{
if (s2.len == 0 || len == 0 || s2.len > len) return -1;
if (fromIndex >= len) fromIndex = len - 1;
int found = -1;
for (char *p = buffer; p <= buffer + fromIndex; p++) {
p = strstr(p, s2.buffer);
if (!p) break;
if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer;
}
return found;
}
String String::substring(unsigned int left, unsigned int right) const
{
if (left > right) {
unsigned int temp = right;
right = left;
left = temp;
}
String out;
if (left >= len) return out;
if (right > len) right = len;
char temp = buffer[right];
buffer[right] = '\0';
out = buffer + left;
buffer[right] = temp;
return out;
}
void String::replace(char find, char replace)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
if (*p == find) *p = replace;
}
}
void String::replace(const String& find, const String& replace)
{
if (len == 0 || find.len == 0) return;
int diff = replace.len - find.len;
char *readFrom = buffer;
char *foundAt;
if (diff == 0) {
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
memcpy(foundAt, replace.buffer, replace.len);
readFrom = foundAt + replace.len;
}
} else if (diff < 0) {
char *writeTo = buffer;
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
unsigned int n = foundAt - readFrom;
memcpy(writeTo, readFrom, n);
writeTo += n;
memcpy(writeTo, replace.buffer, replace.len);
writeTo += replace.len;
readFrom = foundAt + find.len;
len += diff;
}
strcpy(writeTo, readFrom);
} else {
unsigned int size = len;
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
readFrom = foundAt + find.len;
size += diff;
}
if (size == len) return;
if (size > capacity && !changeBuffer(size)) return;
int index = len - 1;
while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
readFrom = buffer + index + find.len;
memmove(readFrom + diff, readFrom, len - (readFrom - buffer));
len += diff;
buffer[len] = 0;
memcpy(buffer + index, replace.buffer, replace.len);
index--;
}
}
}
void String::remove(unsigned int index){
remove(index, (unsigned int)-1);
}
void String::remove(unsigned int index, unsigned int count){
if (index >= len) { return; }
if (count <= 0) { return; }
if (count > len - index) { count = len - index; }
char *writeTo = buffer + index;
len = len - count;
strncpy(writeTo, buffer + index + count,len - index);
buffer[len] = 0;
}
void String::toLowerCase(void)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
*p = tolower(*p);
}
}
void String::toUpperCase(void)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
*p = toupper(*p);
}
}
void String::trim(void)
{
if (!buffer || len == 0) return;
char *begin = buffer;
while (isspace(*begin)) begin++;
char *end = buffer + len - 1;
while (isspace(*end) && end >= begin) end--;
len = end + 1 - begin;
if (begin > buffer) memcpy(buffer, begin, len);
buffer[len] = 0;
}
long String::toInt(void) const
{
if (buffer) return atol(buffer);
return 0;
}
float String::toFloat(void) const
{
return float(toDouble());
}
double String::toDouble(void) const
{
if (buffer) return atof(buffer);
return 0;
}
|