字符串函数

一、字符串函数

在面对字符串操作时,有大量函数可供使用,但用法常常不明,故在此介绍。

stdio.h的函数

fgets

#include <stdio.h>

int main() {
    char buffer[100];
    // 使用 fgets 读取输入
    fgets(buffer, sizeof(buffer), stdin);
    printf("You entered: %s", buffer);
    return 0;
}

stdin为默认格式,sizeof(buffer)是缓冲区,fgets会读入换行符,要求使用者手动去除。 

string.h

strlen

strlen()常用于输出字符串组的长度,示例如下

#include <stdio.h>
#include <string.h>

int main() {
    char s[10] = "1234567";
    printf("strlen(s) = %d\n",strlen(s));
	printf("sizeof(s) = %d\n",sizeof(s));
	printf("char s[10] = ");
	for(int i=0;i<=9;i++)//逐位输出的时候,printf不会用%c输出\0 
		printf("%c",s[i]);
    return 0;
}

运行结果:

strlen(s) = 7
sizeof(s) = 10
char s[10] = 1234567

sizeof输出字节数,cahr类型一个空间有一个字节,就输出10:空间的数量。

strlen输出从开始的非空字符数,检索到第一个空字符'\0',即数字0就返回当前检索到的值。

如果不想从开始检索,可以用以下代码实现。

(笔者在编写时产生一个错误,在此贴出)

错误代码:

#include <stdio.h>
#include <string.h>

int main() {
    char s[10] = "\01234567";
    printf("strlen(s) = %d\n",strlen(s));
    printf("strlen(s+1) = %d\n",strlen(s+1));
	printf("sizeof(s) = %d\n",sizeof(s));
    return 0;
}

理想的结果应该是 

strlen(s) = 0
strlen(s+1) = 7
sizeof(s) = 10

但实际结果是

strlen(s) = 5
strlen(s+1) = 6
sizeof(s) = 10

实际上此处逻辑并没有问题,问题在于\012被认为是一个8进制数,也就是十进制的“10”

所以编译器认为数组内只有6个字符

若想达成预想效果,需要显性初始化字符串。

如下:

#include <stdio.h>
#include <string.h>
int main() {
    char s[10] = {'\0','1','2','3','4','\0'};
    printf("strlen(s) = %d\n",strlen(s));
    printf("strlen(s+1) = %d\n",strlen(s+1));
	printf("sizeof(s) = %d\n",sizeof(s));
    return 0;
}

运行结果:

strlen(s) = 0
strlen(s+1) = 4
sizeof(s) = 10

strcpy

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[50];  // 确保目标数组足够大

    strcpy(dest, src);  // 拷贝 src 到 dest
    printf("Source: %s\n", src);
    printf("Destination: %s\n", dest);

    return 0;
}

strcpy可以将B复制到A中,并在末尾填充终止符\0

可以用    strcpy(dest, src+2);来调整复制初始位置。

那么如何调整结束位置呢?

strncpy

strncpy会把源字符串的最多n个字符复制到目标字符串,不会自动填充\0

疑问1:假如源代码只有3个字符,n大于3会怎么样

答:剩下的为0

疑问2:假如目标字符串小于n怎么办

答:首先,满的字符串可以正常使用,也就是char a[5]={"12345"}是可以正常printf的。

其次,假如char a[4]被cpy了一个5的会发生什么呢?

devC++里面会把a扩充,其他编译器可能会产生未定义行为

现在知道,其他编译器必须要字符串结尾是0才可以。

#include <stdio.h>
#include <string.h>
int main() {
    char s[10] = "12345678";
    char copy[10]={0};
    strncpy(copy,s+1,5);//用strncpy只能复制到copy的0位 
    copy[5]='\0'; 
    printf("copy = {%s}",copy);
	return 0;
}

输出结果

copy={23456}

关于strcpy与strcat休止符检索的问题

#include<stdio.h>
#include<string.h>
int main(){
	char str[50];
	strcpy(str,"hello-world");
	printf("strcpy(str,\"hello-world\")\n");
	for(int i=0;i<=15;i++)
		printf("%-4c",str[i]);
	printf("\n");
	for(int i=0;i<=15;i++)
		printf("%-4d",str[i]);
	printf("\n");
	
	
	strcpy(&str[5],"none");
	printf("strcpy(&str[5],\"none\")\n");
	for(int i=0;i<=15;i++)
		printf("%-4c",str[i]);
	printf("\n");
	for(int i=0;i<=15;i++)
		printf("%-4d",str[i]);
	printf("\n");
	
	
	strcat(str,"ddl?");
	printf("strcat(str,\"ddl?\")\n");
	for(int i=0;i<=15;i++)
		printf("%-4c",str[i]);
	printf("\n");
	for(int i=0;i<=15;i++)
		printf("%-4d",str[i]);
	printf("\n");
} 

输出结果

strcpy(str,"hello-world")
h   e   l   l   o   -   w   o   r   l   d
104 101 108 108 111 45  119 111 114 108 100 0   0   0   0   0
strcpy(&str[5],"none")
h   e   l   l   o   n   o   n   e      d
104 101 108 108 111 110 111 110 101 0   100 0   0   0   0   0
strcat(str,"ddl?")
h   e   l   l   o   n   o   n   e   d   d   l   ?
104 101 108 108 111 110 111 110 101 100 100 108 63  0   0   0

