C 语言字符串操作

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>

char *strupr_t(char *str)
{
	char *orign=str;
	for (; *str!='\0'; str++)
		*str = toupper(*str);
	return orign;
}

char *strlowr_t(char *str)
{
	char *orign=str;
	for (; *str!='\0'; str++)
		*str = tolower(*str);
	return orign;
}

//字符串拷贝(strcpy, strncpy)
void testStrCpy(){
	char dest[1024] = {0};
	char *str = "abcde";
	//strcpy()函数会将源字符串中的结束符('\0')也拷贝到目的字符串中。
    //注意,strcpy()可能会导致溢出。
	strcpy(dest,str);

	printf("%s\n", dest);


	char dest2[7] = {0};
	char *src2 = "abcde";
	dest2[5] = 'A';
	//该函数从源字符串中拷贝n个字符到目的字符串;如果源字符串长度不足,则用 NULL 填充,以保证将n个字符写入目的字符串中;如果源字符串中前n个字符不包含字符串结束符,函数不会为目的字符串添加上结束符。
	//所以如果有需要,应该在拷贝后自己在目的字符串尾部添加结束符。
	strncpy(dest2, src2, 5);
	printf("%s\n", dest2);
}

void testStrCmp()
{
	char *s11 = "abcde";
	char *s12 = "abcef";
	char *s13 = "ad";
	//从这个结果可以发现,strcmp()是根据字典序来对字符串进行比较的。进一步的,还可以发现strcmp()的返回值是比较过程中最后一次比较时两个字符的值的差
	printf("compare(%s, %s) -> %d\n", s11, s12, strcmp(s11, s12));
	printf("compare(%s, %s) -> %d\n", s12, s13, strcmp(s12, s13));
	printf("compare(%s, %s) -> %d\n", s11, s13, strcmp(s11, s13));

	char *s21 = "abcde";
	char *s22 = "abcfg";
	char *s23 = "ad";
	//和strcmp()的区别是,strncmp()只对s1和s2的前n个字节进行比较。
	printf("compare(%s, %s, 4) -> %d\n", s21, s22, strncmp(s21, s22, 4));
	printf("compare(%s, %s, 2) -> %d\n", s21, s23, strncmp(s21, s23, 2));
	printf("compare(%s, %s, 3) -> %d\n", s22, s23, strncmp(s22, s23, 3));

	char *s31 = "AbcdE";
	char *s32 = "abcdE";
    //使用strcasecmp()应该包含 strings.h 而不是 string.h
    //strcasecmp()在比较时不区分大小写
    //strncasecmp()之于strcasecmp()就如strncmp()之于strcmp()
	printf("compare(%s, %s) with case -> %d\n", s31, s32, strcmp(s31, s32));
	printf("compare(%s, %s) ignore case -> %d\n", s31, s32, strcasecmp(s31, s32));
}

void testStrCat()
{
	char dest[1024] = "hello ";
	char *src = "world!";
	//strcat()首先会覆盖掉目的字符串的结束符,然后把源字符串的内容追加到后面,并在最后添加结束符。如果目的字符串缓冲区长度不够,将导致溢出。
    //strcat()在操作完成后,返回目的字符串的首地址,这样可以方便地进行链式操作。
	printf("%s\n", strcat(dest, src));

	char dest2[1024] = "hello ";
	char *src2 = "world!kkkkaaaa";
	//strncat()将最多n个字节的内容追加到目的字符串尾部,并且会在追加后添加终止符号。
    //同strcat()一样,它返回目的字符串的首地址。
	printf("%s\n", strncat(dest2, src2,6));
}

void testStrSearch()
{
	char *s1 = "hello world!";
	char c = 'l';
	//strchr()返回一个字符指针,指向指定字符在指定字符串中第一次出现的位置。如果在指定字符串中没有找到指定字符,则返回 NULL 。该函数的第二个参数按理来说应当是一个字符,不过标准库中确实是int类型。
	printf("%s\n", strchr(s1, c));
	//strrchr()和strchr()类似,但它返回的是指定字符在指定字符串中最后一次出现的位置。如果未找到,同样返回 NULL 。
	printf("%s\n", strrchr(s1, c));
	//strchrnul()的功能和strchr()只有细微的区别,那就是,当没有找到指定字符时,strchrnul()不返回 NULL ,而是返回字符串结束符的位置。
	//这里由于strchrnul()的特性,没办法通过打印字符串来了解strchrnul()的操作
	//'strchrnull' is invalid in C99
	//printf("%p, %p\n", s1, strchrnull(s1, 'f'));

	char *accept = "wo";
	//strpbrk()和strchr()的区别在于,strchr()是从字符串里搜索 一个字符 ,而strpbrk()则是在字符串里搜索 一个字符集中的字符 ,看第二个参数就明白了。strpbrk()遍历字符串,如果发现某个字符在指定的 字符集 中,则立即返回指向该字符的指针。如果最后没有找到任何在指定字符集中的字符,则返回 NULL 。
	printf("%s\n", strpbrk(s1, accept));
}

