35_还在为处理字符串查来查去吗

还在为处理字符串查来查去吗

我们知道,计算机只能存储二进制数据,那么该如何表示和存储字符呢?这就需要使用字符集来实现字符与整数之间的转换。在实际开发中,处理字符串之前,我们必须直面字符串中的开发易错点,编码问题。理解其发展的由来,才能在开发时游刃有余,手起刀落。

ASCll

(American Standard Code for Information Interchange,美国信息交换标准代码)起始于20世纪50年代后期,在1967年定案,是基于拉丁字母的一套计算机编码系统,主要用于显示现代英语和其他西欧语言,它是现今通用的单字节编码系统。ASCII最初是美国国家标准,供不同的计算机在相互通信时作为共同遵守的西文字符编码标准,后来被国际标准化组织 (International Organization for Standardization,ISO)定为国际标准,称为ISO 646标准。

ASCII使用7位二进制数来表示128个字符,称为标准ASCI,包括所有的大写和小写字母、数字0~9、标点符号以及在美式英语中使用的特殊控制字符。具有每一个字符,用什么数字表示如下所示:

  • 0~31及127(共33个)是控制字符或通信专用字符。比如说控制字符 包括LF(换行)、CR(回车)、FF(换页)、DEL (删除)、BS(退格)、BEL (响铃)等;比如说通信专用字符 包括SOH(文头)、EOT(文尾)、ACK(确认)等。ASCII值8 9 10和13分别转换为退格、制表、换行和回车字符,它们并没有特定的图形显示,但会对文本显示产生影响。
  • 32~126(共95个)是字符(32是空格),其中48~57为阿拉伯数字0~9; 65~90为26个大写英文字母,97~122号为26个小写英文字母;其余为标点符号、运算符号等。

由ASCII码表可以看到,数字符号、大写字母符号和小写字母符号的编码都是连续的,所以只要记住数字符号的编码从0x30开始、大写字母符号的编码从0x41开始以及小写字母符号的编码从0x61开始便可以推算出其他数字符号和字母符号的编码。

扩展ASClI

标准ASCII仅使用了每字节的低7位进行编码,最多可以表示128个字符。这往往不能满足实际需求,为此在IBM PC系列及其兼容机上使用了扩展的ASCII码。扩展的ASCII码使用8位二进制数进行编码,扩展的ASCI包含标准ASClI中已有的128个字符,又增加了128个字符,总共是256个,值128~255用来表示框线、音标和其他欧洲非英语系的字母。

1987年4月,MS DOS 3.3把代码页的概念带进了IBM。每个代码页都是一个字符集,并且这一概念后来也被用到了Windows系统里。这样一来,原本的IBM字符集成为第437页代码页,微软自己的
MS DOS Latin 1字符集成为第850页代码页。其他的代码页为其他语言制定,就是说较低的128个ASCII码总是表示标准ASCII字符,而较高的128个ASCII码则取决于定义代码页的语言。

后来,代码页的数量以超乎想象的速度递增,后来更是出现了不同操作系统对于同一个国家语言的代码页互相不兼容的情况。每个系统环境的代码页都对标准字符集ASCII进行了修订,这使局面很混乱。

双字节字符集DBCS

单字节字符集肯定远远包含不了那些包括上万个字符的语言,例如中文、日文,因此这些国家都开发了表示自己本国文字的双字节字符集,用2字节(16位二进制数据)来表示除ASCII以外的字符(ASCI
还是使用1字节来表示),其中常见的就是我国的GB系列编码了(GB为国标的拼音缩写)。不同国家创造出来的字符集虽说与ASCII兼容,但是编码却是互不兼容的,例如相同数值的2字节,在中文和日语中则表示两个不同的字符。这些不同国家的字符集,同样也被微软纳入了代码页体系中,例如中文就是第936页代码页(我们最常见的CP936就是这个意思,它表达的字符集和GBK是一样的)。

Windows支持4种不同的双字节字符集,代码页932(日文)、936(简体中文)、949(韩文)以及950(繁体中文)。在双字节字符集中,一个字符串中的每个字符都由1字节或2字节组成。以日文为例,如果第1字节在0x81~Ox9F或OxEO~OxFC,就必须检查下一字节,才能判断出一个完整的字符。对程序员而言,和双字节字符集打交道如同一场噩梦,因为有的字符是1字节宽,有的字符却是2字节宽。

Unicode国际化

