一、字符串函数
在面对字符串操作时,有大量函数可供使用,但用法常常不明,故在此介绍。
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:如果
str1
比str2
大(按字典顺序比较)。 - 返回 < 0:如果
str1
比str2
小(按字典顺序比较)。
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;
}