字符串和文件操作
文章目录
1.字符串
1.1C/C++字符串概述
1.1.1字符串常量
字符串常量
格式要求:采用英文双引号包含的所有内容,C/C++语言规定,字符串常量都存在一个 ‘\0’ 结尾
在内存的【数据区】,而且字符串本身在程序中是对应当前字符串所处内存的空间首地址,可以采用 char *存储对应首地址
"ABCDEFG" 占用的字节为 8 个字节!!!包括'\0'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char*argv[])
{
printf("字符串占用字节数:%ld\n",sizeof("ABCDEFG"));
/*
str 可以存储字符串常量在内存【数据区】的空间首地址
同时也是当前字符串下标为 0 的元素空间首地址
*/
char *str = "1234567890";
// str(%p)空间首地址 ==> 当前字符串下标为 0 的元素 1 的首地址
printf("str: %s\n",str);
printf("%p\n","1234567890");
// str 下标为 5 的元素的首地址 %s ==> 6
printf("&str[5] : %s\n", &str[5]);
// 通过访问字符串 str 的地址 * 取值str 字符串
printf("*str : %c\n", *str);
// 通过访问字符串 str 下标为 5 的首地址 取值str[5]的元素
printf("str[5] : %c\n", str[5]);
printf("\"1234567890\"[5] : %c\n", "1234567890"[5]);
return 0;
}
1.2.1 字符数组
存储字符串的是一个数组,可以理解为字符串【变量】,其中的数据可以通过操作指定下标,修改
存储字符串的如果是一个指针【常量】 不可以通过访问地址操作其中的内容,会报段错误
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char arr1[4] = {'a', 'b', 'c', '\0'};
/*
arr1 数组名为数据类型为 char * 是当前字符数据空间首地址,同时也是数组
下标为 0 的元素空间首地址
可以将一个符合 C/C++ 规范的字符数组当作一个字符串【变量】操作。
C/C++ 规范要求末尾必须有 \0
*/
// 通过访问 arr1 的首地址 打印出 字符串 abc
printf("arr1 = %s\n", arr1);
// 通过访问 arr1 的首地址 打印出 arr1 的地址
printf("arr1 = %p\n", arr1);
// 通过访问 arr1 下标为 1 的空间地址 打印出 arr1[1] 的元素
printf("arr1[1] = %c\n", arr1[1]);
// arr1 是一个数组存储字符串,可以认为是一个字符串变量
// 其中的元素可以通过下标访问地址修改
arr1[1] = 'p';
printf("arr1 = %s\n",arr1);
// 此时存储字符串的是一个指针常量
// 常量其中的数据不能随意修改会报错【段错误】
char *str = "ABC";
str[1] = 'p';
printf("str = %s\n",str);
return 0;
}
1.2 字符串函数
【重要提示】
- 字符串是一个常量,数据内容无法修改,地址内容无法修改
- 字符串函数操作请注意【内存空间问题】
- 字符串函数操作,注意返回值类型和返回值情况
1.2.1 strcpy 和 strncpy 函数
复制
char *strcpy(char *dest,const char *src,size_t n); 将 scr 指向的字符串内容,拷贝到 dest 字符数据空间中 要求: 1.src 可以是字符串常量,也可以是字符数组 2.dest 必须是可以存储字符数据的内存空间,一般是字符数组或者动态申请内存字符空间,而且空间要求足够使用 【如果 dest 中有数据 全覆盖 直到 src 中 \0 结束 ==> 默认 dest 中无元素】 char *strncpy(char *dest,const char *src,size_t n); 将 scr 指向的字符串内容,拷贝到 dest 字符数据空间中,最多复制 n 个字符 要求: 1.src 可以是字符串常量,也可以是字符数组 2. dest 必须是可以存储字符数据的内存空间,一般是字符数组或者动态申请的内存字符空间,而且龙剑要求足够使用。 【如果 dest 中有数据,src > dest 中有效元素 全覆盖; src < dest 中有效元素个数 复制 n 个之后 如果有\0 不保留,没有\0 保留 dest 中内容】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
/*
【重要提示】
- 字符串是一个常量,数据内容无法修改,地址内容无法修改
- 字符串函数操作请注意【内存空间问题】
- 字符串函数操作,注意返回值类型和返回值情况
*/
char dest[20] = {'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9'};
// 【不默认结尾有\0】 必须 结尾空出一个 \0 的位置或者直接在里面定义
/*
两种正确定义方式
char dest[21] = {'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9', '\0'};
char dest[21] = "01234567890123456789";
*/
char *src = "0123456789";
printf("dest : %s\n",dest);
printf("src : %s\n",src);
printf("_______________________\n");
// 将 src 中的内容 "0123456789" 拷贝到 dest 中
// 如果 dest 中有数据 全覆盖 直到 src 中 \0 结束 ==> 默认 dest 中无元素
strcpy(dest,src);
printf("dest : %s\n",dest);
printf("src : %s\n",src);
char dest2[21] = {'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9'};
char dest1[4] = {'\0'};
char *src1 = "2345";
printf("_______________________\n");
// 将 src 中的内容 "2345" 拷贝到 dest 中 最多复制 n 个
// 如果 dest 中有数据,src > dest 中有效元素 全覆盖
// src < dest 中有效元素个数 复制 n 个之后 如果有\0 不保留,没有\0 保留 dest 中内容
strncpy(dest2,src1,4);
printf("dest2 : %s\n", dest2);
printf("src1 : %s\n", src1);
return 0;
}
1.2.2 strcat 和 strncat
末尾添加
char *strcat(char *dest,const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串结尾。 注意: 1.末尾标记 \0 2.返回值数据类型必须是 char *,返回内容是 deat 对应的空间地址 3.dest 必须有对应的内存空间 char *strncat(char *dest,const char *src,size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止(n 为追加的数量src中) 注意: 1.末尾标记'\0' 2.返回值数据类型为 char *,返回内容为 dest 对应的空间地址 3.dest必须有对应的空间内容
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char dest[20] = {'A','B','C','\0'};
char *src = "123456789";
char *s1 = strcat(dest,src);
printf("s1 = %s\n",s1);
printf("dest = %s\n",dest);
printf("_____________________\n");
// 注意此时 dest 已经被追加过了为 "ABC123456789"
// 4 表示追加的数量 \0 被覆盖
char *s2 = strncat(dest,src,4);
printf("s2 = %s\n",s2);
printf("dest = %s\n",dest);
return 0;
}
1.2.3 strcmp 和 strncmp
比较
字符串数据通过 == 等值判断,比较的不是【字符串内容】
是比较两端的地址是否一致如果是两个内容一致的字符串常量比较,== 判断结果为1(true),
如果是两个字符数组或者字符串和字符数组内容一致,但是通过 == 等值判断结果为 0 (false)
【要求,任何一个语言中,字符串数据比较是否一致,请使用字符串相关函数】不允许使用 == 等值比较
int strcmp(const char *str1,const char *str2) 把 str1所指向的字符串和 str2 所指向的字符串进行比较 返回值 1.0 表示 str1 和 str2 两个字符串不一致 2.1 or -1 表示两个字符串不一致 int strncmp(consr char *str1,const char *str2,sizze_t n) 把 str1所指向的字符串和 str2 所指向的字符串进行比较,最多比较前 n 个字节
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
printf("ret : %d\n",strcmp("ABC","ABC"));
printf("ret : %d\n",strcmp("aBC","CBC"));
printf("ret : %d\n",strcmp("ABC","AzC"));
printf("------------\n");
/*
字符串数据通过 == 等值判断,比较的不是【字符串内容】
是比较两端的地址是否一致,如果是两个内容一致的字符串
常量比较,== 判断结果为1(true),如果是两个字符数组
或者字符串和字符数组内容一致,但是通过 == 等值判断
结果为 0 (false)
【要求,任何一个语言中,字符串数据比较是否一致,请使用字符串相关函数】
C strcmp strncmp
【不允许使用 == 等值判断】
*/
//比较的是地址 对于字符串函数来说 "ABC" 地址都一样
// 所以结果为1 ture
printf("ret :%d\n","ABC" == "ABC");
char arr[4] = {'A','B','C','\0'};
// 字符串"ABC" 地址和 arr 中存储的 "ABC"地址不一样
printf("ret :%d\n","ABC" == arr);
printf("-------------------\n");
printf("ret : %d\n",strncmp("ABCDE","ABCDE111111",5));
printf("ret : %d\n",strncmp("ABCDEF","ABCDE11111",6));
return 0;
}
1.2.4 strchr 和strrchr
搜索
char *strchr(const char*str,int c); 在参数 str 所指向的字符串中搜索第一次出现字符 c (一个无符号字符)的位置 // int c传递的是该字符的 ASCLL 码为了兼容和灵活 // 如果没有该元素返回 NULL s char *strrchr(const *str,int c); // r 代表 right 在参数 str 所指向的字符串中搜索最后一次出现字符 c (一个无符号字符)的位置
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char *str = "ABCDE7BCD";
char *p1 = strchr(str,'7');
printf("p1 = %s\n",p1);
printf("p1 = %p\n",p1);
/*
p1 和 str 都是 char * 指针,存储对应的数据的地址,
因为字符数据在内存中,占用的空间字节数为1,两个字符
地址相减,可以认为是下标位置,要求必须在同一个字符串、
地址范围内
*/
printf("p1 - str : %ld\n",p1 - str);
printf("--------------\n");
int arr[5] = {1, 2, 3, 4, 5};
/*
两个同数组中元素取地址相减操作
CPU 首先计算两个地址直接的字节差,根据数据类型相除
最终结果是两个地址的【下标差/坐标差】
*/
printf("&arr[4] - &arr[1] : %ld\n",&arr[4] - &arr[1]);
printf("--------------------\n");
char *p2 = strrchr(str,'Y');
printf("p2 = %s\n",p2);
printf("p2 = %p\n",p2);
return 0;
}
1.2.5 strstr 和 strlen
char *strstr(const char *haystack,const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char *str = "ABCDEFG";
char *target = "DEF";
char *p = strstr(str,target);
// 位置打印出来为 地址 D的内存空间首地址
printf("p = %p\n",p);
// 地址相减为中间的距离 首地址空间距离
printf("p - str = %ld\n",p - str);
printf("strlen = %ld\n",strlen("ABCDE"));
char arr[5] = {'A','B','C','D','\0'};
// 计算字符串 str 的长度,直到空结束字符,不包括结束字符 \0
printf("strlen = %ld\n",strlen(arr));
return 0;
}
1.2.6 memory 函数
以下函数具备字符串处理功能,更多的使用场景是针对于内存数据操作
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 *memset(void *str,int c,size_t n) 复制字符 c (一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。 void *memcpy(void *dest,const void *src,size_t n) 从 src 复制 n 个字符到 dest void *memmov(void *dest,const void *src,size_t n) 从 src 复制 n 个字符到 dest /* memcpy 和 memmove 功能和 strncpy 一致,memmove 可以更好的保护拷贝源数据 src 【使用 memmove 不会导致内存数据丢失】 */
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct student { int id; char name[32]; int age; } Student; int main(int argc, char const *argv[]) { /* void *memchr(const void *str,int c,size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c (一个无符号字符) */ char *str = "ABCDEFABCDEFG"; void * p = memchr(str,'C',5); printf("p = %p\n",p); /* int memcmp(const void *str1,const void *str2,size_t n) 把 str1 和 str2 的前 n 个字符进行比较,0 相同,非 0 不同 */ char * str1 = "ABCDE"; char * str2 = "ABCDE"; int ret = memcmp(str1,str2,5); printf("ret = %d\n",ret); printf("---------------\n"); Student *stu1 = (Student *)malloc(sizeof(Student)); memset(stu1,0,sizeof(Student)); Student *stu2 = (Student *)malloc(sizeof(Student)); memset(stu2,0,sizeof(Student)); stu1->id = 1; strcpy(stu1->name,"James"); stu1->age = 39; stu2->id = 1; strcpy(stu2->name,"James"); stu2->age = 39; ret = memcmp(stu1,stu2,sizeof(Student)); printf("ret = %d\n",ret); free(stu1); free(stu2); stu1 = NULL; stu2 = NULL; return 0; }
1.2.7 strtok 函数待更新
2.文件操作IO
2.1 IO 概述
I ==> Input 输入
O ==> Output 输出
【输入输出 IO 操作参照物是当前程序运行使用的内存】
内存数据流出到外部 ==> 输出 write put
外部数据流入到内存 ==> 输入 read get基于冯诺依曼计算机理论
1.二进制程序思想
2.万物皆文件思想,都采用 IO 操作对应内容,无论是软件还是硬件。
2.2 fopen 打开文件操作
FILE *fopen(const char *filename, const char *mode );
/*
filename 提供当前函数对应文件路径,路径可以是绝对路径 or 相对路径
mode 模式,选择当前文件的打开模式
*/
mode —— 模式
r
打开一个已有的文本文件,允许读取文件。
w
打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a
打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+
打开一个文本文件,允许读写文件。
w+
打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+
打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式;
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
补充说明,区分二进制文件的方式是可以利用记事本打开对应文件,如果文件是可视化文本文件,就普通文本文件内容,如果是乱码,就是二进制文件。平时的图片,视频,音频,可执行文件,都是二进制文件。
2.3fclose关闭文件操作
int fclose(FILE *fp); /*需要提供给当前函数都是文件指针 FILE *,如果文件关闭成功,返回 0 ,失败返回 EOF(-1)【注意】 文件打开之后必须关闭!!!*/
2.4fget 和 fputc
针对于字符操作
int fput(int c,FILE *fp); /*写入字符数据到文件中,需要提供给函数的参数是 int 类型字符数据, 对于代码而言虽然提供的数据 为 int 类型,但实际写入到文件中的内容是字符数据, int类型数据有且是由【低 8 位】有效写入到文件中, 第二个参数是文件指针,要求当前文件指针具备【写入能力】 模式限制 : w a r+ w+ a+ */ int fgetc(FILE * fp); /*从文件中读取一个字节数据,返回值类型为 int 类型, 但是实际有效数据为 int 类型内存的【低 8 位】, 在读取的过程中,【读取指针会自动后移】完成整个文件的文件的读取操作 如果文件操作到末尾返回 EOF(-1)End of file*/