在上一篇文章中,介绍了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);
}
// the loop function runs over and over again forever
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";
}
// the loop function runs over and over again forever
void loop() {
}
编译后输出为:项目使用了 249084 字节,占用了 (19%) 程序存储空间。最大为 1310720 字节。 全局变量使用了13412字节,(4%)的动态内存,余留314268字节局部变量。最大为327680字节。
再来看看使用WString:
void setup() {String str = "hello wrold";
}
// the loop function runs over and over again forever
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.关于代码版权的问题:
有的伙伴可能会担心搬运这么多代码,会不会收到律师函(划掉)?对于这个问题,大家可以放心地去学习和使用,本文所移植的所有文件都是允许修改和传播的。大家再使用的时候,保留源文件的版权声明就好了!
附:相应文件版权声明:
/*stdlib_noniso.h - nonstandard (but usefull) conversion functionsCopyright (c) 2014 Ivan Grokhotkov. All rights reserved.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*//*WString.h - String library for Wiring & Arduino...mostly rewritten by Paul Stoffregen...Copyright (c) 2009-10 Hernando Barragan. All right reserved.Copyright 2011, Paul Stoffregen, paul@pjrc.comThis library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
四、总结
在移植WString前,我也自己写过一个简单的String类并在项目中使用过,但用起来还是不如WString舒服。移植过程中遇到的许多困难在上面的步骤中都有展现过,虽然移植的过程看起来就是复制一下文件,修改一些代码就可以,但移植之前的准备工作中花了不少的精力去研究Arduino的继承结构,才能和自己的程序有比较好的兼容性。在上一篇文章和这篇文章中已经大量地使用了HardwareUART类来做数据的输出,有了String类地支持,我相信很多小伙伴已经可以在这基础上搓出一个自己的串口类。后面的文章我将会对HardwareUART的功能和设计进行解释。欢迎大家关注我哦~
本期表情包赞助:鹅厂QQ聊天框:
附:代码
/*file: stdlib_noniso.h*/
/*stdlib_noniso.h - nonstandard (but usefull) conversion functionsCopyright (c) 2014 Ivan Grokhotkov. All rights reserved.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*//*修改&移植 龚为玮 2021年12月*/#ifndef STDLIB_NONISO_H
#define STDLIB_NONISO_H#ifdef __cplusplus
extern "C" {
#endifint 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
} // extern "C"
#endif#endif
/*file: stdlib_noniso.c*/
/*core_esp8266_noniso.c - nonstandard (but usefull) conversion functionsCopyright (c) 2014 Ivan Grokhotkov. All rights reserved.This file is part of the esp8266 core for Arduino environment.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USAModified 03 April 2015 by Markus Sattler*//*修改&移植 龚为玮 2021年12月*/#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);// Apply negative signif (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; // how many cells to fill for the integer partif (prec > 0){fillme -= (prec + 1);}// Handle negative numbersif (number < 0.0){negative = true;fillme--;number = -number;}// Round correctly so that print(1.999, 2) prints as "2.00"// I optimized out most of the divisionsdouble rounding = 2.0;for (uint8_t i = 0; i < prec; ++i)rounding *= 10.0;rounding = 1.0 / rounding;number += rounding;// Figure out how big our number really isdouble tenpow = 1.0;int digitcount = 1;while (number >= 10.0 * tenpow){tenpow *= 10.0;digitcount++;}number /= tenpow;fillme -= digitcount;// Pad unused cells with spaceswhile (fillme-- > 0){*out++ = ' ';}// Handle negative signif (negative)*out++ = '-';// Print the digits, and if necessary, the decimal pointdigitcount += prec;int8_t digit = 0;while (digitcount-- > 0){digit = (int8_t)number;if (digit > 9)digit = 9; // insurance*out++ = (char)('0' | digit);if ((digitcount == prec) && (prec > 0)){*out++ = '.';}number -= digit;number *= 10.0;}// make sure the string is terminated*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);
}
/*file: WString.h*/
/*WString.h - String library for Wiring & Arduino...mostly rewritten by Paul Stoffregen...Copyright (c) 2009-10 Hernando Barragan. All right reserved.Copyright 2011, Paul Stoffregen, paul@pjrc.comThis library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*//*修改&移植 龚为玮 2021年12月*/#ifndef String_class_h
#define String_class_h
#ifdef __cplusplus#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//#include <avr/pgmspace.h>// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
// -felide-constructors
// -std=c++0xclass __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;// The string class
class String
{// use a function pointer to allow for "if (s)" without the// complications of an operator bool(). for more information, see:// http://www.artima.com/cppsource/safebool.htmltypedef void (String::*StringIfHelperType)() const;void StringIfHelper() const {}public:// constructors// creates a copy of the initial value.// if the initial value is null or invalid, or if memory allocation// fails, the string will be marked as invalid (i.e. "if (s)" will// be false).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);#endifexplicit 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);// memory management// return true on success, false on failure (in which case, the string// is left unchanged). reserve(0), if successful, will validate an// invalid string (i.e., "if (s)" will be true afterwards)unsigned char reserve(unsigned int size);inline unsigned int length(void) const {return len;}// creates a copy of the assigned value. if the value is null or// invalid, or if the memory allocation fails, the string will be// marked as invalid ("if (s)" will be false).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// concatenate (works w/ built-in types)// returns true on success, false on failure (in which case, the string// is left unchanged). if the argument is null or invalid, the// concatenation is considered unsucessful.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);// if there's not enough memory for the concatenated value, the string// will be left unchanged (but this isn't signalled in any way)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);// comparison (only works w/ Strings and "strings")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;// character acccesschar 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(); }// searchint 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;// modificationvoid 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);// parsing/conversionlong toInt(void) const;float toFloat(void) const;double toDouble(void) const;protected:char *buffer; // the actual char arrayunsigned int capacity; // the array length minus one (for the '\0')unsigned int len; // the String length (not counting the '\0')
protected:void init(void);void invalidate(void);unsigned char changeBuffer(unsigned int maxStrLen);unsigned char concat(const char *cstr, unsigned int length);// copy and moveString & 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 // __cplusplus
#endif // String_class_h
/*file: WString.cpp*/
/*WString.cpp - String library for Wiring & Arduino...mostly rewritten by Paul Stoffregen...Copyright (c) 2009-10 Hernando Barragan. All rights reserved.Copyright 2011, Paul Stoffregen, paul@pjrc.comThis library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*//*修改&移植 龚为玮 2021年12月*/#include "WString.h"
#include "stdlib_noniso.h"/*********************************************/
/* Constructors */
/*********************************************/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);
}
#endifString::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);
}/*********************************************/
/* Memory Management */
/*********************************************/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;
}/*********************************************/
/* Copy and Move */
/*********************************************/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;
}
#endifString & 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;
}
#endifString & 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;
}/*********************************************/
/* concat */
/*********************************************/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;
}/*********************************************/
/* Concatenate */
/*********************************************/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;
}/*********************************************/
/* Comparison */
/*********************************************/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;
}/*********************************************/
/* Character Access */
/*********************************************/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;
}/*********************************************/
/* Search */
/*********************************************/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]; // save the replaced characterbuffer[right] = '\0'; out = buffer + left; // pointer arithmeticbuffer[right] = temp; //restore characterreturn out;
}/*********************************************/
/* Modification */
/*********************************************/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; // compute size needed for resultwhile ((foundAt = strstr(readFrom, find.buffer)) != NULL) {readFrom = foundAt + find.len;size += diff;}if (size == len) return;if (size > capacity && !changeBuffer(size)) return; // XXX: tell user!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){// Pass the biggest integer as the count. The remove method// below will take care of truncating it at the end of the// string.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;
}/*********************************************/
/* Parsing / Conversion */
/*********************************************/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;
}