为了表示不同国家和地区的语言,编码方案有上百种,避免这种混乱的需求由来已久。Unicode是1988年由Apple和Xerox共同建立的一项标准,Unicode的诞生解决了双字节字符集的混乱问题。虽然同样是用16位(2字节)来表示字符,但Unicode只有一个字符集,包含了世界上任何一个国家和地区的语言所用的字符。Unicode标准定义了所有主要语言中使用的字母、符号、标点,其中包括了欧洲地区的语言、中东地区的希伯来语言、亚洲地区的语言等。Unicode有3种编码形式,允许字符以字节、字或双字格式存储。

  • UTF-8 UTF-8将有的字符编码为1字节,有的字符编码为2字节,有的字符编码为3字节,甚至有的字符编码为4字节。值在0x80(128)以下的字符(即标准ASCII)被转换为1字节,适合美国﹔0x80~Ox7FF的字符被转换为2字节,适合欧洲和中东地区;0x800以上的字符被转换为3字节,适合东亚地区﹔最后,代理对
    (Surrogate Pair)被转换为4字节。代理对是UTF-16中用于扩展字符而使用的编码方式,采用4字节(两个UTF-16编码)来表示一个字符。UTF-8是一种相当流行的编码格式,但在对值为0x800以上的大量字符进行编码时,UTF-8不如UTF-16高效。
  • UTF-16 UTF-16将每个字符编码为2字节(16位)。在谈到 Unicode时,除非专内声明,一般都是指UTF-16编码 Windows之所以使用UTF-16,是因为全球各地使用的大部分语言中,通常用一个16位值来表示每个字符,每个字符被编码为2字节,所以很容易遍历字符串并计算它的字符个数。但是,16位不足以表示某些语言的所有字符。对于不能表示的字符,UTF-16支持使用代理对,代理对是用32位(4字节)来表示一个字符的一种方式,由于只有少数应用程序需要使用这类字符,因此UTF-16在节省空间和简化编码这两个目标之间提供了一个很好的折衷。
  • UTF-32 UTF-32将每个字符都编码为4字节,用于不太关心存储空间的环境中。在将字符串保存到文件或传输到网络的时候,基于空间和速度的考虑,很少会使用这种格式,这种编码格式一般在应用程序内部使用。

把长度较小的Unicode值(例如字节)复制到长度较大的Unicode值(例如字或双字)中不会丢失任何数据。另外,Unicode的实现还有大小端(后面会讲)存储的区别,并且UTF-8还存在是否带有BOM标记的问题,因此在很多文本编辑器里有多种关于Unicode这一项的编码转换。

ASCII和ANSI

首先是字面上的差别,ASCII即 American Standard Code for Information Interchange,美国信息互换标准代码。ANSI即 American National Standard Institite,美国国家标准协会的一种编码标准。后者更强调国家标准,一般是面向世界范围内国家和地区之间的交流,该协会规定了很多类似的标准。为了让计算机支持更多语言,值在0x80~0xFFFF范围的字符使用2字节或多字节来表示,比如∶汉字"中"在中文操作系统中使用[OxD6,0xD0]来存储。不同的国家和地区制定了不同的标准,由此产生了GB2312 GBK
GB18030 Big5 Shift _JIS等各自的编码标准。这些使用多字节来代表一个字符的各种延伸编码方式,称为ANSI编码。

  • 在简体中文Windows操作系统中,ANSI编码代表GBK编码

  • 在繁体中文Windows操作系统中,ANSI编码代表Big5

  • 在日文Windows操作系统中,ANSI编码代表Shift_JIlS编码。

不同的ANSI编码互不兼容。当信息在国际间交流时,两种语言的文字无法使用同一个ANSI编码,ANSI编码就是一个具体国家的多字节字符集。ANSI编码表示英文字符时使用1字节,表示中文时用2~4字节。

其次,一般会拿ANSI码和Unicode码对比,两者都是各种语言的表示方法。不同的是,ANSI在不同国家和地区的不同语言中有不同的具体标准,是国家标准,比如,在简体中文系统中就是GB2312-GBK。相对而言,Unicode正如其名,是Universal Code,具有统一、通用的意思,是国际化标准。

字符和字符串处理