void testStrSplit()
{
	char s[1024] = "abc;lsdk:lskdj,;slsj";
	char *delm = ";:,";
	char *result = NULL;
	int len = strlen(s);
	int i = 0;
    //strtok()根据第二个参数指定的分隔符(可能存在多个不同的分隔符)将指定字符串分割成多个子串。通过多次调用strtok(),可以依次获得字符串的多个子串的首地址。要注意的是,除了第一次调用时将待分割字符串作为第一个参数,后续的调用要将第一个参数置为 NULL 。当字符串已经无法再分割时,strtok()返回 NULL 。
    //除了上面说过的strtok()的用法外,还要注意的是,作为待分割的字符串,它必须是 可更改的 。否则虽然可以通过编译,但运行会出错。要理解这个现象,首先要了解strtok()的内部机制。
	result = strtok(s, delm);
	while (result != NULL) {
		printf("Source:%s, Sub:%s\n", s, result);
		result = strtok(NULL, delm);
	}
}

/*
T	o	 	b	e	 	o	r	 	n	o	t	 	t	o	 	b	e
84	111	32	98	101	32	111	114	32	110	111	116	32	116	111	32	98	101
84	111	0	98	101	32	111	114	32	110	111	116	32	116	111	32	98	101
84	111	0	98	101	0	111	114	32	110	111	116	32	116	111	32	98	101
84	111	0	98	101	0	111	114	0	110	111	116	32	116	111	32	98	101
84	111	0	98	101	0	111	114	0	110	111	116	0	116	111	32	98	101
84	111	0	98	101	0	111	114	0	110	111	116	0	116	111	0	98	101
84	111	0	98	101	0	111	114	0	110	111	116	0	116	111	0	98	101

可以看到,s中的分隔符,逐次地被置为'\0'即字符串结束符。这就是strtok()分割字符串的内部原理了。而strtok()返回的指针,其实就是s中各个子串的起始位置了。如果s指向的内容是无法被修改的,那么strtok()自然也就无法将原先的分隔符置为字符结束符了。

当然了,由于源字符串会被修改,在实际中,如果需要,可以用strdup()来建立一个源字符串的副本。
*/
void testStrSplit2()
{
	char s[64] = "To be or not to be";
	char *delm = " ";
	char *result = NULL;
	int i =0,len = strlen(s);
	for (i =0;i<len;i++){
		printf("%c ", s[i]);
	}
	printf("\n");
	for (i=0;i<len;i++){
		printf("%d ",(int)s[i]);
	}
	printf("\n");

	result = strtok(s, delm);

	while (result != NULL){
		for (i=0;i<len;i++){
			printf("%d ",(int)s[i]);
		}
		printf("\n");
		result = strtok(NULL, delm);
	}
}

void testStrSplit3()
{
	char s[64] = "Hello World";
	char *delm = " ";
	char *result = NULL, *ptr = NULL;

	printf("Source:%p\n", s);
	//char *strtok_r(char *str, const char *delim, char **saveptr);
	//strtok_r()是Linux下的strtok()的可重入版本(线程安全版本),它比strtok()多了一个参数 saveptr ,这个参数用于在分割字符串时保存上下文。
	result = strtok_r(s, delm, &ptr);

	while (result != NULL){
		printf("Result:%p\t", result);
		printf("Saveptr:%p\t", ptr);
		printf("---%s\t", result);
		printf("---%s\n", ptr);
		//可以看到,saveptr这个指针在每次调用strtok_r()后就指向了未分割的部分的首地址。相对地,strtok()则是在内部有一个静态缓冲区,通过这个静态缓冲区来记录未处理的起始位置,所以strtok()不是线程安全的。
		result = strtok_r(NULL, delm, &ptr);
	}

}

//因为和strtok()的这个不同之处,strsep不需要区分第一次调用后后续的连续调用,可以用统一的操作来对字符串进行分割。
void testStrSplit4()
{
	char s[64] = "To be or not to be";
	char *source = s;
	char *delm = " ";
	char *result = NULL;

	while (source != NULL){
		printf("Source: %s | ", source);
		result = strsep(&source,delm);
		printf("result: %s | ", result);
	}
}

void testStrMatch()
{
	char *s = "To be or not to be.";
	char *p = "be";
	//strstr()返回字符串needle在字符串haystack中第一次出现的位置;如果没有匹配,则返回 NULL 。
	printf("%s\n", strstr(s, p));
}

void testStrDup()
{
	char *s = "aabbccddee";
	char *dup = strdup(s);
	//strdup()调用malloc()分配一块内存并将字符串s的内容拷贝进去,产生s的副本。要注意的是,在最后应该调用free()来释放副本。
	printf("%s\n",dup);
	free(dup);

	char *s1 = "poiuytrewq";
	//strndup()和strdup()类似,但最多只拷贝s的前n个字节。如果s的长度大于n,还会在副本后添加终止符。
	char *dup1 = strndup(s1, 4);
	printf("%s\n",dup1);
	free(dup1);

	//strdupa()和strdup()类似,但在分配内存时,它使用alloca()而不是malloc()。
	//strndupa()之于strdupa()就如strndup()之于strdup(),不再赘述。
}

