15、字符串函数

(1) strcpy( )
strcpy(str1,str2);//将str2的字符拷贝至str1数组中,注意,str2遇到\0结束,会将\0拷贝至str1 (会把str1的内容覆盖掉)
strncpy(str1,str2,n);  // 将str2中的前n个字符拷贝至str1中,如果拷贝时不足n个,遇到\0拷贝结束
(2) strcat( )
strcat(str1,str2)  //将str2字符数组中的字符连接到str1字符数组中,遇到\0结束
strncat(str1,str2,n) //将str2字符数组中的n个字符连接到str1字符数组中,遇到\0结束
(3) strcmp ( ) 注意: 比较时遇到\0结束比较
int main()
{
        char  str1[] = "a\0bcdef";
        char str2[] = "a\0cdrrr";
        //str1数组中和str2数组拿出一个元素进行比较,相等继续往后比较
        //比较的是字符的ascii值
        //如果str1> str2  返回值等于1
        //如果str1==  str2  返回值等于0
        //如果str1 <str2  返回值等于 - 1
        //printf("%d\n",strcmp(str1,str2));
        printf("%d\n", strncmp(str1, str2,3));
        system("pause");
        return 0;
}
(4) sprintf() : 组包函数
int len = sprintf(buf,"格式","数据");  // 将数据安装格式组包,存放在数组buf中,
// len : sprintf函数的返回值是组完包的有效长度
(5) sscanf() 拆包函数

sscanf (buf,"格式",数据); // 将buf的内容格式化输出到数据

int main()
{
        //%d    0-9的字符
        int  year =0 ;
        int  month = 0;
        int day = 0;
        char  buf[1024] = "beijing:2018:t:10:20";
        //scanf("%d:%d:%d",&year,&month,&day);//从键盘按照相应的格式获取数据
        sscanf(buf, "beijing:%d:t:%d:%d", &year, &month, &day);//从buf中按照`相应的格式获取数据
        printf("%d %d %d\n",year,month,day);
        system("pause");
        return 0;
}
(6) strchr( ) 字符串比较函数

char *p = strchr(buf,ch)   // 在buf字符数组中查找字符ch出现的位置,如果成功返回此字符出现位置的地址,如果没有找到,返回NULL

(7) strstr( )

strstr(str1,str2)  // 在str1字符数组中查找str2字符串出现的位置,并且返回这个位置的地址

(8) strtok ( )
char *strtok(char *str, const char *delim)
' str—要被分解的字符串
' delim—用作分隔符的字符(可以是一个,也可以是集合)'
' 该函数返回被分解的第一个子字符串,若无可检索的字符串,则返回空指针'
    
#include <string.h>
#include <stdio.h>
 
int main () {
   char str[80] = "aaa - bbb - ccc";
   const char s[2] = "-";   //  字符串里可以是一个分隔符,也可以是分隔符的集合, 他会在底层给这些字符串置 '\0'
   char *token;
   
   /* 获取第一个子字符串 */
   token = strtok(str, s);
   
   /* 继续获取其他的子字符串 */
   while( token != NULL ) {
      printf( "%s\n", token ); 
    
      token = strtok(NULL, s);  // 而且会记录下位置,所以第二次切割一定是从NULL切割,也就是第一个分隔符开始
   }
   
   return(0);
}

// 第一次调用strtok(),传入的参数str是要被分割的字符串{aaa - bbb -ccc},而成功后返回的是第一个子字符串{aaa};
// 而第二次调用strtok的时候,传入的参数应该为NULL,使得该函数默认使用上一次未分割完的字符串继续分割 ,就从上一次分割的位置{aaa-}作为本次分割的起始位置,直到分割结束。

strtok()注意事项:

(1)delim 替换成 \0

  • 在这个函数里strtok()在分解字符串的时候,第一个参数str是在不断变化的,这个函数是在改变原字符串,把原字符串{aaa - bbb - ccc}通过第二个参数delim将所有的分割符{-}替换成字符串结束标志字符{\0},则原字符串变化为{aaa \0 bbb \0 ccc}

// 错误示范案例
#include <string.h>
#include <stdio.h>
 
int main () {
   char  *str = "aaa - bbb - ccc"; //****注意这里!
   const char s[2] = "-";
   char *token;
   
   /* 获取第一个子字符串 */
   token = strtok(str, s);
   
   /* 继续获取其他的子字符串 */
   while( token != NULL ) {
      printf( "%s\n", token );
    
      token = strtok(NULL, s);
   }
   return(0);
}