/*********************************************************************/
/*


字符串工具类:基于业务而抽象的字符串工具类,主要提供关于字符串相关操作的函数封装





说明				ANSI		UNICODE			通用
头文件:			char.h		wchar.h			tchar.h
基本数据类型:		char		wchar_t			TCHAR
指针:			char*		wchar_t*		TCHAR*
常量指针:		const char* const wchar_t*  const TCHAR*
指针宏表示:		LPSTR		LPWSTR			LPTSTR
常量指针宏表示:	LPCSTR		LPCWSTR			LPCTSTR

------复杂的宽窄字节数据类型-----:

微软提供的解决方案是:提供一个宏和通用类型来做这种编码适配工作:
通俗的说:
P代表指针的意思,STR代表字符串的意思,L是长指针的意思,在WIN32平台下可以忽略,C代表const常量的意思,W代表wide宽字节的意思,T大家可以理解为通用类型的意思,
就是可以根据工程中是否定义_UNICODE宏,来判断当前工程的编码类型是宽字节还是窄字节,之后分别定义成不同的类型,比如:TCHAR类型,如果工程中定义了_UNICODE宏,那么就表明工程是宽字节编码,他最终就被定义成wchar_t类型,如果工程中没有定义_UNICODE宏,就表明工程当前是窄字节编码,那么TCHAR 被最终定义成char类型。
其方便性就是修改了工程的编码格式之后不用修改代码,所以还是建议大家在编写程序的时候使用通用类型


------字符串转换------:
把字符串转换成整数(int)		atoi		_wtoi		_ttoi
把字符串转换成长整型数(long)	atol		_wtol		_ttol
把字符串转换成浮点数(double)	atof		_wtof		_tstof
将任意类型的数字转换为字符串		itoa		_itow		_itot


------字符串操作------:
获得字符串的数目(长度,并不包括)						strlen	wcslen	_tcslen
拷贝字符串											strcpy	wcscpy	_tcscpy
类似于strcpy/wcscpy,同时指定拷贝的数目					strncpy	wcsncpy	_tcsncpy
字符串安全拷贝											strcpy_s wcscpy_s _tcscpy_s
比较两个字符串,区分大小写								strcmp	wcscmp	_tcscmp
比较两个字符串,不区分大小写								stricmp	wcsicmp	_tcsicmp
类似于strcmp/wcscmp,同时指定比较字符字符串的数目			strncmp	wcsncmp	_tcsncmp
把一个字符串接到另一个字符串的尾部							strcat	wcscat	_tcscat
类似于strcat/wcscat,而且指定粘接字符串的粘接长度			strncat	wcsncat	_tcsnccat
从左(头)边开始,查找子字符第一个位置						strchr	wcschr	_tcschr
从右尾边开始查找子字符出现的第一个位置						strrchr	wcsrchr	_tcsrchr
从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置	strstr	wcsstr	_tcsstr
返回不包含第二个字符串的的初始数目							strcspn	wcscspn	_tcscspn
返回包含第二个字符串的初始数目							strspn	wcsspn	_tcsspn
根据分割符把字符串分割成一系列字符串						strtok	wcstok	_tcstok

获得宽字符串的宽度	wcswidth


------字符串校验------:
测试字符是否为ASCII 码字符,itemChar ∈[0,127] => True    isascii		iswascii	_istascii
测试字符是否为数字或字母									isalnum		iswalnum	_istalnum
测试字符是否是字母										isalpha		iswalpha	_istalpha
测试字符是否是控制符									iscntrl		iswcntrl	_istcntrl
测试字符是否为数字										isdigit		iswdigit	_istdigit
测试字符是否是可见字符									isgraph		iswgraph	_istgraph
测试字符是否是小写字符									islower		iswlower	_istlower
测试字符是否是可打印字符									isprint		iswprint	_istprint
测试字符是否是标点符号									ispunct		iswpunct	_istpunct
测试字符是否是空白符号									isspace		iswspace	_istspace
测试字符是否是大写字符									isupper		iswupper	_istupper
测试字符是否是十六进制的数字								isxdigit	iswxdigit	_istxdigit


------大小写转换------:
把字符转换为小写										tolower		towlower	_totlower
把字符转换为大写										toupper		towupper	_totupper


------各个字符比较------:
比较字符串(建议还是用stricmp)						    strcoll		wcscoll		_tcscoll



------日期和时间转换------:
根据指定的字符串格式和locale设置格式化日期和时间			    strftime	wcsftime	_tcsftime
根据指定格式把字符串转换为时间值, 是strftime的反过程		strptime


------打印和扫描字符串------:
使用vararg参量的格式化输出到标准输出						printf		wprintf		_tprintf
使用vararg参量的格式化输出								fprintf		fwprintf	_ftprintf
从标准输入的格式化读入									scanf		wscanf		_tscanf
格式化读入											fscanf		fwscanf		_ftscanf
根据vararg参量表格式化成字符串							sprintf		swprintf	_stprintf
以字符串作格式化读入									sscanf		swscanf		_stscanf
使用stdarg参量表格式化输出到文件							vfprintf	vfwprintf	_vftprintf
格式化stdarg参量表并写到字符串							vsprintf	vswprintf	_vstprintf
格式化字符串											sprintf_s	swprintf_s	_stprintf_s


------数字转换------:
把字符串的初始部分转换为双精度浮点数						strtod		wcstod		_tcstod
把字符串的初始部分转换为长整数							strtol		wcstol		_tcstol
把字符串的初始部分转换为无符号长整数						strtoul		wcstoul		_tcstoul

CString来了!

虽然C++STL中的string 实比C语言中的字符串处理方便很多,但是这里我要说,跟成熟的字符串处理还是差很多,起码跟CString来说就差了不少.
比如:
    trim操作∶去除掉首尾的不可见字符,比如回车,制表符,空格之类的;
    reverse操作∶进行字符串的首尾颠倒反转;
    upper操作∶将字符串中的英文全变成大写字母;
    lower操作∶将字符串中的英文全变成小写字母;
    right操作:直接返回字符串中结尾的指定字符;
    span_including操作:返回包含指定字符串中任意一个字符的子串
    span_excluding操作:返回不包含指定字符串中任意一个字符的子串
    format:格式化字符串
    replace :替换字符串中的指定字符
    stricmp:不区分大小写进行字符串比较

    针对当前编码,决定当前内部的指针是char*还是wchar_t*,实际上CString 本身是个模板类,实际上有三种:
    CStringA内部使用的是char*
    CStringW内部使用的是wchar_t*
    CString内部使用的是TCHAR*,所以CString本身会根据当前工程的编码决定使用char*还是wchar_t*,这一点也比较方便.
    我们可以先简单使用下 CString,之后改变下工程的编码,看看编译结果就知道了.


优点:
    使用方便,包含了很多实现好的操作,包括 :trim、reverse、format、replace等等;
    无缝兼容MFC;
    无缝兼容MFC;
    自适应当前工程编码,智能匹配宽窄字节;


重点,能实现从窄字节到宽字节的自动转换工作,
即当前工程编码是宽字节Unicode的前提下,CString支持从char*类型的窄字节字符串来构造和赋值,所以是可以方便的从窄字节转换到宽字节。但是反之则没有办法实现.



<string.h> 头文件定义的函数:

void *memchr(const void *str, int c, size_t n)				在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。
int memcmp(const void *str1, const void *str2, size_t n)	把 str1 和 str2 的前 n 个字节进行比较。
void *memcpy(void *dest, const void *src, size_t n)			从 src 复制 n 个字符到 dest。
void *memmove(void *dest, const void *src, size_t n)		另一个用于从 src 复制 n 个字符到 dest 的函数。
void *memset(void *str, int c, size_t n)					复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
size_t strxfrm(char *dest, const char *src, size_t n)		根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。





-------------字符处理函数--------------------

IsCharAlphaNumeric()	如果字符为字母数字,则返回值为非零值
IsCharAlpha()			确定字符是否为字母字符
IsCharUpper()			确定字符是否为大写
IsCharLower()			确定字符是否为小写

*/
/*********************************************************************/