不难看出,strcpy仅将源字符串复制到目标字符串,并多加一个休止符,不会将目标字符串后面全部位置都赋值成'\0'

值得注意的是strcat检索的是第一个\0,因为函数实现功能时用的思路是

while(*str)
    str++;
//遍历到第一个空字符

 memset

memset用于按字节填充数组

用法如下:

void* memset(void* prt , int value , size_t n)

其中prt是要操作的数组,void*表示可以对任意类型数组操作,value是要填充的值,n表示填充的字节数(memset只对字节进行操作,也就是说,它只能把一个字节的数据变成value,所以如果要对int及以上类型数据进行操作,不建议使用memset,清零除外)

示例代码:

#include<stdio.h>
#include<string.h> 
int main(){
	char a[100],b[101];
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	//示范清零a,b
	memset(a,1,sizeof(char)*3);
	//对前三个字节操作,此处可以写成 1 * 3
	int c[5]={1,2,3,4,5};
	memset(c,0,sizeof(int)*3);
	//清零前三,可写成 4 * 3
	memset(c+3,1,sizeof(int));
	//对第四位进行操作,此处得到的结果应该是
	//0000 0001 0000 0001 0000 0001 0000 0001
	//所以我猜测,结果应该是 16843009
	printf("打印a b c前五位\n");
	printf("a = {");
	for(int i=0;i<5;i++){
		printf("%d ",a[i]);
	}
	printf("}\n");
	printf("b = {");
	for(int i=0;i<5;i++){
		printf("%d ",b[i]);
	}
	printf("}\n");
	printf("c = {");
	for(int i=0;i<5;i++){
		printf("%d ",c[i]);
	}
	printf("}\n");
}

输出结果:

打印a b c前五位
a = {1 1 1 0 0 }
b = {0 0 0 0 0 }
c = {0 0 0 16843009 5 }

strncat

连接字符串的一个函数,把B数组的前n位连接在A数组后面。会自动加\0

检索到0结束,故复制范围可超出实际储存的字母个数。

实现示例:

char *StrCat( char *dest, const char *src ) {
assert(dest && src); //src 或 dest 为空指针
char *p = dest;
for (;*p;p++); 
assert(!(src>=dest && src<=p)); //这个约束什么意思?
for (;*src;src++,p++)
*p = *src; 
*(++p)= 0;
return dest;
}

assert会在检测条件为false时直接报错,第二处约束检查src是否落在dest与p中间 

*(++p)是加入终止符的操作

示例代码:

#include<stdio.h>
#include<string.h>
int main(){
	char m1[20]={"Hello,"};
	char m2[20]={"Hello,"};
	char m3[20]={"Hello,"};
	char n[10]={"world."};
	//char *strncat(char *dest, const char *src, size_t n);
	strncat(m1,n,6);
	strncat(m2,n,3);
	strncat(m3,n,10);
	printf("%s\n%s\n%s\n",m1,m2,m3);
} 

运行结果:

Hello,world.
Hello,wor
Hello,world.

strncmp

比较两个字符串的大小

比较方式相当于把两个字符串看成128进制的数,以起始为最高位比较,如果最高位相同就比下一位,直到比出高低,以字典序大小为准。

  • 返回 0:如果前 n 个字符相等。
  • 返回 > 0:如果 str1str2 大(按字典顺序比较)。
  • 返回 < 0:如果 str1str2 小(按字典顺序比较)。

strupr

strupr(str)会把小写字母转换成大写

strcspn

检索目标字符串中第一个出现源字符串第一个字符的索引

ctype.h

isspace

ispunct

二、字符串常见问题处理方法 

如何穷举字符串的全排序

题目

给定一个字符串 str,要求你输出该字符串的所有不同的排列。

  • 输入一个字符串 str,长度不超过 5,且只包含英文字母(大小写均可)。注:可能会出现某些字符重复如JWXww。

  • 输出该字符串的所有不同排列,按照字典序升序排列,且不出现重复的字符串。注:要求如cab在cba之前。

 思路

全排列通过递归实现,以abc为例,创立temp数组,第一位分别为'a','b','c',分三支,用for循环实现,每次用一个值之后在bo数组标记为1,表示已使用,接着再调用函数本身,向下一层写入新的值。写入新值的过程中,会检测到使用过的值而跳过。调用下一层函数结束之后,本层在跳到下一个循环之前先把用过的值“还回去”,把bo重新赋值。

检测层数来判断是否已经用完全部值,若是,则记录下来。

如此只要保证原字符串按升序排列,而递归从i=1开始搜素,则可保证升序输出。