/*
函数名: swab
功  能: 交换字节
用  法: void swab (char *from, char *to, int nbytes);
*/
void testStringSwab()
{
	char source[15] = "rFna koBlrna d";
	char target[15];

	swab(source,target,strlen(source));
	printf("%s\n",target);
}

/*
函数名: strupr
功  能: 将串中的小写字母转换为大写字母
用  法: char *strupr(char *str);
*/
void testStrUpper()
{
	char string[100] = "abcdefghijklmnopqrstuvwxyz";
	char *ptr;

   /* converts string to upper case characters */
	ptr = strupr_t(string);
	printf("%s\n", ptr);
}

/*
函数名: strtol
功  能: 将串转换为长整数
用  法: long strtol(char *str, char **endptr, int base);
*/
void testStrtol()
{
	char *string = "87654321", *endptr;
	long lnumber;

   /* strtol converts string to long integer  */
	lnumber = strtol(string, &endptr, 10);
	printf("string = %s  long = %ld\n", string, lnumber);
}

/*
函数名: strtod
功  能: 将字符串转换为double型值
用  法: double strtod(char *str, char **endptr);
*/
void testStrtod()
{
	char input[80], *endptr;
	double value;

	printf("Enter a floating point number:");
	gets(input);
	value = strtod(input, &endptr);
	printf("The string is %s the number is %lf\n", input, value);
}

/*
函数名: strspn
功  能: 在串中查找指定字符集的子集的第一次出现
用  法: int strspn(char *str1, char *str2);
*/
void testStrspan()
{
	char *string1 = "1234567890";
	char *string2 = "123DC8";
	int length;

	length = strspn(string1, string2);
	printf("Character where strings differ is at position %d\n", length);
}


/*
函数名: strset
功  能: 将一个串中的所有字符都设为指定字符
用  法: char *strset(char *str, char c);
*/
// void testStrset()
// {
// 	char string[10] = "123456789";
// 	char symbol = 'c';

// 	printf("Before strset(): %s\n", string);
// 	strset(string, symbol);
// 	printf("After strset():  %s\n", string);

// }

/*
函数名: strnset
功  能: 将一个串中的n个字符都设为指定字符
用  法: char *strnset(char *str, char ch, unsigned n);
*/
// void testStrnset()
// {
// 	char *string = "abcdefghijklmnopqrstuvwxyz";
// 	char letter = 'x';

// 	printf("string before strnset: %s\n", string);
// 	strnset(string, letter, 13);
// 	printf("string after  strnset: %s\n", string);
// }

void strrev(char *head)
{
  if (!head) return;
  char *tail = head;
  // find the 0 terminator, like head+strlen
  while(*tail) ++tail;
  // tail points to the last real char
  --tail;               
  // head still points to the first
  for( ; head < tail; ++head, --tail) {
      // walk pointers inwards until they meet or cross in the middle
      char h = *head, t = *tail;
      *head = t;           // swapping as we go
      *tail = h;
  }
}

void reverse(char s[])
{
    int length = strlen(s) ;
    int c, i, j;

    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

/*
函数名: strrev
功  能: 串倒转
用  法: char *strrev(char *str);
*/
void testStrrev()
{
	char forward[15] = "string";

	printf("Before strrev(): %s\n", forward);
	strrev(forward);
	printf("After strrev():  %s\n", forward);
	reverse(forward);
	printf("After reverse():  %s\n", forward);
}


/*
函数名: strcspn
功  能: 在串中查找第一个给定字符集内容的段
用  法: int strcspn(char *str1, char *str2);
*/
void testStrcspn()
{
	char *string1 = "1234567890";
	char *string2 = "747DC8";
	int length;

	length = strcspn(string1, string2);
	printf("Character where strings intersect is at position %d\n", length);
}



/*
atof(将字符串转换成浮点型数)
atoi(将字符串转换成整型数)
atol(将字符串转换成长整型数)
strtod(将字符串转换成浮点数)
strtol(将字符串转换成长整型数)
strtoul(将字符串转换成无符号长整型数)
toascii(将整型数转换成合法的ASCII 码字符)
toupper(将小写字母转换成大写字母)
tolower(将大写字母转换成小写字母)
atof(将字符串转换成浮点型数)
*/

int main(int argc, char *argv[])
{
	testStrCpy();
	testStrCmp();
	testStrCat();
	testStrSearch();
	testStrSplit();
	testStrSplit2();
	testStrSplit3();
	testStrSplit4();
	testStrMatch();
	testStrDup();

	testStrspan();
	// testStrset();
	// testStrnset();
	testStrrev();
	testStrcspn();
	testStrtod();
	testStrtol();
	testStrUpper();
	return 0;
}