#pragma once

#include <string>
#include <vector>
#include<fstream>
#include<algorithm>
#include<Windows.h>
#include <locale>
#include<stdint.h>
#include<wchar.h>

#ifdef _UNICODE
typedef  std::wstring _tstring;
#else
typedef  std::string _tstring;
#endif


class CStrUtils
{
public:

    //从文件中载入字符串
    static std::string FromFileA(const  std::string& strFilePath);
    static std::wstring FromFileW(const std::wstring& strFilePath);
    static _tstring FromFile(const _tstring& strFilePath);


    //转换大写
    static std::string ToUpperStrA(const std::string& str);
    static std::wstring ToUpperStrW(const std::wstring& str);
    static _tstring ToUpperStr(const _tstring& str);


    //转换小写
    static std::string ToLowerStrA(const std::string& str);
    static std::wstring ToLowerStrW(const std::wstring& str);
    static _tstring ToLowerStr(const _tstring str);

    //分割字符串
    static std::vector<std::string>  SplitStrA(const std::string& str,const std::string& delim);
    static std::vector<std::wstring> SplitStrW(const std::wstring& str, const std::wstring& delim);
    static std::vector<_tstring> SplitStr(const _tstring& str,const _tstring& delim);

    //替换字符串
    static std::string& ReplaceA(std::string& strSrc,const std::string& strFind,const std::string& strReplace, bool bCase = false);
    static std::wstring& ReplaceW(std::wstring& strSrc,const std::wstring& strFind,const std::wstring& strReplace,bool bCase = false);
    static _tstring Replace(_tstring& strSrc, const _tstring& strFind, const _tstring& strReplace, bool bCase = false);

    //比较字符串
    static int CompareA(const std::string& strSrc, const std::string& strDest, bool bCase = true);
    static int CompareW(const std::wstring& strSrc,const std::wstring& strDest,bool bCase = true );
    static int Compare(const _tstring& strSrc,const _tstring& strDest,bool bCase = true);

    //格式化字符串
    static std::string FormatA(LPCSTR formatstring,...);
    static std::wstring FormatW(LPWSTR formatstring, ...);
    static _tstring Format(LPCTSTR formatstring,...);


    //去掉空格
    static std::string TrimA(const std::string& source );
    static std::wstring TrimW(const std::wstring& source);
    static _tstring Trim(const _tstring& source);



    //stl 模板思想 value_type应用,解决find 查字串 对大小写敏感的底层实现,而提供一个新的方案,把大小写认定为同一个字符
    template<typename charT>
    struct TCharCmpEqual
    {
        explicit TCharCmpEqual(const std::locale& loc) :loc_(loc) {}

        bool operator()(charT ch1, charT ch2)
        {
            return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
        }

    private:
        const std::locale& loc_;
    };
    template<typename T>
    static size_t FindNoCase(const T& str1,const T& str2)
    {
        //查容器str2的元素,在str1中第几个元素开始出现不同
        typename T::const_iterator it = std::search(
            str1.begin(), str1.end(),
            str2.begin(), str2.end(),
            TCharCmpEqual<typename T::value_type>(std::locale())
        );

        if (it != str1.end())
        {
            return it - str1.begin();
        }

        return _tstring::npos;
    }
    static size_t FindNoCaseA(LPCSTR str1,LPCSTR str2);
    static size_t FindNoCaseW(LPCWSTR str1,LPCWSTR str2);
    static size_t FindNoCase(LPCTSTR str1, LPCTSTR str2);


    //其他编码字符串 转 中立字符串转换
    static _tstring WStrToTStr(const std::wstring str);
    static _tstring AStrToTStr(const std::string str);
    static _tstring U8StrToStr(const std::string& str);

    //中立字符串 转 其他编码字符串
    static std::wstring TStrToWStr(const _tstring& str);
    static std::string TStrToAStr(const _tstring& str);
    static std::string TStrToU8Str(const _tstring& str);


    //Unicode 编码字符串 转 其他编码字符串
    static std::string WStrToAStr(const std::wstring& wstr);
    static std::string WStrToU8Str(const std::wstring wstr);

    //Ansi 编码字符串 转 其他编码字符串
    static std::wstring AStrToWStr(const std::string& str);
    static std::string  AStrToU8Str(const std::string& str);


    //Uft8 编码字符串 转 其他编码字符串
    static std::wstring U8StrToWStr(const std::string& str);
    static std::string U8StrToAStr(const std::string &str);

    //对字符串数组进行去重处理
    /*要进行去重的字符串数组,去重原则是不区分大小写判断字符是否相等*/
    BOOL UniqueA(
        /*输出参数*/
        std::vector<std::string>& slist 
    );


