天天看點

字元串——C語言程式設計(八)C語言程式設計(八)字元串

C語言程式設計(八)

文章目錄

  • C語言程式設計(八)
  • 字元串
    • 字元數組
    • 字元串
    • 字元串變量
    • 字元串常量
    • 用指針還是數組?
    • char*是字元串?
    • 字元串指派?
    • 字元串輸入輸出
    • 安全的輸入
    • 常見錯誤
    • 空字元串
    • 字元串數組
      • 附加:之前用switch-case寫的月份程式可以用數組來寫
    • 程式參數
    • 單字元輸入輸出
      • putchar函數
      • getchar函數
    • 标準庫中的字元串函數
    • string.h
      • 字元串中找字元

字元串

字元數組

  • char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’};
    • 這不是C語言的字元串,因為不能用字元串的方式做計算
word[0] H
word[1] e
word[2] l
word[3] l
word[4] o
word[5] !

字元串

  • char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’}; //這才是C語言的字元串
word[0] H
word[1] e
word[2] l
word[3] l
word[4] o
word[5] !
word[6] \0
  • 以 (整數0)結尾的一串字元
    • \0

      是一樣的,但是和 不同
  • 0标志字元串的結束 ,但它不是字元串的一部分
  • 計算字元串長度的時候不包含這個0
  • 字元串以數組的形式存在,以數組或指針的形式通路
  • 更多的是以指針的形式
  • string.h

    裡有很多處理字元串的函數

字元串變量

  • char *str = “Hello”; //定義一個指針指向字元數組
  • char word[] = “Hello”; //字元數組
  • char line[10] = “Hello”; //位元組長度為10的字元數組; char長度是1位元組

字元串是數組; 這些變量都是字元數組的變量

  • “Hello”
  • ″Hello″ 會被編譯器變成一個字元數組放在某處,這個數組的長度是6,結尾還有表示結束的 。 {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};像這樣的字元數組;是以還需加一.
  • 兩個相鄰的字元串常量會被自動連接配接起來
  • 行末的

    \

    表示下一行還是這個字元串常量

字元串

  • C語言的字元串是以字元數組的形态存在的
    • 不能用運算符對字元串做運算
    • 通過數組的方式可以周遊字元串
  • 唯一特殊的地方是字元串

    " "

    (雙引号)字面量可以用來初始化字元數組
  • 以及标準庫提供了一系列字元串函數

字元串常量

  • char* s = "Hello, world!";

  • s 是一個指針,初始化為指向一個字元串常量
    • 由于這個常量所在的地方,是以實際上s是

      const char* s

      ,但是由于曆史的原因,編譯器接受不帶

      const

      的寫法,
    • 但是試圖對s所指的字元串做寫入會導緻嚴重的後果
  • 如果需要修改字元串,應該用數組:

    char s[] = "Hello, world!";

#include <stdio.h>
int main()
{
	int i = 0;
	char *s1 = "Hello World";
	char *s2 = "Hello World";
	char s3[] = "Hello World";
	//其中指針s1和s2指向同一個空間,也就是值相同; 而s3[]的數組是重新開辟一個空間,然後把字元數組的值複制進入 
	printf("&i=%p\n", &i);
	printf("s1=%p\n", s1);
	printf("s2=%p\n", s2);
	printf("s3=%p\n", s3);
	
	for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
		printf("%c", s3[i]);
	}//周遊輸出字元數組 
	printf("\n");
	
	
	s3[0] = 'b'; //注意字元數組中的元素要用單引号,不能是雙引号 
	printf("s3[0]=%c\n", s3[0]);
	
	for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
		printf("%c", s3[i]);
	}//周遊輸出字元數組 
	printf("\n");
	
	return 0;
		
}
           

輸出結果:

&i=000000000062FE0C
s1=0000000000404000
s2=0000000000404000
s3=000000000062FE00
Hello World
s3[0]=b
bello World
           

