开发工具-字符串基本操作

113 阅读10分钟

安全的strcpy函数。

// dest:目标字符串,不需要初始化,在STRCPY函数中有初始化代码。
// destlen:目标字符串dest占用内存的大小。
// src:原字符串。
// 返回值:目标字符串dest的地址。
// 注意,超出dest容量的内容将丢弃。
char *STRCPY(char* dest,const size_t destlen,const char* src)
{
  if (dest==0) return 0;    // 判断空指针。
  memset(dest,0,destlen);   // 初始化dest。
  // memset(dest,0,sizeof(dest));   // 不能这么写,在64位系统中,sizeof(dest)永远是8。
  if (src==0) return dest;

  if (strlen(src)>destlen-1) strncpy(dest,src,destlen-1); 
  else strcpy(dest,src);

  return dest;
}

int main(int argc,char *argv[])
{
  char str[11];   // 字符串str的大小是11字节。
  STRCPY(str,sizeof(str),"google");  // 待复制的内容没有超过str可以存放字符串的大小。
  printf("str=%s=\n",str);    // 出输结果是str=google=
  STRCPY(str,sizeof(str),"www.google.com");  // 待复制的内容超过了str可以存放字符串的大小。
  printf("str=%s=\n",str);    // 出输结果是str=www.google=
}

安全的strncpy函数。

// dest:目标字符串,不需要初始化,在STRCPY函数中有初始化代码。
// destlen:目标字符串dest占用内存的大小。
// src:原字符串。
// n:待复制的字节数。
// 返回值:目标字符串dest的地址。
// 注意,超出dest容量的内容将丢弃。
char *STRNCPY(char* dest,const size_t destlen,const char* src,size_t n)
{
  if (dest==0) return 0;    // 判断空指针。
  memset(dest,0,destlen);   // 初始化dest。
  // memset(dest,0,sizeof(dest));   // 不能这么写,在64位系统中,sizeof(dest)永远是8。
  if (src==0) return dest;

  if (n>destlen-1) strncpy(dest,src,destlen-1); 
  else strncpy(dest,src,n);

  return dest;
}

int main()
{
  char str[11];   // 字符串str的大小是11字节。
  STRNCPY(str,sizeof(str),"google",5);  // 待复制的内容没有超过str可以存放字符串的大小。
  printf("str=%s=\n",str);    // 出输结果是str=googl=
  STRNCPY(str,sizeof(str),"www.google.com",8);  // 待复制的内容没有超过str可以存放字符串的大小。
  printf("str=%s=\n",str);    // 出输结果是str=www.goog=
  STRNCPY(str,sizeof(str),"www.google.com",17);  // 待复制的内容超过了str可以存放字符串的大小。
  printf("str=%s=\n",str);    // 出输结果是str=www.google=
}

安全的strcat函数

// dest:目标字符串,注意,如果dest从未使过,那么需要至少一次初始化。
// destlen:目标字符串dest占用内存的大小。
// src:待追加字符串。
// 返回值:目标字符串dest的地址。
// 注意,超出dest容量的内容将丢弃。
char *STRCAT(char* dest,const size_t destlen,const char* src)
{
  if (dest==0) return 0;    // 判断空指针。
  if (src==0) return dest;
  unsigned int left=destlen-1-strlen(dest);
  if (strlen(src)>left) { strncat(dest,src,left); dest[destlen-1]=0; }
  else strcat(dest,src);
  return dest;
}

int main()
{
  char str[11];   // 字符串str的大小是11字节。
  STRCPY(str,sizeof(str),"www");  
  STRCAT(str,sizeof(str),".fr");  // str原有的内容加上待追加的内容没有超过str可以存放的大小。
  printf("str=%s=\n",str);        // 出输结果是str=www.fr=
  STRCAT(str,sizeof(str),"eecplus.net");  // str原有的内容加上待追加的内容超过了str可以存放的大小。
  printf("str=%s=\n",str);                // 出输结果是str=www.freecp=
}

安全的strncat函数

