浅谈c++字符串——2 c标准库中字符串

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/q8250356/article/details/100641405

2 C标准库字符串

2.1 <stdlib.h>定义的宽字节字符wchar_t

在标准C中,字符串有两种 char*和wchar_t*,一个是多字节字符串,另一个是宽字节字符串。前者是C的关键字,编译器会直接为其分配空间,而wchar_t在便准库中定义,不包含头文件的话,编译器会报错。但是可以修改项目属性中的SDL选项来更改安全属性,可以避免编译错误。建议不使用,安全性控制由程序员控制,只要是考虑到跨平台的问题。

2.2 C标准库中操作字符的库函数

注意:在新的VC++标准中,这些C标准库的库函数已经因为安全性问题被禁用(主要是为了防止内存泄露),取代的是C++提供的新的stdlib.h库函数,这些函数通常具有更高的安全性,命名规则一般是在原来的库函数后面加”_s”,或者在函数名前面加”_”亦或者都要加。

2.2.1 <stdlib.h>中定义

double atof(const char *str)

把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)

参数:str -- 要转换为浮点数的字符串

double db;

char ch[40] = "18284.7475755";

db = ::atof(ch);

int atoi(const char *str)

把参数 str 所指向的字符串转换为一个整数(类型为 int 型)

参数:str -- 要转换为整数的字符串

int db;

char ch[40] = "18284";

db = ::atoi(ch);

long int atol(const char *str)

把参数参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)

参数:str -- 要转换为长整数的字符串

long val;

char ch[40] = "18284";

db = ::atol(ch);

double strtod(const char *str, char **endptr)

把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。如果 endptr 不为空,则指向转换中最后一个字符后的字符的指针会存储在 endptr 引用的位置

参数:

str -- 要转换为双精度浮点数的字符串。

endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。

double db;

char ch[50] = "183495.8475756\0aaa";

char *pc;

db = ::strtod(ch, &pc);//db==183495.8475756

pc++; //pc最终指向”aaa

long int strtol(const char *str, char **endptr, int base)

把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0。

参数:

str -- 要转换为长整数的字符串。

endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。

base -- 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。(位权)

unsigned long int strtoul(

const char *str,

char **endptr,

int base)。

