淺談c++字符串——2 c標準庫中字符串

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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章