// dest:目标字符串,注意,如果dest从未使过,那么需要至少一次初始化。
// destlen:目标字符串dest占用内存的大小。
// src:待追加字符串。
// n:待追加的字节数。
// 返回值:目标字符串dest的地址。
// 注意,超出dest容量的内容将丢弃。
char *STRNCAT(char* dest,const size_t destlen,const char* src,size_t n)
{
  if (dest==0) return 0;    // 判断空指针。
  if (src==0) return dest;
  size_t left=destlen-1-strlen(dest);
  if (n>left) { strncat(dest,src,left); dest[destlen-1]=0; }
  else strncat(dest,src,n);
  return dest;
}

int main()
{
  char str[11];   // 字符串str的大小是11字节。
  STRCPY(str,sizeof(str),"www");  
  STRNCAT(str,sizeof(str),".free",10);  // str原有的内容加上待追加的内容没有超过str可以存放的大小。
  printf("str=%s=\n",str);              // 出输结果是str=www.free=
  STRCPY(str,sizeof(str),"www");  
  STRNCAT(str,sizeof(str),".freecplus.com",6);  // str原有的内容加上待追加的内容没有超过str可以存放的大小。
  printf("str=%s=\n",str);                      // 出输结果是str=www.freec=
  STRCPY(str,sizeof(str),"www");  
  STRNCAT(str,sizeof(str),".freecplus.com",10); // str原有的内容加上待追加的内容超过了str可以存放的大小。
  printf("str=%s=\n",str);                      // 出输结果是str=www.freecp=
}

安全的sprintf函数

// 将可变参数(...)按照fmt描述的格式输出到dest字符串中。
// dest:输出字符串,不需要初始化,在SPRINTF函数中会对它进行初始化。
// destlen:输出字符串dest占用内存的大小,如果格式化后的字符串内容的长度大于destlen-1,后面的内容将丢弃。
// fmt:格式控制描述。
// ...:填充到格式控制描述fmt中的参数。
// 返回值:格式化后的内容的长度,程序员一般不关心返回值。
int SPRINTF(char *dest,const size_t destlen,const char *fmt,...)
{
  if (dest==0) return -1;    // 判断空指针。
  memset(dest,0,destlen);
  // memset(dest,0,sizeof(dest));   // 不能这么写,在64位系统中,sizeof(dest)永远是8。
  va_list arg;
  va_start(arg,fmt);
  int ret=vsnprintf(dest,destlen,fmt,arg );
  va_end(arg);
  return ret;
}

int main()
{
  char str[21];   // 字符串str的大小是21字节。

  SPRINTF(str,sizeof(str),"name:%s,no:%d","messi",10);
  printf("str=%s=\n",str);    // 出输结果是str=name:messi,no:10=

  SPRINTF(str,sizeof(str),"name:%s,no:%d,job:%s","messi",10,"striker");
  printf("str=%s=\n",str);    // 出输结果是str=name:messi,no:10,job=
}

安全的snprintf函数

// 将可变参数(...)按照fmt描述的格式输出到dest字符串中。
// dest:输出字符串,不需要初始化,在SNPRINTF函数中会对它进行初始化。
// destlen:输出字符串dest占用内存的大小,如果格式化后的字符串内容的长度大于destlen-1,后面的内容将丢弃。
// n:把格式化后的字符串截取n-1存放到dest中,如果n>destlen,则取destlen-1。
// fmt:格式控制描述。
// ...:填充到格式控制描述fmt中的参数。
// 返回值:格式化后的内容的长度,程序员一般不关心返回值。
int SNPRINTF(char *dest,const size_t destlen,size_t n,const char *fmt,...)
{
  if (dest==0) return -1;    // 判断空指针。
  memset(dest,0,destlen);
  // memset(dest,0,sizeof(dest));   // 不能这么写,在64位系统中,sizeof(dest)永远是8。
  int len=n;
  if (n>destlen) len=destlen;
  va_list arg;
  va_start(arg,fmt);
  int ret=vsnprintf(dest,len,fmt,arg );
  va_end(arg);
  return ret;
}

int main()
{
  char str[26];   // 字符串str的大小是11字节。

  SNPRINTF(str,sizeof(str),5,"messi");
  printf("str=%s=\n",str);    // 出输结果是str=mess=

  SNPRINTF(str,sizeof(str),9,"name:%s,no:%d,job:%s","messi",10,"striker");
  printf("str=%s=\n",str);    // 出输结果是str=name:mes=

  SNPRINTF(str,sizeof(str),30,"name:%s,no:%d,job:%s","messi",10,"striker");
  printf("str=%s=\n",str);    // 出输结果是str=name:messi,no:10,job:stri=
}