    //基本数据类型转为字符串
    template<typename T>
    static _tstring ToString(const T& in)
    {
        _tstring out;

#ifdef _UNICODE
        wstringstream stream;
        stream << in;
        out = stream.str();
#else
        stringstream stream;
        stream << in;
        out = stream.str();
#endif
        return out;
    }
    template<typename T>
    static T StringTo(const _tstring& in)
    {
        T out;

#ifdef _UNICODE
        wstringstream stream;
        stream << in;
        stream >> out;
        return out;
#else
        stringstream stream;
        stream << in;
        stream >> out;
        return out;
#endif
    }


    //对字符串组成进行分析
    static bool IsDoubleStrA(const CHAR* validString);






private:
    //宽字符转其他
    static std::string _WStrToOtherStr(UINT CodePage, const std::wstring& str);

    //其他转宽字符
    static std::wstring _OtherStrToWStr(UINT CodePage,const std::string& str);

};


#include "CStrUtils.h"


std::string CStrUtils::FromFileA(const std::string& strFilePath)
{
	std::string strContext;//存文件内容
	size_t fileSize = 0; //文件大小
	char* lpBuf = nullptr; //文件数据缓冲接收曲

	std::ifstream  fileInputStream(strFilePath.c_str(), std::ios::binary | std::ios::in);
	if (!fileInputStream.is_open())
	{
		return strContext;
	}

	//查文件大小
	fileInputStream.seekg(0, std::ios::end);
	fileSize = fileInputStream.tellg();

	//读文件内容
	fileInputStream.seekg(0, std::ios::beg);
	lpBuf = (char*) new (std::nothrow) char[fileSize + sizeof(char)];
	if (nullptr != lpBuf)
	{
		memset(lpBuf, 0, fileSize);
		fileInputStream.read((char*)lpBuf, fileSize);
		size_t nByteSize = (size_t)fileInputStream.gcount();		//查读到的字符个数
		if (nByteSize > 0)
		{
			strContext = lpBuf;
		}
		delete[] lpBuf;
		lpBuf = nullptr;
		
	}
	fileInputStream.close();
	return strContext;

}
std::wstring CStrUtils::FromFileW(const std::wstring& strFilePath)
{
	std::wstring strContext;
	size_t fileSize = 0;
	wchar_t* lpBuf = nullptr;

	std::ifstream fileInputStream(strFilePath.c_str(),std::ios::binary | std::ios::in);
	if (!fileInputStream.is_open())
	{
		return strContext;
	}

	//查文件大小
	fileInputStream.seekg(std::ios::end);
	fileSize = fileInputStream.tellg();
	
	//读取文件内容
	fileInputStream.seekg(0,std::ios::beg);
	lpBuf = (wchar_t*) new (std::nothrow) wchar_t[fileSize + sizeof(wchar_t)];
	if (nullptr != lpBuf)
	{
		memset(lpBuf, 0, fileSize);
		fileInputStream.read((char*)lpBuf, fileSize);
		size_t nByteSize = (size_t)fileInputStream.gcount();		//查读到的字符个数
		if (nByteSize > 0)
		{
			strContext = lpBuf;
		}
		delete[] lpBuf;
		lpBuf = nullptr;
	}
	fileInputStream.close();
	return strContext;
}
_tstring CStrUtils::FromFile(const _tstring& strFilePath)
{
#ifdef _UNICODE
	return FromFileW(strFilePath);
#else
	return FromFileA(strFilePath);
#endif 

}

std::string CStrUtils::ToUpperStrA(const std::string& str)
{
	std::string strResult = str;
	std::transform(strResult.begin(), strResult.end(), strResult.begin(), [](char ch) ->char
		{
			if ((ch >= 'a') && (ch <= 'z'))
			{
				ch = ch - ('a' - 'A');
			}
			return ch;
		}
	);
	return strResult;
}

std::wstring CStrUtils::ToUpperStrW(const std::wstring& str)
{
	std::wstring strResult = str;
	std::transform(strResult.begin(), strResult.end(), strResult.begin(), [](wchar_t ch)->wchar_t
		{
			if ((ch >= L'a') && (ch <= L'z'))
			{
				ch = ch - (L'a' - L'A');
			}
			return ch;
		}
	);
	return strResult;
}

_tstring CStrUtils::ToUpperStr(const _tstring& str)
{
#ifdef _UNICODE
	return ToUpperStrW(str);
#else
	return ToUpperStrA(str);
#endif 
}

std::string CStrUtils::ToLowerStrA(const std::string& str)
{
	std::string strResult = str;
	std::transform(strResult.begin(), strResult.end(), strResult.begin(), [](char ch)->char
		{
			if ((ch >= 'A') || (ch <= 'Z'))
			{
				ch = ch + ('a' - 'A');
			}
			return ch;
		});

	return strResult;
}

std::wstring CStrUtils::ToLowerStrW(const std::wstring& str)
{
	std::wstring strResult = str;
	std::transform(strResult.begin(), strResult.end(), strResult.begin(), [](wchar_t ch)->wchar_t
		{
			if ((ch >= L'A') || (ch <= L'Z'))
			{
				ch = ch + (L'a' - L'A');
			}
		});
	return strResult;
}

_tstring CStrUtils::ToLowerStr(const _tstring str)
{
#ifdef _UNICODE
	return ToLowerStrW(str);
#else
	return ToLowerStrA(str);
#endif
}

