# 8.C Characters and Strings
:::spoiler 編輯分工用架構(分工在這裡段傳婕點開來看)
## <font color="#f00">⚠全部大概有40幾個functions我大概分一半(中間有分隔線)有問題直接說可以再討論</font>
## <ctype.h>
- ### int isblank(int c);
---
8.3 p.368
- ### int isdigit(int c);
- ### int isalpha(int c);
- ### int isalnum(int c);
- ### int isxdigit(int c);
---
8.3.2 p.371
- ### int islower(int c);
- ### int isupper(int c);
- ### int tolower(int c);
- ### int toupper(int c);
---
8.3.3 p.372
- ### int isspace(int c);
- ### int iscntrl(int c);
- ### int ispunct(int c);
- ### int isprint(int c);
- ### int isgraph(int c);
---
## <stdlib.h>
8.4.1 p.374
- ### double strtod(const char *nPtr, char **endPtr);
---
8.4.2 p.375
- ### long strtol(const char *nPtr, char **endPtr, int base);
---
8.4.3 p.376
- ### unsigned long strtoul(const char *nPtr, char **endPtr, int base);
---
## <stdio.h>
8.5.2 p.378
- ### int getchar(void);
---
8.5.1 p.377
- ### char *fgets(char *s, int n, FILE *stream);
- ### int putchar(int c);
---
- ### int puts(const char *s);
---
8.5.3 p.379
- ### int sprintf(char *s, const char *format, ...);
---
8.5.4 p.380
- ### int sscanf(char *s, const char *format, ...);
---
# <font color=blue>以上杜宗儫編輯</font>
# <font color=blue>以下段傳婕編輯</font>
## <string.h>
8.6.1 p.382
- ### char *strcpy(char *s1, const char *s2)
- ### char *strncpy(char *s1, const char *s2, size_t n)
---
8.6.2 p.382
- ### char *strcat(char *s1, const char *s2)
- ### char *strncat(char *s1, const char *s2, size_t n)
---
8.7 p.383
- ### int strcmp(const char *s1, const char *s2);
- ### int strncmp(const char *s1, const char *s2, size_t n);
---
8.8.1 p.386
- ### char *strchr(const char *s, int c);
---
8.8.2 p.387
- ### size_t strcspn(const char *s1, const char *s2);
---
8.8.5 p389
- ### size_t strspn(const char *s1, const char *s2);
---
8.8.3 p.387
- ### char *strpbrk(const char *s1, const char *s2);
---
8.8.4 p.388
- ### char *strrchr(const char *s, int c);
---
8.8.6 p.389
- ### char *strstr(const char *s1, const char *s2);
---
8.8.7 p.390
- ### char *strtok(char *s1, const char *s2);
---
8.9.1 p.392
- ### void *memcpy(void *s1, const void *s2, size_t n);
---
8.9.2 p.393
- ### void *memmove(void *s1, const void *s2, size_t n);
---
8.9.3 p.394
- ### int memcmp(const void *s1, const void *s2, size_t n);
---
8.9.4 p.394
- ### void *memchr(const void *s, int c, size_t n);
---
8.9.5 p.395
- ### void *memset(void *s, int c, size_t n);
---
8.10.1 p.396
- ### char *strerror(int errornum);
---
8.10.2 p.396
- ### size_t strlen(const char *s);
---
:::
## 8.2 Fundamentals of strings and characters
```c=
char color[]="blue";
char color[]={'b','l','u','e','\0'};
```
每個字串陣列結束於null-character('\0')
>如果要修改字串的文字,它需要儲存在陣列中
>[name=小蜜蜂]
>如果陣列沒有足夠的空間儲存null-charater將會出現錯誤
>[name=小蜜蜂]
>printf字串時會持續輸出直到遇到null-character為止
>[name=小蜜蜂]
>確認陣列空間足夠儲存字串,否則超過字符會覆蓋原先數據
>[name=小蜜蜂]
```c=
char word[20];
scanf("%19s",word);
```
在scanf時設定輸入字數需考慮null-character('\0')
反正就是記得null-character佔一個位子
>將單個字符做為字串處理時可能發生錯誤,因為字串是個pointer可能是個相當大個整數,但是字符是一個小整數(ascii值範圍為0~255)
>[name=小蜜蜂]
<font color="#f00">本章節舉例大量使用:</font>
```c=
printf("%s",(a==1)?"true":"false");
```
等同於
```c=
if(a==1){
printf("true");
}
else{
printf("false");
}
```
## 8.3.1 Functions isdigit, isalpha, isalnum and isxdigit
### <font color="#f00">⚠本小節使用的library為<ctype.h></font>
- ### int isdigit(int c);
用途:當c是數字時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isdigit('8') ? "8 is a digit" : "8 is not a digit" );
```
isdigit('8')回傳true value所以輸出"8 is a digit"的結果
- ### int isalpha(int c);
用途:當c是字母時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isalpha('A') ? "A is a letter" : "A is not a letter");
```
isalpha('A')回傳true value所以輸出"A is a letter"的結果
不論大小寫都是字母
- ### int isalnum(int c);
用途:當c是數字或字母時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isalnum('8') ? "8 is a digit or a letter" : "8 is not a digit or a letter");
printf("%s",isalnum('A') ? "A is a digit or a letter" : "A is not a digit or a letter");
```
isalnum('8')回傳true value所以輸出"8 is a digit or a letter"的結果
isalnum('A')回傳true value所以輸出"A is a digit or a letter"的結果
- ### int isxdigit(int c);
用途:當c是十六進制字符時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isxdigit('f') ? "f is a hexadecimal digit" : "f is not a hexadecimal digit");
```
isdigit('f')回傳true value所以輸出"f is a hexadecimal digit"的結果
## 8.3.2 Functions islower, isupper, tolower and toupper
### <font color="#f00">⚠本小節使用的library為<ctype.h></font>
- ### int islower(int c);
用途:當c是小寫字母時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",islower('f') ? "f is a lowercase letter" : "f is not a lowercase letter ");
```
islower('f')回傳true value所以輸出"f is a lowercase letter"的結果
- ### int isupper(int c);
用途:當c是大寫字母時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isupper('F') ? "F is a uppercase letter" : "F is not a uppercase letter ");
```
isupper('F')回傳true value所以輸出"F is a uppercase letter的結果
- ### int tolower(int c);
用途:當c是大寫字母時將c換成小寫字母,其餘返回值不變(ex. 7→7)
舉例:
```c=
printf("%s%c","U converted to lowercase is ",tolower('U'));
```
tolower('U')='u'
- ### int toupper(int c);
用途:當c是小寫字母時將c換成大寫字母,其餘返回值不變(ex. A→A)
舉例:
```c=
printf("%s%c","u converted to uppercase is ",toupper('u'));
```
tolower('u')='U'
## 8.3.3 Functions isspace, iscntrl, ispunct, isprint and isgraph
### <font color="#f00">⚠本小節使用的library為<ctype.h></font>
- ### int isspace(int c)
用途:當c是whitespace character時回傳true value,反之回傳0(false)
#### whitespace chatacter :
1. newline('\n')
2. space(' ')
3. form feed('\f')
4. carriage return('\r')
5. horinzontal tab('\t')
6. virtical tab('\v')
- ### int iscntrl(int c)
用途:當c是control character時回傳true value,反之回傳0(false)
#### control chatacter :
1. horinzontal tab('\t')
2. virtical tab('\v')
3. form feed('\f')
4. alert('\a')
5. backspace('\b')
6. carriage return('\r')
7. newline('\n')
- ### int ispunct(int c)
用途:當c不是a space,a digit,a letter (ex. $,#...)時回傳true value,反之回傳0(false)
- ### int isprint(int c)
用途:當c是打印字符(反正好像就是在螢幕上看的到字符)時回傳true value,反之回傳0(false)
舉例:
```c=
printf("%s",isprint('#')?"# is a printing character":"# is not a printing character")
```
isprint('#')回傳true value所以輸出"# is a printing character"的結果
```c=
printf("%s%s%s","Alert",isprint('\a')?"# is a":"# is not a","printing character")
```
isprint('\a')回傳false value所以輸出" is not a"的結果
- ### int isgraph(int c)
用途:當c是打印字符(但不包含空白字元)時回傳true value,反之回傳0(false)
:::danger
與 isprint不同的是不包含空白字元
:::
舉例:
```c=
printf("%s%s%s","Space",isgraph(' ')?"is a":"is not a","printing character other than space")
```
isprint(' ')回傳false value所以輸出" is not a"的結果
## 8.4.1 Function strtod
### <font color="#f00">⚠本小節使用的library為<stdlib.h></font>
用途:分離字串中的數字部分,當要轉換的字串第一個字元(空白字元會直接略過)無法被轉換成浮點數的話會回傳false value
```c=
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[30] = "20.30300 This is test";
char *ptr;
double ret;
ret = strtod(str, &ptr);
printf("數字(double)是 %lf\n", ret);
printf("字符串部分是:%s", ptr);
return 0;
}
```
輸出結果:
:::info
數字(double)是 20.303000
字符串部分是:This is test
:::
## 8.4.2 Function strtol
### <font color="#f00">⚠本小節使用的library為<stdlib.h></font>
用途:效果大概跟function strtod相同,大概差別是這個是轉換成long , function strod是轉換成double
```c=
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[30] = "2030300 This is test";
char *ptr;
long ret;
ret = strtol(str, &ptr, 0);
printf("數字(長整數)是 %ld\n", ret);//ret是基數,必須介於2和36(包含)之間,或者是特殊值0
printf("字符串部分是:%s", ptr);
return 0;
}
```
輸出結果:
:::info
數字(長整數)是 2030300
字符串部分是:This is test
:::
## 8.4.3 Function strtoul
### <font color="#f00">⚠本小節使用的library為<stdlib.h></font>
用途:效果大概跟function strtol相同,大概差別是這個是轉換成unsigned long , function strol轉換成long
```c=
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[30] = "2030300 This is test";
char *ptr;
long ret;
ret = strtol(str, &ptr, 0);
printf("數字(無符號長整數)是 %ld\n", ret);//ret是基數,必須介於2和36(包含)之間,或者是特殊值0
printf("字符串部分是:%s", ptr);
return 0;
}
```
輸出結果:
:::info
數字(無符號長整數)是 2030300
字符串部分是:This is test
:::
## 8.5.1 Functions fgets and putchar
### <font color="#f00">⚠本小節使用的library為<stdio.h></font>
- ### int *fgets(char *s,int n,FILE *steam);
用途:大致上與function gets相同,但fgets可以限制輸入大小
舉例:
```c=
#include<stdio.h>
int main(){
char buffer[5];
fgets(buffer,sizeof(buffer),stdin);
puts(buffer);
}
```
:::info
輸入:123456123456
輸出:1234 (正常空字元占一格)
輸入:12
輸出:12\n (多了換行字元)
:::
#### 使用fget要注意的地方:
由例子中看到fgets最麻煩的就是換行字元,只要資料輸入不足buffer大小就會出現換行字元'\n'所以要先除掉它,第二就是資料輸入那麼長,可能只要前面的5個字元就好了,剩下那多輸入的數值,就留在stream裡面了,這些值是無用值,會造成後面的輸入不正確
- ### int putchar(int c)
用途:輸出單個字元
舉例:
```c=
#include <stdio.h>
int main(void) {
char c;
printf("請輸入一個字元:");
c = getchar();
putchar(c);
return 0;
}
```
:::info
請輸入一個字元:A
A
:::
## 8.5.2 Function getchar
### <font color="#f00">⚠本小節使用的library為<stdio.h></font>
用途:輸入單個字元
舉例:
```c=
#include <stdio.h>
int main(void) {
char c;
printf("請輸入一個字元:");
c = getchar();
putchar(c);
return 0;
}
```
:::info
請輸入一個字元:A
A
:::
如果輸入了兩個以上的字元,則 getchar 會取得第一個字元,並將第二個字元留在緩衝區中,直到再使用 getchar 或 scanf 取得輸入。
## 8.5.3 Function sprinf
### <font color="#f00">⚠本小節使用的library為<stdio.h></font>
用途:把數字類的東西合併到字串陣列中
舉例:
```c=
#include <stdio.h>
#difine SIZE 80
int main()
{
int x;
double y;
puts("Enter an integer and a double:");
scanf("%d%lf",&x,&y);
char s[SIZE];
sprintf(s,"integer:%6d\ndouble:%7.2f",x,y);
printf("%s\n%s\n","The formatted output stored in array s is:",s);
}
```
輸出結果:
:::info
Enter an integer and a double:
298 87.375
The formatted output stored in array s is:
integer:298
double:87.375
:::
## 8.5.2 Function sscanf
### <font color="#f00">⚠本小節使用的library為<stdio.h></font>
用途:跟scanf功能相同,但sscanf是從以固定字符串為輸入源(不同於scanf從鍵盤輸入)
```c=
#include <stdio.h>
int main()
{
char name[20], tel[50];
char input[]="name:john age:40 tel:082-313530";
int age;
sscanf(input, "%*[^:]:%s %*[^:]:%d %*[^:]:%s", name, &age, tel);
printf("%s %d %s\n", name, age, tel);
return 0;
}
```
輸出結果:
:::info
john 40 082-313530
:::
### 好處:搭配%[ ]中括號裡面的東西可以有更多的功能
例如:
%[a-z]代表僅取到小寫字母為止:
```c=
#include <stdio.h>
int main()
{
char output[100];
char input[]="name:john age:40 tel:082-313530";
int age;
sscanf(input, "%[a-z]", output);
printf("%s",output);
return 0;
}
```
輸出結果:
:::info
name
:::
%[^a]代表僅取到不是a為止:
```c=
#include <stdio.h>
int main()
{
char output[100];
char input[]="name:john age:40 tel:082-313530";
int age;
sscanf(input, "%[^a]", output);
printf("%s",output);
return 0;
}
```
輸出結果:
:::info
n
:::
%*[]表示跳過此數據不讀入:
```c=
#include<stdio.h>
int main(){
char input[]="abc.123@abc";
char output[100];
sscanf(input,"%*[^.].%[^@]",output);
printf("%s",output);
}
```
輸出結果:
:::info
123
:::
## 8.6.1&2 Function strcpy & strcat
### <font color="#f00">⚠本小節使用的library為<string.h></font>
用途:兩個字串陣列間的取代(strcpy)或連接(strcat)
- ### strcpy~(copy)~
將第二個陣列複製並取代第一個陣列裡的內容
```c=
#include<stdio.h>
#include<string.h>
int main()
{
char x[30]="給我帽子好嗎please";
char y[] = "Hello world";
printf("原本:\n---------\nx:%s\ny:%s\n\n",x,y);
strcpy(x,y);
printf("strcpy:\n---------\nx:%s\ny:%s\n",x,y);
return 0;
}
```
輸出結果:
:::info
原本:
---------
x:給我帽子好嗎please
y:Hello world
strcpy:
---------
x:Hello world
y:Hello world
:::
- ### strcat~(concatenate連結)~
將第二陣列連結到第一陣列的尾(第一陣列的空字元會被覆蓋掉),回傳第一陣列
```c=
#include<stdio.h>
#include<string.h>
int main()
{
char x[50]="給我帽子好嗎please";
char y[] = "Hello world";
printf("原本:\n---------\nx:%s\ny:%s\n\n",x,y);
strcat(x,y);
printf("strcat:\n---------\nx:%s\ny:%s\n",x,y);
return 0;
}
```
輸出結果:
:::info
原本:
---------
x:給我帽子好嗎please
y:Hello world
strcat:
---------
x:給我帽子好嗎pleaseHello world
y:Hello world
:::
- ### strncpy & strncat
多了n,就是可以指定要使用多少第二陣列的字符而已
```c=
strncpy(x,y,SIZE1);
/******************************************
*注意,若SIZE1<=y的陣列大小,則結果不會附加空字元 *
******************************************/
strncat(x,y,SIZE2);
```
## 8.7 Function strcmp
### <font color="#f00">⚠本小節使用的library為<string.h></font>
用途:比較兩個字串
- ### strcmp~(compare)~
```c=
s1[] = "Fat";
s2[] = "FAt";
```
- strcmp會一個個字元互相比較( s1[0] 比 s2[0] ; s1[1] 比 s2[1]...)
- ==若兩個字串相等,則回傳0==(很特殊,因為布林邏輯中,0表示偽)
- 若s1 > s2,回傳正值 ; 反之亦然
```c=
char s1[] = "Fat";
char s2[] = "FAt";
printf("Fat : FAt = %d\n",strcmp(s1,s2));
char s3[] = "Fat";
char s4[] = "Fat";
printf("Fat : Fat = %d\n",strcmp(s3,s4));
char s5[] = "FAt";
char s6[] = "Fat";
printf("FAt : Fat = %d\n",strcmp(s5,s6));
```
輸出結果:
:::info
Fat : FAt = 32
Fat : Fat = 0
FAt : Fat = -32
:::
> 字串間的大小之分,取決於最早比到不同的字元,該字元的ASCII碼==先後順序==
EX. a = 65 ; A = 97。a先於A,所以a > A,且a比A早32個序位
但是strcmp的回傳值會因編譯器不同而有所差異,反正我們大部分都用來看是不是相等而已
## 8.8 字串搜尋函式
### <font color="#f00">⚠本小節使用的library為<string.h></font>
用途:當你需要找特定字元/字串時用
- ### strchr~(character)~
- 找字元在字串第一次出現的位置,並回傳其指標
```c=
char s1[]="Hello from the other side.";
//有e出現的地方:Hello, the, other, side
printf("%s\n",strchr(s1,'e'));//回傳第一個e的指標(即其位址)
```
輸出結果:
:::info
ello from the other side.
:::
- ### strrchr~(r我假設有later之意)~
- 找字元在字串最後一次出現的位置,並回傳其指標
```c=
char s1[]="Hello from the other side.";
//有e出現的地方:Hello, the, other, side
printf("%s\n",strrchr(s1,'e'));//回傳最後一個e的指標
```
輸出結果:
:::info
e.
:::
- ### strpbrk
- 找字串2任何字元set在字串1第一次出現的位置,並回傳其指標
```c=
char s1[] = "I like ducks.";
char s2[] = "aeoui";//想像成s2的字串集合
printf("%s\n",strpbrk(s1,s2));//回傳符合s2集合的第一個位址指標
```
輸出結果:
:::info
ike ducks.
:::
- ### strstr
- 找字串2在字串1第一次出現的位置,並回傳其指標
```c=
char s1[] = "duces lucy ducks.";
char s2[] = "uck";
printf("%s\n",strstr(s1,s2));//回傳完全符合s2字串的第一個位址指標
```
輸出結果:
:::info
ucks.
:::
- ### strcspn
- 計算在字串1中,遇到第一個屬於字串2的字元set前經過幾個字(長度)
- 回傳的資料型態是unsigned long(==lu==)
```c=
char s1[] = "123456789";
char s2[] = "468";
printf("%lu\n",strcspn(s1,s2));//碰到4,計算4之前經過幾個字元
```
輸出結果:
:::info
3
:::
- ### strspn
- 跟上面類似,但改成不屬於
```c=
char s1[] = "4681864";
char s2[] = "468";
printf("%lu\n",strspn(s1,s2));//碰到1,回傳前面經過幾個字元
```
輸出結果:
:::info
3
:::
- ### strtok~(token字符)~
- 將字串依照你指定的分割字元,切成一個個字符,並回傳其指標
```c=
char s1[]="(02)0800-326-731";
char *tokenPtr = strtok(s1,"()-");//回傳的資料型態為指標,且一次只分割一個字符
/************************************
* strtok回傳第一個不為分界字元的字元指標,*
* 並將下一個遇到的分界字元改成'\0' *
* **********************************/
while(tokenPtr!=NULL)//如果沒字符可分割,strtok會回傳NULL
{
printf("%s\n",tokenPtr);
tokenPtr = strtok(NULL,"()-");//此NULL表示strtok應從上次切割完儲存的字符指標開始
}
```
輸出結果:
:::info
02
0800
326
731
:::
## 8.9 記憶體函式
### <font color="#f00">⚠本小節使用的library為<string.h></font>
在這個章節講的「物件」== 記憶體區塊內的資料
用途:
- ### memcpy
- 與strcpy相同,將s2指向的物件,複製指定的==位元組==數量資料至s1所指向的物件,此函式可以接收任何資料型別的指標
- 在已知所需複製字串長度時,memcpy比strcpy更有效率
- s1 s2若有部分指向的物件內容是相同的,則是未定義行為
- 直接放在printf裡要加(char *)
```c=
char s1[17];
char s2[] = "Copy this string";
memcpy(s1,s2,17);
printf("s1 = %s\n",s1);
```
輸出結果:
:::info
s1 = Copy this string
:::
- ### memmove
- 跟memcpy類似,只是可以接受部分物件內容重疊
> 所以可以對同個字串陣列做複製貼上
- 回傳值要(char *)
```c=
char s1[] = "Hot dog bad";
printf("原本s1:\n%s\n-----------\n",s1);
printf("memmove:\n%s\n----------\n",(char *)memmove(s1,&s1[8],3));
```
輸出結果:
:::info
原本s1:
Hot dog bad
\-----------
memmove:
bad dog bad
:::
記得strcpy, memcpy, memmove都是將複製的字串貼到s1的第0元素指標,所以前面的內容會被覆蓋掉
- ### memcmp
- 跟strcmp類似,只是可以指定比較的位元組
```c=
char s1[] = "Hot dog bad";
char s2[] = "Hot dog Ass";
printf("strcmp比較全部:\n%d\n---\n",strcmp(s1,s2));
printf("memcmp指定比較部分:\n%d\n",memcmp(s1,s2,7));
```
輸出結果:
:::info
strcmp比較全部:
33
\-\-\-
memcmp指定比較部分:
0
:::
- ### memchr
- 跟strchr類似,只是可以指定搜尋的字串長度範圍
- 回傳值要(char *)
```c=
char s1[] = "Hoooo";
printf("strchr:\n%s\n---\n",strchr(s1,'o'));
printf("memchr:\n%s\n",(char *)memchr(s1,'o',3));
```
輸出結果:
:::info
strchr:
oooo
\-\-\-
memchr:
oooo
:::
- ### memset
- 會將s2字元(單一位元組)連續複製到s1指定的前n位元組
- 適合為陣列元素初始化
- 傳回值要(char *)
```c=
char s1[] = "123456789";
printf("原本s1:\n%s\n---\n",s1);
printf("memset:\n%s\n",(char *)memset(s1,'0',4));
```
輸出結果:
:::info
原本s1:
123456789
\-\-\-
memset:
000056789
:::
## 8.9 Function strerror & strlen
### <font color="#f00">⚠本小節使用的library為<string.h></font>
- ### strerror
- strerror會接收一個錯誤號碼,然後產生一個錯誤訊息字串,此字串會回傳指標
```c=
for(int i = 0;i<=5;i++)
{
printf("(%d): %s\n",i,strerror(i));
}
```
輸出結果:
:::info
(0): Undefined error: 0
(1): Operation not permitted
(2): No such file or directory
(3): No such process
(4): Interrupted system call
(5): Input/output error
:::
錯誤訊息的號碼定義於<errno.h>
- ### strlen
- 回傳字串的字元數(==不包括空字元==)
- 回傳資料型態是==lu==
```c=
char s1[] = "Hello pretty girl.";
printf("%lu\n",strlen(s1));
```
輸出結果:
:::info
18
:::
###### tags: `程設好難` `學校門口大樹石頭下` `他的課就很好睡阿`
<style>
.navbar-brand::after { content: " × 老葉的程式設計"; }
</style>