删除字符串左边指定的字符

// str:待处理的字符串。
// chr:需要删除的字符。
void DeleteLChar(char *str,const char chr)
{
  if (str == 0) return;
  if (strlen(str) == 0) return;
  char strTemp[strlen(str)+1];
  int iTemp=0;
  memset(strTemp,0,sizeof(strTemp));
  strcpy(strTemp,str);
  while ( strTemp[iTemp] == chr )  iTemp++;
  memset(str,0,strlen(str)+1);
  strcpy(str,strTemp+iTemp);
  return;
}

删除字符串右边指定的字符

// str:待处理的字符串。
// chr:需要删除的字符。
void DeleteRChar(char *str,const char chr)
{
  if (str == 0) return;
  if (strlen(str) == 0) return;
  int istrlen = strlen(str);
  while (istrlen>0)
  {
    if (str[istrlen-1] != chr) break;
    str[istrlen-1]=0;
    istrlen--;
  }
}

删除字符串左右两边指定的字符

// str:待处理的字符串。
// chr:需要删除的字符。
void DeleteLRChar(char *str,const char chr)
{
  DeleteLChar(str,chr);
  DeleteRChar(str,chr);
}

int main()
{
  char str[11];   // 字符串str的大小是11字节。

  STRCPY(str,sizeof(str),"  西施  ");
  DeleteLChar(str,' ');  // 删除str左边的空格
  printf("str=%s=\n",str);    // 出输结果是str=西施  =

  STRCPY(str,sizeof(str),"  西施  ");
  DeleteRChar(str,' ');  // 删除str右边的空格
  printf("str=%s=\n",str);    // 出输结果是str=  西施=

  STRCPY(str,sizeof(str),"  西施  ");
  DeleteLRChar(str,' ');  // 删除str两边的空格
  printf("str=%s=\n",str);    // 出输结果是str=西施=
}

把字符串中的小写字母转换成大写,忽略不是字母的字符

// str:待转换的字符串,支持char[]和string两种类型。
void ToUpper(char *str)
{
  if (str == 0) return;
  if (strlen(str) == 0) return;
  int istrlen=strlen(str);
  for (int ii=0;ii<istrlen;ii++)
  {
    if ( (str[ii] >= 'a') && (str[ii] <= 'z') ) str[ii]=str[ii] - 32;
  }
}

void ToUpper(string &str)
{
  if (str.empty()) return;
  char strtemp[str.size()+1];
  STRCPY(strtemp,sizeof(strtemp),str.c_str());
  ToUpper(strtemp);
  str=strtemp;
  return;
}

int main()
{
  char str1[31];   // C语言风格的字符串。

  STRCPY(str1,sizeof(str1),"12abz45ABz8西施。");
  ToUpper(str1);      // 把str1中的小写字母转换为大写。
  printf("str1=%s=\n",str1);    // 出输结果是str1=12ABZ45ABZ8西施。=

  STRCPY(str1,sizeof(str1),"12abz45ABz8西施。");
  ToLower(str1);      // 把str1中的大写字母转换为小写。
  printf("str1=%s=\n",str1);    // 出输结果是str1=12abz45abz8西施。=

  string str2;    // C++语言风格的字符串。
  
  str2="12abz45ABz8西施。";
  ToUpper(str2);      // 把str2中的小写字母转换为大写。
  printf("str2=%s=\n",str2.c_str());    // 出输结果是str2=12ABZ45ABZ8西施。=

  str2="12abz45ABz8西施。";
  ToLower(str2);      // 把str2中的大写字母转换为小写。
  printf("str2=%s=\n",str2.c_str());    // 出输结果是str1=12abz45abz8西施。=
}

把字符串中的大写字母转换成小写,忽略不是字母的字符

// str:待转换的字符串,支持char[]和string两种类型。
void ToLower(char *str)
{
  if (str == 0) return;
  if (strlen(str) == 0) return;
  int istrlen=strlen(str);
  for (int ii=0;ii<istrlen;ii++)
  {
    if ( (str[ii] >= 'A') && (str[ii] <= 'Z') ) str[ii]=str[ii] + 32;
  }
}

void ToLower(string &str)
{
  if (str.empty()) return;
  char strtemp[str.size()+1];
  STRCPY(strtemp,sizeof(strtemp),str.c_str());
  ToLower(strtemp);
  str=strtemp;
  return;
}