std::vector<std::string> CStrUtils::SplitStrA(const std::string& str, const std::string& delim)
{
	std::vector<std::string> vecResult;
	size_t iStart = 0;
	size_t iEnd = 0;

	//双指针算法思路:iStart 始终指向不是分隔符的第一个字符,iEnd始终指向分隔符字符。
	// 完成一次截取子串后,继续缩小索引,更改双指针指向
	//a,b,c,d
	//0,1,2,3


	while ((iStart = str.find_first_not_of(delim, iEnd)) != std::string::npos)
	{
		iEnd = str.find(delim,iStart);
		vecResult.push_back(str.substr(iStart,iEnd-iStart));
	}
	return vecResult;

	//C风格写法:
	//BOOL CStrUtil::SplitToArray(TCHAR * str, const TCHAR * separator, vector<TCHAR*>&_arrs)
	//{
	//	TCHAR* token;
	//	_arrs.clear();
	//	token = _tcstok(str, separator);
	//	while (token)
	//	{
	//		_arrs.push_back(token);
	//		token = _tcstok(NULL, separator);
	//	}
	//	return TRUE;
	//}
	// 
	// 
	//Or
	/*BOOL CStrUtil::__SplitToArrayA__(string str, char separator, vector<string>&_arrs)
	{
		_arrs.clear();

		stringstream strSstrem(str);

		string next;
		while (getline(strSstrem, next, separator))
		{
			_arrs.push_back(next);
		}


		if (!_arrs.empty())
			return TRUE;
		else
			return FALSE;
	}*/


}

std::vector<std::wstring> CStrUtils::SplitStrW(const std::wstring& str, const std::wstring& delim)
{
	std::vector<std::wstring> vecResult;
	size_t iStart = 0;
	size_t iEnd = 0;
	while ((iStart == str.find_first_not_of(delim,iEnd)) != std::string::npos)
	{
		iEnd = str.find(delim,iStart);
		vecResult.push_back(str.substr(iStart,iEnd-iStart));
	}
	return vecResult;
}

std::vector<_tstring> CStrUtils::SplitStr(const _tstring& str, const _tstring& delim)
{
#ifdef _UNICODE
	return SplitStrW(str, delim);
#else
	return SplitStrA(str, delim);
#endif
}

std::string& CStrUtils::ReplaceA(std::string& strSrc, const std::string& strFind, const std::string& strReplace, bool bCase)
{
	std::string strDest = strSrc;
	size_t nFind = std::string::npos;
	if (bCase)
	{
		do
		{

			//查这个替换串在源串位置
			nFind = strDest.find(strFind);
			if (std::string::npos != nFind)
			{
				//从这个位置开始,替换strFind.size()个字符,这些字符由 strReplace 更新
				strDest.replace(nFind,strFind.size(),strReplace);
			}

		} while (std::string::npos != nFind);

	}
	else
	{
		//对大小写不敏感,实现大写也能匹配上,同样去做替换的逻辑
		do
		{
			nFind = FindNoCase(strDest, strFind);
			if (std::string::npos != nFind)
			{
				strDest.replace(nFind, strFind.size(), strReplace);
			}
		} while (std::string::npos != nFind);
	}

	strSrc = strDest;
	return strSrc;

}

std::wstring& CStrUtils::ReplaceW(std::wstring& strSrc, const std::wstring& strFind, const std::wstring& strReplace, bool bCase)
{
	std::wstring strDest = strSrc;
	size_t nFind = std::wstring::npos;

	if (bCase)
	{
		while (_tstring::npos != (nFind = strDest.find(strFind)))
		{
			strDest.replace(nFind,strFind.size(),strReplace);
		}
	}
	else
	{
		while (std::wstring::npos != (nFind = FindNoCase(strDest, strFind)))
		{
			strDest.replace(nFind, strFind.size(), strReplace);
		}
	}

	strSrc = strDest;
	return strSrc;

}

_tstring CStrUtils::Replace(_tstring& strSrc, const _tstring& strFind, const _tstring& strReplace, bool bCase)
{
#ifdef _UNICODE
	return ReplaceW(strSrc, strFind, strReplace, bCase);
#else
	return ReplaceA(strSrc, strFind, strReplace, bCase);
#endif
}

int CStrUtils::CompareA(const std::string& strSrc, const std::string& strDest, bool bCase)
{
	if (bCase)
	{
		return strcmp(strSrc.c_str(), strDest.c_str());
	}
	else
	{
		return stricmp(strSrc.c_str(),strDest.c_str());
	}
}

int CStrUtils::CompareW(const std::wstring& strSrc, const std::wstring& strDest, bool bCase)
{
	if (bCase)
	{
		return wcscmp(strSrc.c_str(),strDest.c_str());
	}
	else
	{
		return _wcsicmp(strSrc.c_str(), strDest.c_str());
	}
}

int CStrUtils::Compare(const _tstring& strSrc, const _tstring& strDest, bool bCase)
{
#ifdef _UNICODE
	return CompareW(strSrc, strDest, bCase);
#else
	return CompareA(strSrc, strDest, bCase);
#endif

}