注意:在这里,我实现函数的时候将字符串数组直接用指针指向它了,结果运行错误,后面发现虽然第一个参数是可以传指针,但我们要考虑空间内存布局,在strtok()函数里是delim的{分隔符}替换{ \0}改变原字符串而我们用指针指向这个字符串的时候,其实指向的是字符串常量,它的内存分布在文字常量区是不可被改变的,所以出现了错误!

(2)delim分隔符

  • strtok()的第二个参数delim,其实delim里可以是所有分隔符的集合,比如{aaa - bbb ; ccc | ddd},第二个参数delim可以是{-;|},用一个或多个分隔符去分解字符串都可以

(3)delim分隔符可不可以出现在第一个字符?

  • 答案是:可以

  • 当strtok分解的字符串首字符就是分隔符,那么strtok()会忽略首个分隔符,直接从第二个分隔符往下继续分解,例如:{- aaa - bbb - ccc} 那么strtok()会忽略第一个{-},还是以{aaa - bbb - ccc}的字符串形式继续分解。

 strtok()函数源码:

char *strtok(char *str,const char *delim)
{
    static char *next_start = NULL;  //保存到静态存储区,函数返回后不会被销毁

    if(str == NULL && (str = next_start) == NULL)
    {
        return NULL;
    }

    char   *s = str;
    const char *t = NULL;

    while(*s)
    {
        t = delim;

        while(*t)
        {
            if(*t == *s)
            {
                next_start = s + 1;

                if(s == str)    //第一个字符就是分隔符
                {
                    str = next_start;
                    break;
                }
                else
                {
                    *s = '\0';
                    return str;
                }
            }
            else
            {
                t++;
            }
        }

        s++;
    }

    return NULL;
}

注意: char* str 与 char str[]区别
一、C语言中的几个存储区
    一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域;未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

4、常量区:常量字符串就是放在这里的。程序结束后由系统释放。

5、程序代码区
    

  int a=0;    //全局初始化区
  char *p1; //全局未初始化区
  main()
  {
      int b;                  //栈
      char s[]="abc";         //栈
      char *p2;               //栈
      char *p3="123456";      //123456\0在常量区,p3在栈上。
      static int c=0;         //全局(静态)初始化区
      p1 = (char*)malloc(10);
      p2 = (char*)malloc(20);   //分配得来得10和20字节的区域就在堆区。
      
      
      strcpy(p1,"123456");      //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
 }

二、问题:
    使用两个字符串指针,使用memcpy为什么会报段错误?
1、问题
void test(void)
{
        char * a = "hello world1";
        char * b = "hello world2";
        memcpy(a, b, strlen(b) + 1); // 此时会直接段错误,  不能修改 a指向的字符串常量  ,(关键问题就是a没有开辟空间直接指向常量区)
		return 0;
}

void main(void){
	test();
}
解释:
char *str = "hello world"//一份拷贝,"hello world"是常量字符串存在静态数据区,把该字符串常量存在的静态数据区的首地址赋给指针str,所以test()函数退出时,该字符串常量所在内存不会被回收,故能通过指针访问; 而memcpy的第一个参数是变量,不能是常量,因此在此时会报错。
2、问题:
void test(void)
{
        char  a[] = "hello world1";
        char  b[] = "hello world2";
        memcpy(a, b, strlen(b) + 1); // 此时程序正常
		return 0;
}

void main(void){
	test();
}
解释:
str[] = "hello world" (自己开辟了空间)// "hello world"常量字符串在内存中有两份拷贝,一份在动态分配的栈中,一份在静态存储区,str[]数组为函数内部局部变量,存储在栈上,在test()函数退出时,栈要清空,局部变量的内存也被清空
void c(int n,char *pName)
{
    char *a[4] = {"aaa","bbb","ccc","ddd"};
    pName = a[n];
}

void main()
{
    int n=0;
    char *pName = "DB";

    c(2,pName);
    printf("%s\n",pName);    //输出为DB
}

解释:
//输出DB,因为char *pName = “DB”;已经使得pName指向了DB,但c(2,pName);并不能改变pName指向的地址。形象点说就是:我有一个箱子给你用,你可以在里面装东西,但是你不能把我的箱子换成另外一个给我。在这里指的是不能函数调用不能使pName变成函数中的二维数组a

atoi() : 将字符串转整数

atof(): 将字符串转float类型的数据

int n = atoi( 需要转的字符串)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值