注意:

char *s1 = "Hello World";
s1[0] = 'b';
//會直接報錯,因為s1是一個指針,初始化為指向一個字元串常量,而這個常量是不可改變的,但是如果用數組就可以用數組:char s1[] = "Hello, world!";而數組是一種特殊的指針, 它指向一個字元串數組的首位址,
           

字元用單引号, 字元串用雙引号

用指針還是數組?

  • char *str = “Hello”;

  • char word[] = “Hello”;`
  • 數組:這個字元串在這裡
    • 作為本地變量空間自動被回收
  • 指針:這個字元串不知道在哪裡
    • 處理參數
    • 動态配置設定空間
  • 是以如果要構造一個字元串用數組; 如果要處理一個字元串用指針

char*是字元串?

  • 字元串可以表達為

    char*

    的形式;
  • char*

    不一定是字元串
    • 本意是指向字元的指針,可能指向的是字元的數組(就像

      int*

      一樣;

      int*

      不一定是數組; 它可能指向單個int,也可能指向數組)
    • 隻有它所指的字元數組有結尾的0,才能說它所指的是字元串。

      比如:char* word = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};

字元串指派?

char *t = “title”;
char *s;
s = t;
           
  • 并沒有産生新的字元串,隻是讓指針s指向了t所指的字元串,對s的任何操作就是對t做的

字元串輸入輸出

char string[8];
scanf(“%s”, string);
printf(“%s”, string);
           
#include <stdio.h>
int main()
{
	char word[8];//可以放8個char類型,超過的話,字元數組會越界
	char word2[8];
	while(1){
    scanf("%s", word);
	scanf("%s", word2);
	printf("%s##%s##\n", word, word2);
    }
	return 0;
}
           

輸出:

hello world
hello##world##

hello
world
hello##world##

helloworld
hello_world
helloworld##hello_world##
           

可以發現%s讀入一個單詞(到空格、tab或回車為止)

  • scanf

    讀入一個單詞(到空格、tab或回車為止)
  • scanf

    是不安全的,因為不知道要讀入的内容的長度

安全的輸入

char string[8];
scanf(“%7s”, string);
           
  • 在%和s之間的數字表示最多允許讀入的字元的數量,這個數字應該比數組的大小小一; 最後一個字元要放0.
    • 下一次scanf從哪裡開始?根據個數來讀内容而不是空格或回車符.
#include <stdio.h>
int main()
{
	char word[8];//可以放8個char類型,超過的話,字元數組會越界
	char word2[8];
	while(1){
    scanf("%7s", word);//可以放8個char類型,但隻能限制輸入7個字元,最後一個字元要放0
	scanf("%7s", word2);
	printf("%s##%s##\n", word, word2);
	printf("\n"); 
    }
	return 0;
}
           
helloworld
hellowo##rld##

1234
123456
1234##123456##

12345678
1234567##8##
           

常見錯誤

char *string;
scanf(“%s”, string);
           
  • 以為char*是字元串類型,定義了一個字元串類型的變量string就可以直接使用了
    • 由于沒有對string初始化為0,是以不一定每次運作都出錯

空字元串

  • char buffer[100]=””;

    • 這是一個空的字元串,

      buffer[0] == ‘\0’

  • char buffer[] = “”;

    • 這個數組的長度隻有1!

字元串數組

  • char **a

    • a是一個指針,指向另一個指針,那個指針指向一個字元(串)
  • char a[][]

    • a是一個二維數組,第二個次元的大小不知道,不能編譯,是以二維數組必須要有第二個次元的大小;
  • char a[][10]

    • a是一個二維數組,a[x]是一個char[10]; 是指每個a[]單元最多有10個字元元素
  • char *a[]

    • a是一個一維數組,a[x]是一個char*; 是一個指針
#include <stdio.h>
int main()
{
	char a[][10]={  
		"Hello",
		"World",
		"1234567890" 
			
	};  //類似于數組的元素集合 ,因為是集合而不是塊,是以得用分号; 
	return 0;

} 
           

這樣編譯會出錯,因為"1234567890"超過了9個char長度的大小,

char a[][10]

是指每一行都不超過9個字元元素,(因為C語言中是字元串後面是

\0

結尾占據一個空間,好比顧頭不顧腚)。

But:

#include <stdio.h>
int main()
{
	char *a[]={  
		"Hello",
		"World",
		"1234567890" 
			
	};  //類似于數組的元素集合 ,因為是集合而不是塊,是以得用分号; 
	return 0;

} 
           

這樣寫就沒問題, 他們的差別是:

字元串——C語言程式設計(八)C語言程式設計(八)字元串

附加:之前用switch-case寫的月份程式可以用數組來寫

  • 用char month[][20]

#include <stdio.h>
int main()
{
    char month[][20]= {
        "null",
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    };
    int i;
    while (1) {
        scanf("%d",&i);
        printf("%s\n",month[i]);
    }   
}
           

char *month[]

#include <stdio.h>

int main()
{
    char *month[] = {
        "null",
        "January",
        "February",
        'March',
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    };
    int i;
    printf("請輸入月份:"); 
    scanf("%d",&i);
    printf("%s\n", month[i]);
}
           

程式參數

  • int main(int argc, char const *argv[])

    //括号裡的參數(一個整數 一個字元串數組); 前面的整數是說明字元串數組有多大
  • argv[0]

    是指令本身
    • 當使用Unix的符号連結時,反映符号連結的名字
#include <stdio.h>
int main(int argc, char const*argv[])
{
	int i;
	for(i=0; i<argc; i++){
		printf("%d:%s\n",i, argv);
	}//周遊輸出數組 
	return 0; 
} 
           
  • 得在Unix上使用;windows上不能直接運作出結果

單字元輸入輸出

putchar函數

  • int putchar(int c);

    //雖然是傳入一個int類型參數; 但是它int型能接收的隻是一個字元char
  • 向标準輸出寫一個字元(每一次隻寫入一個字元) put char:輸出字元
  • 傳回寫了幾個字元,

    EOF(-1)

    表示寫失敗

每一次隻寫一個字元的意思是,每次

getchar函數

  • int getchar(void);

    //沒有參數
  • 從标準輸入讀入一個字元 get char 讀入字元
  • 傳回類型是int是為了傳回

    EOF(-1)

    • Windows—>Ctrl-Z

    • Unix—>Ctrl-D

#include <stdio.h>
int main(int argc, char const*argv[])
{
	int ch;
	
	while( (ch = getchar()) != EOF){
		putchar(ch);
	}
	printf("EOF\n"); 
	return 0;
 } 
           

輸出:

1234
1234
12656
12656
ch
ch
EOF
EOF

^Z
EOF
           

在windows系統中

Ctrl + Z

再加回車會傳回

EOF

; 在

Unix上用Ctrl + D

其他的輸出什麼就傳回什麼.

标準庫中的字元串函數

  • C的标準庫中的函數
  • 頭檔案:

    string.h

  • 使用這些函數時得輸入

    #include <string.h>

string.h

  • strlen

    函數
    • size_t strlen(const char *s);

      //傳入的字元串不會修改 (const)
    • 傳回s的字元串長度(不包括結尾的0); 就是專門用來輸出數組字元串的長度, 與

      sizeof

      有差別; (

      sizeof

      是直接傳回占用記憶體空間的位元組長度大小; 而

      strlen

      呢是直接傳回字元串的個數)
    • strlen函數的作用是它會告訴你字元串的長度
#include <stdio.h>
#include <string.h>
int main(int argc, char const*argv[])
{
	char line[] = "Hello";
	printf("strlen=%lu\n",strlen(line));
	printf("sizeof=%lu\n",sizeof(line));
	
	return 0;
 } 
           

輸出:

strlen=5
sizeof=6
           

之是以sizeof=6, 是因為字元串數組中含有

/0

, 而這個不會被strlen函數記進去。

仿寫

strlen

函數

#include <stdio.h>
//#include <string.h>
size_t mylen(const char* s)
{
	
	int idx = 0;
	while (s[idx]!='\0'){
		idx++;	
	}
	return idx;
} //仿照strlen函數

int main(int argc, char const*argv[])
{
	char line[] = "Hello";
//	printf("strlen=%lu\n",strlen(line));
	printf("sizeof=%lu\n",sizeof(line));
	printf("mylen =%lu\n", mylen(line));
	return 0;
 } 

           

輸出:

sizeof=6
mylen =5
           

其中這自己定義的mylen函數與标準庫裡的strlen函數功能一樣。

  • strcmp

    函數
    • int strcmp(const char *s1, const char *s2);

    • strcmp

      函數的作用是:比較兩個字元串,傳回:
    • 0:s1==s2

    • 1:s1>s2

    • -1:s1<s2

#include <stdio.h>
#include <string.h>
int main()
{
	char s1[] = "abc";
	char s2[] = "abc";
	printf("%d\n", strcmp(s1, s2));//字元串相同,則傳回0
	printf("%d\n", 'a'-'b');
	
	char s3[] = "abc";
	char s4[] = "ABc";
	printf("%d\n", strcmp(s3, s4)); //s3和s4不同, s3>s4
	printf("%d\n", 'a'-'A');
	
	return 0;
 } 

           

輸出:

0
-1
1
32
           

仿寫

strcmp

函數

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

int mycmp(const char *a, const char *b)
{
	int c;
	while (*a == *b && *a != '\0'){
		a++;
		b++;
	}
	if (*a-*b > 0){
		c = 1;
	}else if(*a-*b <0){
		c = -1;
	}else{
		c = 0;
	}
	return c;
}

int main(int argc, char const *argv[])
{
	char s1[] = "abc";
	char s2[] = "abc";
	printf("%d\n", mycmp(s1, s2));
	printf("%d\n", 'a'-'b');

 } 
           

輸出:

0
-1
           
  • strcpy

    函數
    • char * strcpy(char *restrict dst, const char *restrict src);

    • src

      的字元串拷貝到

      dst

      //注意是将後面的參數拷貝到前面
      • restrict

        表明

        src

        dst

        不重疊

        (only C99)

    • 傳回

      dst

    • 為了能鍊起代碼來
    • 複制一個字元串
    • 仿寫一個

      strcpy

      函數
char *dst = (char*)malloc(strlen(src)+1);//先動态申請記憶體空間, , 字元串的長度要+1, 因為要包括最後的'\0'
strcpy(dst, src);
           

舉個栗子:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
	char s1[]  = "abc";
	char *s2 = (char*) malloc(strlen(s1)+1);//注意配置設定的記憶體大小要加1 
	strcpy(s2, s1); //将s1拷貝到s2,  
	int i;
	for(i=0; i<strlen(s2); i++){
		printf("%c", s2[i]); 
	}
	return 0;
}
           

輸出:

abc
           

也可以這樣, 直接相同大小空間直接替換過去

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

int main()
{
	char s1[] = "abc";
	char s2[] = "ABC";
	strcpy(s2, s1); //将s1拷貝到s2,  
//	int i;
//	for(i=0; i<strlen(s2); i++){
//		printf("%c", s2[i]); 
//	}
    //可以不必要用for周遊輸出字元串數組
    
    //直接%s就可以輸出字元串數組
    printf("%s\n", s2); 
	return 0;
}

           

輸出:

abc
           

仿寫

strcpy

函數(隻寫部分代碼)

//第一種:
char* mycpy(char* dst, const char* src)
{
	int idx = 0;
    while (src[idx]){
        dst[idx] = src[idx];
        idx++;
    }
    dst[idx] = '\0';
    
    return 0;
}//用數組定義的mycpy函數

//第二種:
char* mycpy(char* dst, const char* src)
{
	char* ret = dst;//先保留起始位址
    while (dst++ = src++); 
    dst[idx] = '\0';
    return ret;
}//用指針定義的mycpy函數
           
  • strcat

    函數
    • 字元串——C語言程式設計(八)C語言程式設計(八)字元串
    • char * strcat(char *restrict s1, const char *restrict s2);

      • 把s2拷貝到s1的後面,接成一個長的字元串 (也就是連接配接起來)
      • 傳回s1
      • s1必須具有足夠的空間
  • 安全問題
    • strcpy

      strcat

      都可能出現安全問題, 可能造成數組越界, 超出存儲範圍, 就會有可能占到别的資料存放的地方, 這樣會導緻其他資料不安全,容易丢失。
    • 如果目的地沒有足夠的空間?
    • 是以建議是盡量少用或者不用

      strcpy

      strcat

  • 安全版本(多了個n; 即最多能拷貝過去n的範圍大小)
    • char * strncpy(char *restrict dst, const char *restrict src, size_t n);

    • char * strncat(char *restrict s1, const char *restrict s2, size_t n);

    • int strncmp(const char *s1, const char *s2, size_t n);

      //這裡的n是隻是讓它比較到第n個

字元串中找字元

  • strchr
    • char * strchr(const char *s, int c);

      //在s中,從左邊尋找

      c

      第一次出現的位置, 并傳回指針
    • char * strrchr(const char *s, int c);

      //在s中,從右邊尋找

      c

      第一次出現的位置, 并傳回指針
    • 它們都是尋找單個字元的
    • 傳回NULL表示沒有找到

舉個栗子: 一個數組字元串

hello

,找

l

的後半段

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

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元 
	printf("%s\n", p); //傳回的指針賦給指針p, 從指針p所指的位址開始輸出字元串 ,是以不需要用*p ,與之前的指針指向某個變量的值不一樣, 這個指針相當于數組頭指針, 它指向的是一連串的數組空間 

	return 0;
}


           

輸出:

llo
           

怎樣找第二個

l

呢 ?

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

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元 
	//尋找第二個l開始 
	p = strchr(p+1, 'l');
	printf("%s\n", p); 
}

           

輸出:

lo
           

也可以将找到的那部分拷貝出來

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

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元 
	char *t = (char*)malloc(strlen(p)+1);
	strcpy(t, p);//将p的字元串拷貝到t中 
	printf("%s\n", t);
	free(t);
	return 0;
}
           

輸出:

llo
           

如果是要

l

的前一段呢?

這裡要用到一些小技巧

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

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //從左邊第一個'l'讀起 ,strchr傳回一個指針,指向一個你要找的字元 
	char c = *p;//先儲存p所指的空間上存放的值 
	*p = '\0'; //再将p所指空間上的值改為結束時的'\0' 
	printf("%s\n", s);//這時輸出就隻有'l'的前部分 
	char *t = (char*)malloc(strlen(s)+1);
	strcpy(t, s);//将p的字元串拷貝到t中 
	printf("%s\n", t);//這時輸出'l' 前面的部分 
	free(t);
	*p = c; //将p所指的那個空間的原來的值恢複回來 
	printf("%s\n", s);//這時就是原來的值了 
	return 0;
}
           

輸出:

he
he
hello
           

附注: 要輸出字元串數組得用

%s

, (

%c

輸出不了,除非用數組周遊才可以,那就相對比較麻煩)

  • strstr
    • 是字元串中尋找一個字元串的
    • char * strstr(const char *s1, const char *s2);

    • char * strcasestr(const char *s1, const char *s2);

    • 傳回NULL表示沒有找到
    • …省略說明…

繼續閱讀