std::string CStrUtils::FormatA(LPCSTR formatstring, ...)
{
	std::string strResult;
	va_list args;
	LPSTR lpBuf = nullptr;
	DWORD dwCchCount = MAX_PATH;

	va_start(args,formatstring);

	do
	{
		lpBuf = (LPSTR) ::HeapAlloc(::GetProcessHeap(),HEAP_ZERO_MEMORY,dwCchCount * sizeof(char));
		if (nullptr == lpBuf)
		{
			break;
		}
		//对可变参数内容做整合转换到字符串中
		if (-1 != _vsnprintf_s(lpBuf, dwCchCount, _TRUNCATE, formatstring, args))
		{
			strResult = lpBuf;
			break;
		}

		//失败,怀疑是缓冲区空间不够
		::HeapFree(::GetProcessHeap(),0,lpBuf);
		if (dwCchCount < INT32_MAX)
		{
			dwCchCount *= 2;
		}
		else
		{
			break;
		}


	} while (true);

	//释放缓冲
	if (nullptr != lpBuf)
	{
		::HeapFree(::GetProcessHeap(), 0, lpBuf);
		lpBuf = nullptr;
	}

	va_end(args);

	return strResult;


}

std::wstring CStrUtils::FormatW(LPWSTR formatstring, ...)
{
	std::wstring strResult;
	va_list args;
	LPWSTR lpBuf = nullptr;
	DWORD dwCchCount = MAX_PATH;

	va_start(args, formatstring);

	do
	{
		lpBuf = (LPWSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY,dwCchCount * sizeof(wchar_t));
		if (nullptr == lpBuf)
		{
			break;
		}
		//对可变参数内容做整合转换到字符串中
		if (-1 != _vsnwprintf_s(lpBuf, dwCchCount, _TRUNCATE, formatstring, args))
		{
			strResult = lpBuf;
			break;
		}

		//失败,怀疑是缓冲区空间不够
		::HeapFree(::GetProcessHeap(), 0, lpBuf);
		lpBuf = nullptr;

		if (dwCchCount < INT32_MAX)
		{
			dwCchCount *= 2;
		}
		else
		{
			break;
		}

	} while (true);


	if (nullptr != lpBuf)
	{
		::HeapFree(::GetProcessHeap(), 0, lpBuf);
		lpBuf = nullptr;
	}

	va_end(args);

	return strResult;

}

_tstring CStrUtils::Format(LPCTSTR formatstring, ...)
{

	_tstring strResult;
	va_list args;
	LPTSTR lpBuf = nullptr;
	DWORD dwCchCount = MAX_PATH;

	va_start(args, formatstring);

	do
	{

		lpBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwCchCount * sizeof(TCHAR));
		if (nullptr == lpBuf)
		{
			break;
		}


#ifdef _UNICODE

		if (-1 != _vsnwprintf_s(lpBuf, dwCchCount, _TRUNCATE, formatstring, args))
		{
			strResult = lpBuf;
			break;
		}
#else
		if (-1 != _vsnprintf_s(lpBuf,dwCchCount, _TRUNCATE,formatstring,args))
		{
			strResult = lpBuf;
			break;
		}
#endif 
	
		::HeapFree(::GetProcessHeap(), 0, lpBuf);
		 lpBuf = nullptr;

		 if (dwCchCount < INT32_MAX)
		 {
			 dwCchCount *= 2;
		 }
		 else
		 {
			 break;
		 }

	} while (true);

	if (nullptr != lpBuf)
	{
		::HeapFree(::GetProcessHeap(), 0, lpBuf);
		lpBuf = nullptr;
	}

	va_end(args);

	return strResult;

}
std::string CStrUtils::TrimA(const std::string& source)
{
	std::string str = source;
	size_t pos = _tstring::npos;

	if (str.empty()) //啥字符都没有,处理个屁,直接返回。
	{
		return str;
	}

	//从开头,一个一个字符的找,能不找到空格? 不能就需要处理去空
	pos = str.find_last_not_of(' ');
	if (pos != std::string::npos)
	{

		str.erase(pos + 1); //截断之前的空格

		pos = str.find_first_not_of(' '); 		//处理结尾有空格的情况。直接截断
		if (pos != std::string::npos)
		{
			str.erase(0, pos);
		}

	}
	else //证明,都是空字符构成的字符串,去掉空字符,然后再返回
	{
		//a blank string
		str.erase(str.begin(), str.end());
	}
	return str;



}
std::wstring CStrUtils::TrimW(const std::wstring& source)
{
	std::wstring str = source;
	size_t pos = std::wstring::npos;

	if (str.empty()) //啥字符都没有,处理个屁,直接返回。
	{
		return str;
	}

	//从开头,一个一个字符的找,能不找到空格? 不能就需要处理去空
	pos = str.find_last_not_of(L' ');
	if (pos != std::wstring::npos)
	{

		str.erase(pos + 1); //截断之前的空格

		pos = str.find_first_not_of(L' '); 		//处理结尾有空格的情况。直接截断
		if (pos != std::string::npos)
		{
			str.erase(0, pos);
		}

	}
	else //证明,都是空字符构成的字符串,去掉空字符,然后再返回
	{
		//a blank string
		str.erase(str.begin(), str.end());
	}
	return str;



}
_tstring CStrUtils::Trim(const _tstring& source)
{
	_tstring str = source;
	size_t pos = _tstring::npos;
	if (str.empty())
	{
		return str;
	}
	str.erase(0, str.find_first_not_of(TEXT(" ")));
	str.erase(str.find_last_not_of(TEXT(" ")) + 1);
	return str;
}





size_t CStrUtils::FindNoCaseA(LPCSTR str1, LPCSTR str2)
{
	return FindNoCase(std::string(str1),std::string(str2));
}

