---
# System prepended metadata

title: 8.C Characters and Strings
tags: [學校門口大樹石頭下, 程設好難, 他的課就很好睡阿]

---

# 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比Ａ早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~(ｒ我假設有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));//碰到１，回傳前面經過幾個字元
```
輸出結果：
:::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>