示例代码
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
char str[6]={0};
char total[121][6]={0};
bool bo[6]={0};
char temp[6]={0};
int order=1;
int k=1;
void sort(char* a,int n){
	for(int i=1;i<=n;i++){
		for(int k=i+1;k<=n;k++){
			if(a[i]>=a[k]){
				int temp=a[i];
				a[i]=a[k];
				a[k]=temp;
			}
		}
	}
}
//此处应采用数组复制思路,不然递归回退没数组用了 
void func(int layer,int len){
	if(layer>len){
//layer超出数组右界,应该结束操作,储存数组,并且进入下一个数组 
		strncpy(total[order]+1,temp+1,len);
		order++;
		return;
	} 
	//大体思路为一直向temp里面存数,直到存完了就记录,回退一层继续循环 
	for(int i=1;i<=len;i++){//此处为对字符的遍历 
		if(bo[i]==1)
			continue;
		temp[layer]=str[i];
		bo[i]=1;
		func(layer+1,len);
		bo[i]=0;
	}
	return ;
}
int main(){	
	scanf("%s",str+1);
	int len=strlen(str+1);
	sort(str,len);
//	while(numberNew<=)
	func(1,len);

	for(int i=1;i<=order;i++){
        for(int k=1;k<i;k++){
            int count=0;
            for(int j=1;j<len;j++){
                if(total[i][j]==total[k][j])
                    count++;
            }
            if(count==len)
                memset(total[k],0,6);
        }
        for(int k=i+1;k<=order;k++){
            int count=0;
            for(int j=1;j<=len;j++){
                if(total[i][j]==total[k][j])
                    count++;
            }
            if(count==len)
                memset(total[k],0,6);
        }
		printf("%s\n",total[i]+1);
	}
	return 0;
} 

三、 一些奇怪的字符串操作

1、

char a[10];
a="string";
//以上非法
char *a;
a="string";
//以上合法

原因在于数组名是常量指针,不可修改

而定义出的指针变量可以指向常量"string"的首元素。

需要注意的是,字符串常量通常存储在只读内存中,不能修改

"hello,world" 是存储在程序的只读数据段中的字符串字面量,虽然用指针可以访问它,但不能修改它。如果尝试修改,可能会导致程序崩溃。

四、一些函数的实现

#include "my_string.h"
char *my_strcpy(char *dest, const char *src){
    int i=0;
    while(src[i]){
        dest[i]=src[i];
        i++;
    }
    dest[i]=0;
    return dest;
}
char *my_strncpy(char *dest, const char *src, size_t n){
    int len=my_strlen(src);
    if(n>=len){
    	strcpy(dest,src);
	}else{
		for(int i=0;i<=n-1;i++)
			dest[i]=src[i];
	}
    return dest;
}
int my_strlen(const char *str){
    int i=0;
    while(str[i])
        i++;
    return i;
}
int my_strcmp(const char *str1, const char *str2){
    int m,len1=strlen(str1),len2=strlen(str2);
    int lenmin=len1>len2?len2:len1;
	while(m<=lenmin){
        if(str1[m]>str2[m])
            return 1;
        else if(str1[m]<str2[m])
            return -1;
        else{
        	;
        }
        m++;
    }
    if(len1==len2)
    	return 0;
    if(len1>len2)
    	return 1;
    else 
    	return 0;
}
int my_strncmp(const char *str1, const char *str2, size_t n){
    size_t m=0;
    while(m<=n-1){
        if(str1[m]>str2[m])
            return 1;
        else if(str1[m]<str2[m])
            return -1;
        else{
            if(m==n-1)
                return 0;
        }
        m++;
    }
}
char *my_strcat(char *dest, const char *src){
    int len=my_strlen(dest);
    my_strcpy(dest+len,src);
    return dest;
}
char *my_strchr(const char *str, int c){
    int i=0;
    int len=my_strlen(str);
    while(i<=len-1){
        if(str[i]==c)
            break;
        i++;
    }
    if(i==len)
        return NULL;
    char* s=(char*)str+i;
    return s;
}
char *my_strstr(const char *s1, const char *s2){
    int len1=my_strlen(s1),len2=my_strlen(s2);
    for(int left=0;left+len2-1<=len1-1;left++){
        int right =left+len2-1;
        int count=0;
        for(int j=0,k=left;j<=len2-1;j++,k++){
            if(s1[k]==s2[j])
                count++;
        }
        if(count==len2){
            char* s=(char*)s1+left;
            return s;
        }
    }
    return NULL;
}
int my_atoi(const char *str){
    int len=my_strlen(str);
    int sum=0;int ten=1;
    char cha=str[0];int start=0;
    if(cha=='-')
    	start++;	
    for(int i=len-1;i>=start;i--){
        sum+=(str[i]-'0')*ten;
        ten*=10;
    }
    if(start)
    	sum=(-sum);
    return sum;
}
double my_atof(const char *str){
    char point ='.';
    char* ppoint =my_strchr(str,point);
    double num=0;
    int cha=0;
    if(str[0]=='-')
    	cha=1;
    int ten=1;
    for(char*p=ppoint-1;p>=str+cha;p--){
        num+=(*p-'0')*ten;
        ten*=10;
    }
    double te=0.1;
    for(char*p=ppoint+1;p<str+my_strlen(str);p++){
        num+=(*p-'0')*te;
        te*=0.1;
    }
    if(cha)
    	num=(-num);
    return num;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值