size_t CStrUtils::FindNoCaseW(LPCWSTR str1, LPCWSTR str2)
{
	return FindNoCase(std::wstring(str1),std::wstring(str2));
}

size_t CStrUtils::FindNoCase(LPCTSTR str1, LPCTSTR str2)
{
	return FindNoCase(_tstring(str1), _tstring(str2));
}

_tstring CStrUtils::WStrToTStr(const std::wstring str)
{
#ifdef _UNICODE
	return str;
#else
	return _WStrToOtherStr(CP_ACP,str);
#endif 
}

_tstring CStrUtils::AStrToTStr(const std::string str)
{
#ifdef _UNICODE
	return _OtherStrToWStr(CP_ACP,str);
#else
	return str;
#endif
}

_tstring CStrUtils::U8StrToStr(const std::string& str)
{
#ifdef _UNICODE
	return _OtherStrToWStr(CP_UTF8,str);
#else
	return _WStrToOtherStr(CP_ACP, _OtherStrToWStr(CP_UTF8,str));
#endif 

}

std::wstring CStrUtils::TStrToWStr(const _tstring& str)
{
#ifdef _UNICODE
	return str;
#else
	return _OtherStrToWStr(CP_ACP,str);
#endif 
}

std::string CStrUtils::TStrToAStr(const _tstring& str)
{
#ifdef _UNICODE
	return _WStrToOtherStr(CP_ACP,str);
#else
	return str;
#endif 
}

std::string CStrUtils::TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODE
	return _WStrToOtherStr(CP_UTF8,str);
#else
	return _WStrToOtherStr(CP_UTF8,_OtherStrToWStr(CP_ACP,str));
#endif
}

std::string CStrUtils::WStrToAStr(const std::wstring& wstr)
{
	return _WStrToOtherStr(CP_ACP, wstr);
}

std::string CStrUtils::WStrToU8Str(const std::wstring wstr)
{
	return _WStrToOtherStr(CP_UTF8, wstr);
}

std::wstring CStrUtils::AStrToWStr(const std::string& str)
{
	return _OtherStrToWStr(CP_ACP,str);
}

std::string CStrUtils::AStrToU8Str(const std::string& str)
{
	return _WStrToOtherStr(CP_UTF8,_OtherStrToWStr(CP_ACP,str));
}

std::wstring CStrUtils::U8StrToWStr(const std::string& str)
{
	return _OtherStrToWStr(CP_UTF8,str);
}

std::string CStrUtils::U8StrToAStr(const std::string& str)
{
	return _WStrToOtherStr(CP_ACP,_OtherStrToWStr(CP_UTF8,str));
}

BOOL CStrUtils::UniqueA(std::vector<std::string>& slist)
{
	if (slist.empty())
	{
		return FALSE;
	}

	int num = (int)slist.size();
	for (int i = 0; i < num; i++)
	{
		int c = i + 1;
		for (int k = c; k < num; k++)
		{
			if (_strcmpi(slist[i].c_str(), slist[k].c_str()))
			{
				slist[c] = slist[k];
				c++;
			}
		}
		for (int j = 0; j < num - c; j++)
		{
			slist.pop_back();
		}
		num = (int)slist.size();
	}
	return TRUE;
}

bool CStrUtils::IsDoubleStrA(const CHAR* validString)
{
	CHAR* end = NULL;
	double val = strtod(validString, &end);
	return end != validString && *end == '' && val != HUGE_VAL && val != 0;
}

std::string CStrUtils::_WStrToOtherStr(UINT CodePage, const std::wstring& str)
{
	std::string strResult;
	LPSTR lpMultiByteStr = NULL;

	do
	{
		int nConverted = 0;
		nConverted = ::WideCharToMultiByte(CodePage,0,str.c_str(),-1,NULL,0,NULL,NULL);
		if (nConverted == 0)
		{
			break;
		}

		lpMultiByteStr = (LPSTR) ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nConverted);
		if (NULL == lpMultiByteStr)
		{
			break;
		}
		nConverted = ::WideCharToMultiByte(CodePage,0,str.c_str(),-1, lpMultiByteStr,nConverted,NULL,NULL);
		if(0 == nConverted)
		{
			break;
		}

		strResult = lpMultiByteStr;

	} while (false);

	if (lpMultiByteStr)
	{
		::HeapFree(::GetProcessHeap(),0,lpMultiByteStr);
		lpMultiByteStr = NULL;
	}

	return strResult;
}

std::wstring CStrUtils::_OtherStrToWStr(UINT CodePage, const std::string& str)
{
	std::wstring strResult;
	LPWSTR lpWideStr = NULL;

	do
	{
		int nConverted = 0;
		nConverted = ::MultiByteToWideChar(CodePage,0,str.c_str(),-1,NULL,0);
		if (0 == nConverted)
		{
			break;
		}

		lpWideStr = (LPWSTR)::HeapAlloc(::GetProcessHeap(),HEAP_ZERO_MEMORY,nConverted * sizeof(WCHAR));
		if (NULL == lpWideStr)
		{
			break;
		}

		nConverted = ::MultiByteToWideChar(CodePage,0,str.c_str(),-1,lpWideStr,nConverted);
		if (0 == nConverted)
		{
			break;
		}

		strResult = lpWideStr;
	
	} while (true);

	if (NULL != lpWideStr)
	{
		::HeapFree(::GetProcessHeap(), 0, lpWideStr);
		lpWideStr = NULL;
	}

	return strResult;
}