把参数 str 所指向的字符串根据给定的 base 转换为一个无符号长整数(类型为 unsigned long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0

参数:

str -- 要转换为无符号长整数的字符串。

endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。

base -- 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。

void *calloc(size_t nitems, size_t size)

分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。

参数:

nitems -- 要被分配的元素个数。

size -- 元素的大小。

分析如下代码:

char *pc = (char*)::calloc(10, sizeof(char));//在程序堆栈中申请10个char类型的长度(10个byte)

for (int i = 0; i < 16;i++)//但却赋值了16个字节的值

{

pc[i] = 'a';

}//执行完后pc字符串的值为 aaaaaaaaaaaaaaaa铪除了出现我们要求赋的字符还出现了乱码,再看下下面两个函数的返回值

size_t nstr = sizeof(pc);//nstr==4对char*指针使用sizeof()是无意义的,即使是const char*或者 char* const除非char pc[]=”aaaa”;

size_t strlength = strlen(pc);//strlength==24当计算字符串的长度时是以’\0’为结束标志的如果是使用以上的动态分配空间创建字符串必须在字符串结束的后一位置手动赋值’\0’

void free(void *ptr)

释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。

参数:

ptr – 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。

void *malloc(size_t size)

分配所需的内存空间,并返回一个指向它的指针。

参数:size -- 内存块的大小,以字节为单位。

malloc, calloc与new的区别:前者由C标准库定义,如果使用时不包含<stdlib.h>就会编译出错;而new是C++关键字,不需要包含任何头文件,编译器会自动将其编译为目标代码。malloc返回值为void*,我们需要显示转换(char*)malloc(),而new则不需我们人为做这些操作,并且可以自动计算所需要大小。

void *realloc(void *ptr, size_t size)

尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小

参数:

ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。

size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。

int mblen(const char *str, size_t n)

返回参数 str 所指向的多字节字符的长度

参数:

str -- 指向多字节字符的第一个字节的指针。

n -- 要检查的字符长度的最大字节数。

char ch[10] = "abcdefg";

int nlen = ::mblen(ch, sizeof(ch)); //nLen==1,表示一个char字符占一个字节

size_t mbstowcs(schar_t *pwcs, const char *str, size_t n)

把参数 str 所指向的多字节字符的字符串转换为参数 pwcs 所指向的数组。

参数:

pwcs -- 指向一个 wchar_t 元素的数组,数组长度足以存储一个最大字符长度的宽字符串。

str -- 要被转换的多字节字符字符串。

n -- 要被转换的最大字符数。

wchar_t wch[10]=_T("\0");

size_t nwch;

char mbch[4] = "123";

::mbstowcs_s(&nwch, wch, mbch, strlen(mbch));

//在C++标准库中认为mbstowcs不安全要求使用mbstowcs_s第一个参数实际转换的字符数的引用,(其他重载参看MSDN)

int mbtowc(whcar_t *pwc, const char *str, size_t n)

把一个多字节序列转换为一个宽字。

参数:

pwc -- 指向类型为 wchar_t 对象的指针。

str -- 指向多字节字符的第一个字节的指针。

n -- 要被检查的最大字节数。

char ch = 'a';

wchar_t wch;

int nwchar = ::mbtowc(&wch, &ch, ::mblen(&ch,sizeof(char)));//由于编译器的不同所以建议先用mblen()计算一个多字节字符的长度

int nLen = sizeof(wch);//nLen==2

size_t wcstombs(char *str, const wchar_t *pwcs, size_t n)

把宽字符字符串 pwcs 转换为一个 str 开始的多字节字符串。最多会有 n 个字节被写入 str 中。

参数:

pwc -- 指向类型为 wchar_t 对象的指针。

str -- 指向多字节字符的第一个字节的指针。

n -- 要被检查的最大字节数。

返回:

该函数返回转换和写入到 str 中的字节数,不包括结尾的空字符。

int wctomb(char *str, wchar_t wchar)

把一个宽字符 wchar 转换为它的多字节表示形式,并把它存储在 str 指向的字符数组的开头。

参数:

str -- 一个指针,指向一个足以存储多字节字符的数组。

wchar -- 类型为 wchar_t 的宽字符。

2.2.2 <ctype.h>中定义

C 标准库的 ctype.h 头文件提供了一些函数,可用于测试和映射单个字符。

这些函数接受 int 作为参数,它的值必须是 EOF 或表示为一个无符号字符。

如果参数 c 满足描述的条件,则这些函数返回非零(true)。如果参数 c 不满足描述的条件,则这些函数返回零。

int isalnum(int c)
该函数检查所传的字符是否是字母和数字。

int isalpha(int c)
该函数检查所传的字符是否是字母。

int iscntrl(int c)
该函数检查所传的字符是否是控制字符。

int isdigit(int c)
该函数检查所传的字符是否是十进制数字。

int isgraph(int c)
该函数检查所传的字符是否有图形表示法。

int islower(int c)
该函数检查所传的字符是否是小写字母。

int isprint(int c)
该函数检查所传的字符是否是可打印的。

int ispunct(int c)
该函数检查所传的字符是否是标点符号字符。

int isspace(int c)
该函数检查所传的字符是否是空白字符。

int isupper(int c)
该函数检查所传的字符是否是大写字母。

int isxdigit(int c)
该函数检查所传的字符是否是十六进制数字。

标准库还包含了两个转换函数,它们接受并返回一个 "int"

int tolower(int c)
该函数把大写字母转换为小写字母。

int toupper(int c)
该函数把小写字母转换为大写字母。

2.2.3 <stdio.h>中定义

int fprintf(FILE *stream, const char *format, ...)

// fprintf(stdout,”aaaa”)==printf(“aaaa”)

发送格式化输出到流 stream 中。

参数:

stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。

format -- 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier

int vfprintf(

FILE *stream,

const char *format,

va_list arg

)

// fprintf的内部实现

使用参数列表发送格式化输出到流 stream 中

void WriteFrmtd(FILE *stream, char *format, ...)

{

    va_list args;

    va_start(args, format);

    vfprintf(stream, format, args);//相当于重定位

    va_end(args);

}

void main()

{

    WriteFrmtd(stdout, "This is just one argument %d \n%f", 10,1.9);

}

int printf(const char *format, ...)

发送格式化输出到标准输出 stdout

参数:

format -- 这是字符串,包含了要被写入到标准输出 stdout 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier,具体讲解如下:

specifier(说明符)

输出

c

字符

d 或 i

有符号十进制整数

e

使用 e 字符的科学科学记数法(尾数和指数)

E

使用 E 字符的科学科学记数法(尾数和指数)

f

十进制浮点数

g

自动选择 %e 或 %f 中合适的表示法

G

自动选择 %E 或 %f 中合适的表示法

o

有符号八进制

s

字符的字符串

u

无符号十进制整数

x

无符号十六进制整数

X

无符号十六进制整数(大写字母)

p

指针地址

n

无输出

%

字符

 

flags(标识)

描述

-

在给定的字段宽度内左对齐,默认是右对齐(参见 width 子说明符)。

+

强制在结果之前显示加号或减号(+ 或 -),即正数前面会显示 + 号。默认情况下,只有负数前面会显示一个 - 号。

(space)

如果没有写入任何符号,则在该值前面插入一个空格。

#

与 o、x 或 X 说明符一起使用时,非零值前面会分别显示 0、0x 或 0X。
与 e、E 和 f 一起使用时,会强制输出包含一个小数点,即使后边没有数字时也会显示小数点。默认情况下,如果后边没有数字时候,不会显示显示小数点。
与 g 或 G 一起使用时,结果与使用 e 或 E 时相同,但是尾部的零不会被移除。

0

在指定填充 padding 的数字左边放置零(0),而不是空格(参见 width 子说明符)。

 

width(宽度)

描述

(number)

要输出的字符的最小数目。如果输出的值短于该数,结果会用空格填充。如果输出的值长于该数,结果不会被截断。

*

宽度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。

 

.precision(精度)

描述

.number

对于整数说明符(d、i、o、u、x、X):precision 指定了要写入的数字的最小位数。如果写入的值短于该数,结果会用前导零来填充。如果写入的值长于该数,结果不会被截断。精度为 0 意味着不写入任何字符。
对于 e、E 和 f 说明符:要在小数点后输出的小数位数。
对于 g 和 G 说明符:要输出的最大有效位数。
对于 s: 要输出的最大字符数。默认情况下,所有字符都会被输出,直到遇到末尾的空字符。
对于 c 类型:没有任何影响。
当未指定任何精度时,默认为 1。如果指定时不带有一个显式值,则假定为 0。

.*

精度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。

 

length(长度)

描述

h

参数被解释为短整型或无符号短整型(仅适用于整数说明符:i、d、o、u、x 和 X)。

l

参数被解释为长整型或无符号长整型,适用于整数说明符(i、d、o、u、x 和 X)及说明符 c(表示一个宽字符)和 s(表示宽字符字符串)。

L

参数被解释为长双精度型(仅适用于浮点数说明符:e、E、f、g 和 G)。

 

讨论一下printf函数的实现:

void MyPrintf(char* formate, int param0 = ‘\0’, ...)

{

    va_list ap;

    va_start(ap, formate);

    char *pch = formate;

    int nSpecifier = 0;

    //判断格式字符串的个数

    while (*pch!='\0')

    {

        if (*pch == '%' && (*(pch + 1) == 'd' || *(pch + 1) == 'c'))

       {

            nSpecifier++;

           pch++;

       }

    pch++;

    }

    //如果没有格式字符串就直接输出

    if (nSpecifier == 0 && param0 == '\0')

    {

        printf(formate);

        return;

    }

    //else

    pch = formate;

    int n = 0;

    while (n<nSpecifier)

    {

        while (*pch != '%' && *pch!='\0')

        {

            cout << *pch;

            pch++;

        }

        //目前只支持两种格式字符串控制

        switch (*(pch + 1))

        {

        case 'd':printf("%d", va_arg(ap, int)); break;

        case 'c':printf("%c", va_arg(ap, char)); break;

        default:

            break;

        }

        pch += 2;

        n++;

    }

    va_end(ap);

}

int vprintf(const char *format, va_list arg)

//等于vfprintf(stdout, format, args);

//是printf函数的实现

使用参数列表发送格式化输出到标准输出 stdout

void MtPrintf(char *format, ...)

{

    va_list args;

    va_start(args, format);

    vprintf(format, args);

    va_end(args);

}

int sprintf(char *str, const char *format, ...)

发送格式化输出到 str 所指向的字符串

int vsprintf(char *str, const char *format, va_list arg)

// sprintf的实现

使用参数列表发送格式化输出到字符串

int scanf(const char *format, ...)

从标准输入 stdin 读取格式化输入。*问题*貌似在vs2013中scanf_s包括<conio.h>中的_cscanf_s()使用后都没有反应,必须写明接收的字节数如:scanf_s(“%s”, ch, sizeof(ch));

int fscanf(FILE *stream, const char *format, ...)

从流 stream 读取格式化输入。

int getc(FILE *stream)

从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动

char c;

printf("请输入字符:");

c = getc(stdin);//相当于getchar()

int getchar(void)

从标准输入 stdin 获取一个字符(一个无符号字符)。这等同于 getc 带有 stdin 作为参数

char *gets(char *str)

从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

int fgetc(FILE *stream)

从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。

char *fgets(char *str, int n, FILE *stream)

从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

int fputc(int char, FILE *stream)

把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。

int fputs(const char *str, FILE *stream)

把字符串写入到指定的流 stream 中,但不包括空字符。

int getc(FILE *stream)

从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。

int getchar(void)

从标准输入 stdin 获取一个字符(一个无符号字符)。

char *gets(char *str)

从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

int putc(int char, FILE *stream)

把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。

int putchar(int char)

把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。

int puts(const char *str)

把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。

int ungetc(int char, FILE *stream)

把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符。

void perror(const char *str)

把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。

2.2.4 <string.h> 中定义

函数 & 描述

void *memchr(const void *str, int c, size_t n)
在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。

int memcmp(const void *str1, const void *str2, size_t n)
把 str1 和 str2 的前 n 个字节进行比较。

void *memcpy(void *dest, const void *src, size_t n)
从 src 复制 n 个字符到 dest

void *memmove(void *dest, const void *src, size_t n)
另一个用于从 str2 复制 n 个字符到 str1 的函数。

void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

char *strcat(char *dest, const char *src)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

char *strncat(char *dest, const char *src, size_t n)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

char *strchr(const char *str, int c)
在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。

int strcmp(const char *str1, const char *str2)
把 str1 所指向的字符串和 str2 所指向的字符串进行比较。

int strncmp(const char *str1, const char *str2, size_t n)
把 str1 和 str2 进行比较,最多比较前 n 个字节。

int strcoll(const char *str1, const char *str2)
把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。

char *strcpy(char *dest, const char *src)
把 src 所指向的字符串复制到 dest

char *strncpy(char *dest, const char *src, size_t n)
把 src 所指向的字符串复制到 dest,最多复制 n 个字符。

size_t strcspn(const char *str1, const char *str2)
检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。

int len;

const char str1[] = "ABCDEF4960910";

const char str2[] = "013";

len = strcspn(str1, str2);

printf("第一个匹配的字符是在 %d\n", len + 1);//输出10,实际上就是找str1中第一个包含在str2中任意字符的位置

char *strerror(int errnum)
从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。

size_t strlen(const char *str)
计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

char *strpbrk(const char *str1, const char *str2)
检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。

char *strrchr(const char *str, int c)
在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。

size_t strspn(const char *str1, const char *str2)
检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。

int len;

const char str1[] = "ABCDEFG019874";

const char str2[] = "ABCD";

len = strspn(str1, str2);

printf("初始段匹配长度 %d\n", len );//输出4,即找到str1中第一个不在str2中出现过的字符

char *strstr(const char *haystack, const char *needle)
在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。

char *strtok(char *str, const char *delim)
分解字符串 str 为一组字符串,delim 为分隔符。

size_t strxfrm(char *dest, const char *src, size_t n)
根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。

 

2.2.5 <conio.h>(非标准库--控制台输入输出库)

注:conio.h不是C标准库中的头文件

char *cgets( char *buffer );

从键盘得到一个字符串

int cputs( const char *string )

在当前光标处向文本屏幕输出字符串str,光标自动右移字符串长度个字符位置

char ch[10];

size_t nLen;

_cgets_s(ch,&nLen); //vc6.0后采用_cgets_s,参数二是实际获得的字节数

_cputs(ch); //vc6.0后采用_cputs

int _cscanf_s(char *format[,argument, ...])

C++新标准_cscanf_s

从控制台执行格式化输入

int inp(

   unsigned short port

);

unsigned short inpw(

   unsigned short port

);

unsigned long inpd(

   unsigned short port

);

从端口逐字节 (_inp),逐字 (_inpw)或者逐双字 (_inpd)输入。

int getch(void)

这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏,但是这个函数并非标准函数,要注意移植性!

int getche(void);

输入后立即从控制台取字符,不以回车为结束(带回显)

注意不同版本编译器对函数名的要求(以下是VS2013的错误提示):

error C4996: 'getche': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _getche. See online help for details.

error C4996: 'getch': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _getch. See online help for details.

猜你喜欢

转载自blog.csdn.net/q8250356/article/details/100641405