字符串替换函数

// 在字符串str中,如果存在字符串str1,就替换为字符串str2。
// str:待处理的字符串。
// str1:旧的内容。
// str2:新的内容。
// bloop:是否循环执行替换。
// 注意:
// 1、如果str2比str1要长,替换后str会变长,所以必须保证str有足够的长度,否则内存会溢出。
// 2、如果str2中包函了str1的内容,且bloop为true,存在逻辑错误,将不执行任何替换。
void UpdateStr(char *str,const char *str1,const char *str2,bool bloop)
{
  if (str == 0) return;
  if (strlen(str) == 0) return;
  if ( (str1 == 0) || (str2 == 0) ) return;

  // 如果bloop为true并且str2中包函了str1的内容,直接返回,因为会进入死循环,最终导致内存溢出。
  if ( (bloop==true) && (strstr(str2,str1)>0) ) return;

  // 尽可能分配更多的空间,但仍有可能出现内存溢出的情况,最好优化成string。
  int ilen=strlen(str)*10;
  if (ilen<1000) ilen=1000;
  char strTemp[ilen];
  char *strStart=str;
  char *strPos=0;
  while (true)
  {
    if (bloop == true)
    {
      strPos=strstr(str,str1);
    }
    else
    {
      strPos=strstr(strStart,str1);
    }

    if (strPos == 0) break;

    memset(strTemp,0,sizeof(strTemp));
    STRNCPY(strTemp,sizeof(strTemp),str,strPos-str);
    STRCAT(strTemp,sizeof(strTemp),str2);
    STRCAT(strTemp,sizeof(strTemp),strPos+strlen(str1));
    strcpy(str,strTemp);

    strStart=strPos+strlen(str2);
  }
}


int main()
{
  char str[301];

  STRCPY(str,sizeof(str),"name:messi,no:10,job:striker.");
  UpdateStr(str,":","=");     // 把冒号替换成等号。
  printf("str=%s=\n",str);    // 出输结果是str1=name=messi,no=10,job=striker.=

  STRCPY(str,sizeof(str),"name:messi,no:10,job:striker.");
  UpdateStr(str,"name:","");    // 把"name:"替换成"",相当于删除内容"name:"。
  printf("str=%s=\n",str);      // 出输结果是str1=messi,no:10,job:striker.=

  STRCPY(str,sizeof(str),"messi----10----striker");  
  UpdateStr(str,"--","-",false);    // 把两个"--"替换成一个"-",bLoop参数为false。
  printf("str=%s=\n",str);          // 出输结果是str1=messi--10--striker=

  STRCPY(str,sizeof(str),"messi----10----striker");  
  UpdateStr(str,"--","-",true);    // 把两个"--"替换成一个"-",bLoop参数为true。
  printf("str=%s=\n",str);         // 出输结果是str1=messi-10-striker=

  STRCPY(str,sizeof(str),"messi-10-striker");  
  UpdateStr(str,"-","--",false);    // 把一个"-"替换成两个"--",bLoop参数为false。
  printf("str=%s=\n",str);          // 出输结果是str1=messi--10--striker=

  STRCPY(str,sizeof(str),"messi-10-striker");  

  // 以下代码把"-"替换成"--",bloop参数为true,存在逻辑错误,UpdateStr将不执行替换。
  UpdateStr(str,"-","--",true);    // 把一个"-"替换成两个"--",bloop参数为true。
  printf("str=%s=\n",str);         // 出输结果是str1=messi-10-striker=
}

从一个字符串中提取出数字的内容,存放到另一个字符串中

