//C语言之字符函数与预处理
//1. 改变文件(光标)位置的函数及参数?
//fseek(FILE * stream, int offset, int mode);
//mode: SEEK_SET 0, SEEK_CUR 1, SEEK_END 2
//rewind(FILE * stream);
//fsetpos(FILE* stream, streampos*);
//2. 如何验证文件是否已到尾部?
//feof(FILE * stream);
//fgetc() == EOF
//3. 打开文件的模式?
//w, w + , r, r + , a, a +
//一、字符函数
//对于字符串或字符数组的操作相关的函数,如字符串的长度、查找字符、拼接、复制、分隔...
//字符处理函数都在 <string.h> 头文件中。
//1.1 字符串的查找函数
// 从_String 字符串查找 子串subString的位置,如果没有找到则返回NULL。
char* strstr(char* const _String,
const char* subString);
// 从_String 字符串查找 c字符的位置,未找到则返回NULL
// 从左到右的方式查找
char* strchr(char* const _String, char c);
// 从_String 字符串查找 c字符的位置,未找到则返回NULL
// 从右到左的方式查找
char* strrchr(char* const _String, char c);
//如
#include <stdio.h>
#include <string.h>
int main() {
char arr[128] = "123abcd998877";
// 查找子串
char* p1 = strstr(arr, "999");
if (p1 != NULL) {
printf("%s\n", p1);
}
// 查找字符
char* p2 = strchr(arr, 'c');
if (p2 != NULL) {
printf("p2 =%s\n", p2);
}
// 从右边开始查找字符,返回右边第一次出现的位置
char* p3 = strrchr(arr, '7');
if (p3 != NULL) {
printf("p3 =%s\n", p3);
}
return 0;
}
//1.2 字符串拼接与分隔函数
// 将src的内容拼接到dest内容之后,返回dest
char* strcat(char* dest, const char* src);
// 将src的前n个字符内容拼接到dest内容之后,返回dest
char* strncat(char* dest,
const char* src, size_t n);
// 按delimiters给定的所有分隔符对dest进行切割
// 切割的原理,将分隔符转成\0,遇到重复(可能是不同的分隔符)的则跳过,最后没有可切割时,则返回
//NULL, 表示完成了。
char* strtok(char* dest, const char* delimiters);
//如: 字符串的拼接
#define _CRT_SECURE_NO_WARNINGS
int main() {
char s1[128] = "123";
//s2指针指向的常量区的"abc"空间,
// 其空间内容是不可以修改的
char* s2 = "abc";
// 将s2的内容拼接到s1内容的后面,即修改s1空间
char* p1 = strcat(s1, s2);
printf("p1 is %s\n", p1);
char* s3 = "907";
// 将s3的前2个字符拼接到s1空间中
strncat(s1, s3, 2);
printf("p1 is %s\n", p1);
return 0;
}
//如2: 字符串的切割
#define _CRT_SECURE_NO_WARNINGS
int main() {
char s1[128] = "1,2.3#4@5.@6,@#7*8";
// 切割s1字符串,并指定切割的所有可能的字符串
// 切割的原理是将分隔符修改为 \0, 如果连续出现的,
// 只会修改第一个字符为\0, 之后则跳过重复的分隔符
// 切割失败时,表示字符串切割完成,则返回NULL
char* p = strtok(s1, ",.#@*");
while (p != NULL) {
printf("%s ", p); // 将上一次切割内容打印
// 因为第一次切割时,字符串内的内容并没有切割结束
// 因此第二次切割时,则会沿着上一次的结果继续切割
// 所以,第一个参数为NULL, 表示没有新位置,继上一次位置向后继续分隔
p = strtok(NULL, ",.#@*");
}
return 0;
}
//练习: 将s1数组的内容按行分隔,并读取每一行的pid 证件号和phone手机号。
char s1[1024] = "1001,19978651243\n1002,19978651244\n1004,19978655243\n1004,19978651943\n1005, 19778651243\n1006, 15978651243\n1007, 19978651990\n1008, 15578651240\n";
//完整示例:
int main() {
char s1[1024] =
"1001,19978651243\n1002,19978651244\n1004,19978655243\n1004,19978651943\n1005,19778651243\n1006, 15978651243\n1007, 19978651990\n1008, 15578651240\n";
char* line = strtok(s1, "\n");
char* lines[32];
int i = 0;
// 1. 先读取所有行的位置
while (line != NULL) {
lines[i++] = line;
line = strtok(NULL, "\n");
}
// 2. 针对每一行进行分隔
for (int k = 0; k < i; k++) {
char* pid = strtok(lines[k], ",");
char* phone = strtok(NULL, ",");
printf("pid(%s), phone(%s)\n", pid, phone);
}
//3. 验证s1的内容是否修改
printf("s1 is %s\n", s1);
return 0;
}
//1.3 自定义切割字符的函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
// 从arr中查找delims中任意一个字符,并修改为\0
char* mytok(char* arr, const char* delims) {
static char* next;
char* p;
if (arr == NULL) {
p = next;
}
else {
p = arr;
}
if (p == NULL) return NULL;
int len = strlen(p);
// 从arr中查找到 delims中任意一个分隔符
int i = 0;
int finded = 0;
for (; !finded && i < len; i++) {
for (int j = 0; j < strlen(delims); j++) {
if (p[i] == delims[j]) { // 查到了分隔符的位置
p[i] = 0; // 分隔符置 \0
finded = 1; // 查到了
break;
}
}
}
if (finded)
next = p + i; // 记录下一次分隔的位置
else
next = NULL; // 分隔结束
return p;
}
int main() {
char s1[] = "1,2,3,4";
char* n1 = mytok(s1, ",");
while (n1 != NULL) {
printf("%s\n", n1);
n1 = mytok(NULL, ",");
}
//printf("n1 is %s\n",n1); // 要求: 1
//char* n2 = mytok(NULL, ",");
//printf("n2 is %s\n", n2);
return 0;
}
//1.4 字符串比较函数
// 字符串比较是指的是两者的字符串按每一次的字符ASCII值进行比较, 比较的结果(返回值)有三种情
//况:
// 0: 两者的字符的内容完整一样
// 1: 前者(s1)中比较的字符ASCII大于后者(s2)
// -1: 前者(s1)中比较的字符ASCII小于后者(s2)
int strcmp(const char* s1, const char* s2);
// 功能同上,只不过只比较 前n个字符
int strncmp(const char* s1, const char* s2, int n);
int main() {
char s1[128] = "abc123";
// 如果s1中每一个字符的ASCII都与
// 第二个字符串的每一个字符的ASCII相同的情况,则返回0
int ret1 = strcmp(s1, "abc123");
// s1和比较字符串按从左到右的顺序进行每一个字符比较
// 存在一个字符比比较的字符的ASCII小,则返回-1
int ret2 = strcmp(s1, "dbc123");
// 如果s1中的字符比后者要大小,则返回1
int ret3 = strcmp(s1, "aac123");
printf("ret1=%d,ret2=%d, ret3=%d\n", ret1, ret2, ret3);
char s2[32] = "abcdef";
printf("s1 == s2: %d\n", strcmp(s1, s2));
// 只比较s1和s2的前3个字符,结果是 0
printf("%d\n", strncmp(s1, s2, 3));
return 0;
}
//练习: 简易版本的翻译app
//从键盘输入英文单词,并输出其解释中文, 直到输入q, quit退出。 英文单词的翻译在 words.txt
//文件中。
//文件内容如下: 文件的编码格式 ANSI
//red:红, 红色
//yellow : 黄, 黄色
//book : 书, 书籍, 图书
//blank : 黑, 黑色
int main() {
FILE* fw = fopen("words.txt", "rb");
/*fseek(fw, 0, SEEK_END);
long len = ftell(fw);
fseek(fw, 0, SEEK_SET);
char* buf = (char*)malloc(len+1);
memset(buf, 0, len + 1);
fread(buf, 1, len, fw);*/
/*printf("file size is %ld\n", len);
printf("%s", buf);*/
char line[64] = "";
while (1) {
printf("查询的单词:");
fgets(line, 64, stdin);
//strtok(line, "\n");//去掉换行
line[strlen(line) - 1] = 0;
if (strncmp(line, "q", 1) == 0) break;
fseek(fw, 0, SEEK_SET);// 确保文件的光标在开始位置
while (!feof(fw)) {
char fline[128] = "";
fgets(fline, 128, fw); // 从文件中读取一行
if (strlen(fline) == 0) break;
char* word = strtok(fline, ":");
// printf("word: %s\n", word);
if (strcmp(line, word) == 0) {
printf("%s\n", strtok(NULL, ":"));
break;
}
}
if (feof(fw)) {
printf("暂无此单词的翻译,请稍等新的版本!\n");
}
}
//free(buf);
fclose(fw);
return 0;
}
//1.5 字符串复制与清零函数
// 将src的内容复制到dest开始的位置,并返回dest
char* strcpy(char* dest, const char* src);
// 将src的前n个字符内容复制到dest开始的位置,并返回dest
char* strncpy(char* dest, const char* src, int n);
// 将src空间的内容的n个字节复制到dest空间中,返回dest位置
void* memcpy(void* dest, const void* src, size_t n);
// 功能同memcpy, 但memmove是已过时的函数
void* memmove(void* dest, const void* src, size_t n);
// 设置size大小的dest空间的值为n, 如果n为0时,表示初始化空间
memset(void* dest, int n, size_t size);
//如1
int main() {
char dest[256] = "";
// 将后者的字符串复制到dest位置空间中
strcpy(dest, "123");
// 复制之后,返回新内容的开始位置 (传入的位置)
char* p = strcpy(dest + 3, "456");
printf("dest is %s\n", dest);
printf("p is %s\n", p);
const char* s1 = "hi,lucy!";
// 将s1的lucy内容复制到 dest空间中
strncpy(dest, s1 + 3, 4);
printf("dest is %s\n", dest);
return 0;
}
//如2
typedef struct { int x; int y; } Point;
int main() {
char name[32] = "";
const char* s1 = "disen";
const char* s2 = "hi,";
// 1. 复制字符串
memcpy(name, s2, 3);
memcpy(name + 3, s1, 5);
printf("name is %s\n", name);
// 2. 复制非char类型的数组
int arr[30] = { 0 };
const int a1[3] = { 1, 2, 3 };
memcpy(arr, a1, 3 * sizeof(int));
for (int i = 0; i < 30; i++) {
if (arr[i] != 0)
printf("%d ", arr[i]);
}
//3. 复制结构体类型变量的空间
Point p1 = { 2, 5 };
Point p2 = { 8, 9 };
memcpy(&p1, &p2, sizeof(Point));
printf("p1(%d,%d)\n", p1.x, p1.y);
return 0;
}
//如3
int main() {
char dest[128] = "";
const char* s1 = "good";
memmove(dest, s1, 4);
printf("dest is %s, s1 is %s\n", dest, s1);
return 0;
}
//二、预处理
//预处理是程序编译的四个重要阶段之一, 程序编译过程:
//第一阶段: 预处理 , 对程序文件中的 # 相关指令进行处理, 如 #学include , #define, #if,
//#endif
//第二阶段: 编译, 将预处理之后的文件内容进行编译成汇编程序,即包含各种的指令和数据,以
//及语法检查。
//第三阶段: 汇编, 将汇编程序 编译成二进制文件, 如.obj 。
//第四阶段 : 链接, 链接指定头文件中相关函数的定义所在的库文件(静态库, 动态库 DLL), 链接之
//后,生成可执行文件。
//2.1 #define 指令
//#define 定义宏
//#define 宏名
//#define 宏名 值
//#define 宏名(参数名,...) 表达式
//2.2 #ifdef 和 #ifndef 指令
//#ifdef 宏名 // 验证是否定义 宏
//#ifndef 宏名 // 验证宏是否没有定义
//如:
#include <stdio.h>
#define PI 3.1415926
#define mul(a, b) (a)*(b)
double area(double r) {
// PI 宏在预处理时,则迭代成 表达式值 3.1415926
#ifdef PI
return PI * r * r;
#else
return 3.16 * r * r;
#endif // PI
}
int main() {
printf("area is %.6f\n", area(5.0));
// 预处理的表达式: 10-2*8-5;
printf("%d\n", mul(10 - 2, 8 - 5));
return 0;
}