// src:源字符串。
// dest:目标字符串。
// bsigned:是否包括符号(+和-),true-包括;false-不包括。
// bdot:是否包括小数点的圆点符号,true-包括;false-不包括。
void PickNumber(const char *src,char *dest,const bool bsigned,const bool bdot)
{
  if (dest==0) return;    // 判断空指针。
  if (src==0) { strcpy(dest,""); return; }
  char strtemp[strlen(src)+1];
  memset(strtemp,0,sizeof(strtemp));
  strcpy(strtemp,src);
  DeleteLRChar(strtemp,' ');
  int ipossrc,iposdst,ilen;
  ipossrc=iposdst=ilen=0;
  ilen=strlen(strtemp);

  for (ipossrc=0;ipossrc<ilen;ipossrc++)
  {
    if ( (bsigned==true) && (strtemp[ipossrc] == '+') )
    {
      dest[iposdst++]=strtemp[ipossrc]; continue;
    }
    if ( (bsigned==true) && (strtemp[ipossrc] == '-') )
    {
      dest[iposdst++]=strtemp[ipossrc]; continue;
    }
    if ( (bdot==true) && (strtemp[ipossrc] == '.') )
    {
      dest[iposdst++]=strtemp[ipossrc]; continue;
    }
    if (isdigit(strtemp[ipossrc])) dest[iposdst++]=strtemp[ipossrc];
  }
  dest[iposdst]=0;
}

int main()
{
  char str[26];   // 字符串str的大小是11字节。

  STRCPY(str,sizeof(str),"iab+12.3xy");
  PickNumber(str,str,false,false);
  printf("str=%s=\n",str);    // 出输结果是str=123=

  STRCPY(str,sizeof(str),"iab+12.3xy");
  PickNumber(str,str,true,false);
  printf("str=%s=\n",str);    // 出输结果是str=+123=

  STRCPY(str,sizeof(str),"iab+12.3xy");
  PickNumber(str,str,true,true);
  printf("str=%s=\n",str);    // 出输结果是str=+12.3=
}

正则表达式,判断一个字符串是否匹配另一个字符串

// str:需要判断的字符串,精确表示的字符串,如文件名"_public.cpp"。
// rules:匹配规则表达式,用星号"*"表示任意字符串,多个字符串之间用半角的逗号分隔,如"*.h,*.cpp"。
// 注意,str参数不支持"*",rules参数支持"*",函数在判断str是否匹配rules的时候,会忽略字母的大小写。
bool MatchStr(const string &str,const string &rules)
{
  // 如果用于比较的字符是空的,返回false
  if (rules.size() == 0) return false;

  // 如果被比较的字符串是"*",返回true
  if (rules == "*") return true;

  int ii,jj;
  int  iPOS1,iPOS2;
  CCmdStr CmdStr,CmdSubStr;
  string strFileName,strMatchStr;
  strFileName=str;
  strMatchStr=rules;

  // 把字符串都转换成大写后再来比较
  ToUpper(strFileName);
  ToUpper(strMatchStr);

  CmdStr.SplitToCmd(strMatchStr,",");
  for (ii=0;ii<CmdStr.CmdCount();ii++)
  {
    // 如果为空,就一定要跳过,否则就会被配上
    if (CmdStr.m_vCmdStr[ii].empty() == true) continue;
    iPOS1=iPOS2=0;
    CmdSubStr.SplitToCmd(CmdStr.m_vCmdStr[ii],"*");
    for (jj=0;jj<CmdSubStr.CmdCount();jj++)
    {
      // 如果是文件名的首部
      if (jj == 0)
      {
        if (strncmp(strFileName.c_str(),CmdSubStr.m_vCmdStr[jj].c_str(),CmdSubStr.m_vCmdStr[jj].size()) != 0) break;
      }

      // 如果是文件名的尾部
      if (jj == CmdSubStr.CmdCount()-1)
      {
        if (strcmp(strFileName.c_str()+strFileName.size()-CmdSubStr.m_vCmdStr[jj].size(),CmdSubStr.m_vCmdStr[jj].c_str()) != 0) break;
      }
      iPOS2=strFileName.find(CmdSubStr.m_vCmdStr[jj],iPOS1);
      if (iPOS2 < 0) break;
      iPOS1=iPOS2+CmdSubStr.m_vCmdStr[jj].size();
    }
    if (jj==CmdSubStr.CmdCount()) return true;
  }
  return false;
}

int main()
{
  char filename[301];  
  STRCPY(filename,sizeof(filename),"_public.h");
  
  // 以下代码将输出yes。
  if (MatchStr(filename,"*.h,*.cpp")==true) printf("yes\n");
  else printf("no\n");

  // 以下代码将输出yes。
  if (MatchStr(filename,"*.H")==true) printf("yes\n");
  else printf("no\n");

  // 以下代码将输出no。
  if (MatchStr(filename,"*.cpp")==true) printf("yes\n");
  else printf("no\n");

  return 0;
}