C语言字符函数与字符串函数超详解

news2024/12/28 2:45:10

文章目录

  • 前言
  • 1. 字符分类函数
  • 2. 字符转换函数
  • 3. strlen
    • 3. 1 strlen 的使用
    • 3. 2 strlen 的模拟实现
  • 4. strcpy
    • 4. 1 strcpy 的使用
    • 4. 2 strcpy 的模拟实现
  • 5. strcat
    • 5. 1 strcat 的使用
    • 5. 2 strcat 的模拟实现
  • 6. strcmp
    • 6. 1 strcmp 的使用
    • 6. 2 strcmp 的模拟实现
  • 7. strncpy 函数的使用
  • 8. strncat 函数的使用
  • 9. strncmp 函数的使用
  • 10. strstr
    • 10. 1 strstr 的使用
    • 10. 2 strstr 的模拟实现
  • 11. strtok 函数的使用
  • 12. strerror函数的使用


前言

在编程的过程中,我们经常要处理字符字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数,接下来我们就了解一下这些函数。

1. 字符分类函数

C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。
这些函数的使用都需要包含一个头文件是 ctype.h
cplusplus上的 ctype.h 。
下面是常用的字符分类函数

字符分类函数
可以看出来,这些函数的用法及其相似,因此我们借助其中一个进行讲解。

int islower (int c);

islower 是能够判断参数部分的c是否是小写字母的函数。
通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,
如果不是小写字母,则返回0。

来做一个测试:
写一个代码,将字符串中的小写字母转大写,其他字符不变。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
void Upper(char* c)
{
	(*c) -= 32;//大写字母和小写字母的差是32
}

int main()
{
	char str[] = "Hello World";
	for (int i = 0; i < strlen(str); i++)//strlen 函数返回的是 str 的长度,后面会介绍
	{
		if (islower(str[i]))
			Upper(&str[i]);
	}
	printf("%s", str);
}

输出结果为:
输出结果

2. 字符转换函数

C语言提供两个字符转换函数:

int tolower ( int c ); //将参数传进去的⼤写字母转⼩写 
int toupper ( int c ); //将参数传进去的⼩写字母转⼤写

这两个函数同样也在 ctype.h中。
上面的代码,我们将小写转大写,是 -32 完成的效果,有了转换函数,就可以直接使用 tolower 函数。
修改后的代码:

int main()
{
	char str[] = "Hello World";
	for (int i = 0; i < strlen(str); i++)
	{
		if (islower(str[i]))
			str[i] = toupper(str[i]);
	}
	printf("%s", str);
}

注:以下函数均包括于string.h中。

3. strlen

3. 1 strlen 的使用

size_t strlen ( const char * str );

注意:

  1. 字符串以 '\0' 作为结束标志,strlen 函数返回的是在字符串中 '\0' 前面出现的字符个数
    (不包含 '\0' )。
  2. 参数指向的字符串必须要以 '\0'结束。
  3. 注意函数的返回值为 size_t,是无符号的!
  4. strlen 的使用需要包含头文件(string.h

关于它的使用,也就是将一个字符串的首元素地址传递过去,然后就可以接收它的返回值了。
示例:

#include<stdio.h>
#include<string.h>
int main()
{
	char* a = "bbb";
	char b[] = "abcd";
	printf("%zd %zd", strlen(a), strlen(b));//strlen 的返回值是 size_t 类型的,打印的时候用%zd
	return 0;
}

运行结果:
输出结果

3. 2 strlen 的模拟实现

模拟实现 strlen 的核心思路其实就是:找到字符串的 ‘\0’ ,然后找到其与字符串首元素中间的字符个数

方法一:计数器

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

size_t my_strlen(char* str)
{
	//计数器
	size_t count = 0;
	char* cur = str;
	while (*cur != '\0')//这里也可以直接写成 while(*cur),因为'\0'的码值为 0
		cur++,count++;
	return count;
}

int main()
{
	char* a = "abcdefg";
	printf("%zd\n", strlen(a));//对照
	printf("%zd\n", my_strlen(a));
	return 0;
}

方法二:指针-指针

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

size_t my_strlen(char* str)
{
	//指针-指针
	char* cur = str;
	while (*cur)
		cur++;
	return cur - str;//指针-指针得到的是指针之间的元素个数
}

int main()
{
	char* a = "abcdefg";
	printf("%zd\n", strlen(a));
	printf("%zd\n", my_strlen(a));
	return 0;
}

方法三:不创建临时变量

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

size_t my_strlen(char* str)
{
	//不创建临时变量
	if (!*str)//递归的终止条件
		return 0;
	else
		return 1 + my_strlen(str + 1);
}

int main()
{
	char* a = "abcdefg";
	printf("%zd\n", strlen(a));
	printf("%zd\n", my_strlen(a));
	return 0;
}

不创建临时变量模拟实现 strlen 使用到了函数递归,如果你对上面的代码有疑惑,可以看函数递归与迭代这篇博客,这里不再过多赘述。

4. strcpy

4. 1 strcpy 的使用

 char* strcpy(char * destination, const char * source );

它的作用就是将 source 中的字符串拷贝到 destination 中。

  1. 源字符串必须以'\0'结束。
  2. 会将源字符串中的 '\0'拷贝到目标空间。
  3. 目标空间必须足够大,以确保能存放源字符串。
  4. 目标空间必须可修改

使用范例:

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

int main()
{
	char str1[] = "abcd";
	char str2[] = "efghxxxxx";
	printf("str1 = %s\nstr2 = %s\n\n", str1, str2);
	strcpy(str2, str1);
	printf("str1 = %s\nstr2 = %s\n\n", str1, str2);
	return 0;
}

结果:
结果

4. 2 strcpy 的模拟实现

要想实现这个函数,我们要先搞清楚 strcpy 是怎么工作的,在拷贝结束之后,str2 里究竟放的是什么, 我们可以通过调试上面的代码来得到答案:
strcpy函数执行前
strcpy函数执行后
可以看到,在执行 strcpy 函数后, str2 里实际存放的不只是 str1 的数据,只是在拷贝的时候将 str1'\0' 拷贝过来了,所以 str2 的内容变得和 str1 一样了。

这样,我们就可以开始尝试模拟实现 strcpy 函数了。

#include<stdio.h>

char* my_strcpy(char* destination, const char* source)
{
	//注意返回值,返回的是复制结束后 destination 的首元素地址
	char* ret = destination;
	while (*destination++ = *source++);
	*destination = '\0';//可不要忘了'\0'!
	return ret;
}

int main()
{
	char str1[] = "abcd";
	char str2[] = "efghxxxxx";
	printf("str1 = %s\nstr2 = %s\n\n", str1, str2);
	my_strcpy(str2, str1);
	printf("str1 = %s\nstr2 = %s\n\n", str1, str2);
	return 0;
}

5. strcat

5. 1 strcat 的使用

char *strcat(char *dest, const char*src)

strcat 用于连接两个字符串,将 src 中的字符串连接到 dest 字符串的后面,连接时会删除掉 src 后面的'\0'

  1. 源字符串必须以'\0'结束。
  2. 目标字符串中也得有'\0',否则没办法知道追加从哪里开始。
  3. 目标空间必须可修改
  4. 目标空间必须足够大,能容纳下源字符串的内容。

使用范例:

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

int main()
{
	char str1[50] = "abcd";//注意目标字符串要足够大
	char str2[] = "efghxxxxx";
	strcat(str1, str2);
	printf("%s", str1);
	return 0;
}

运行结果:
运行结果

5. 2 strcat 的模拟实现

在这里我们不妨多思考一下:在这个函数的模拟实现中,我们必然会涉及到指针的解引用,那么为了防止空指针的解引用,我们可以使用 assert 进行断言,这样在排查问题时也更方便。

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

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);//断言 dest 和 src 这两个指针都不为空
	//注意返回值,也是返回目标字符串的首元素地址
	char* ret = dest;
	while (*dest)//找 dest 字符串的'\0'
		dest++;
	while (*dest++ = *src++);
	*dest = '\0';//这两步和 strcpy 是一样的
}

int main()
{
	char str1[50] = "abcd";//注意目标字符串要足够大
	char str2[] = "efghxxxxx";
	my_strcat(str1, str2);
	printf("%s", str1);
	return 0;
}

不仅是 strcat 函数,实际上 strlenstrcpy 的模拟实现中都可以加入 assert 断言来增强代码的健壮性

6. strcmp

6. 1 strcmp 的使用

int strcmp (const char * str1, const char * str2)

这个函数用来比较两个字符串是否相同,关于它的返回值,我们不妨来看看cplusplus上对返回值的描述。
返回值
也就是说:

第一个字符串大于第二个字符串,则返回大于 0 的数字
第一个字符串等于第二个字符串,则返回 0
第一个字符串小于第二个字符串,则返回小于 0 的数字

strcpm是如何比较两个字符串?
比较两个字符串中对应位置上字符ASCII码值的大小。

使用范例:

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

int main()
{
	char str1[] = "abcd";
	char str2[] = "abcd";
	char str3[] = "abce";
	printf("%d %d", strcmp(str1, str2), strcmp(str1, str3));
	return 0;
}

输出结果:
输出结果

6. 2 strcmp 的模拟实现

#include<stdio.h>

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 || *str2)//当 str1 和 str2 有一个不为 '\0' 时进行循环
	{
		if (*str1 != *str2)
			return *str1 - *str2;//返回的就是两个字符串中第一个不相同字符的码值的差
		str1++, str2++;
	}
	return 0;
}

int main()
{
	char str1[] = "abcd";
	char str2[] = "abcd";
	char str3[] = "abce";
	printf("%d %d", my_strcmp(str1, str2), my_strcmp(str1, str3));
	return 0;
}

7. strncpy 函数的使用

char * strncpy ( char * destination, const char * source, size_t num );

用法和 strcpy 基本一致,只是多了一个参数来控制复制多少元素过去。

  1. 拷贝num个字符从源字符串到目标空间。
  2. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个。
  3. 拷贝完成后,如果没有到源字符串的末尾,不会追加'\0'

测试:

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

int main()
{
	char str1[50] = "abcdxxxxxxxxxxxxxxxxxxxxxxxx";
	char str2[] = "efghxxxxx";
	strncpy(str1, str2, 13);
	printf("%s", str1);
	return 0;
}

我们通过调试观察 str1 的内容变化。
执行前
执行后

8. strncat 函数的使用

char * strncat ( char * destination, const char * source, size_t num );
  1. source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个'\0'字符。
  2. 如果source 指向的字符串的长度小于num的时候,只会将字符串中到'\0' 的内容追加到destination指向的字符串末尾

测试:

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20];
	char str2[20];
	strcpy(str1, "To be ");//将"To be"放进 str1 中
	strcpy(str2, "or not to be");

	strncat(str1, str2, 6);
	printf("%s\n", str1);
	return 0;
}

结果:
结果

9. strncmp 函数的使用

 int strncmp ( const char * str1, const char * str2, size_t num );

比较str1str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束。如果num个字符都相等,就是相等返回0.
返回值与 strcmp 一致。
返回值

10. strstr

10. 1 strstr 的使用

char * strstr ( const char * str1, const char * str2);
  1. 函数返回指向字符串str2在字符串str1第一次出现的位置的指针
  2. 字符串的比较匹配不包含'\0'字符,以'\0'作为结束标志)。

示例:

#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "This is a simple string";
	char* pch;
	pch = strstr(str, "simple");//找到 simple 第一次在 str 中出现的位置,pch指向 s.
	strncpy(pch, "sample", 6);//使用 strcpy 会有 '\0'
	printf("%s\n", str);
	return 0;
}

结果:
结果

10. 2 strstr 的模拟实现

大致思路:先在 str1 中遍历找和 str2 首元素相同的元素,找到了就将此时的 str1 复制出来,开始同时遍历两个字符串,判断两个字符串是否相同,如果相同,返回 str1 ,不相同就继续遍历。

#include <stdio.h>

char* my_strstr(const char* str1, const char* str2)
{
	while (*str1)
	{
		if (*str1 == *str2)
		{
			char* tmp1 = str1;
			char* tmp2 = str2;
			while (*tmp1 && *tmp2)
			{
				if (*tmp1 != *tmp2)
					break;
				tmp1++, tmp2++;
			}
			if (!*tmp2)//如果循环结束的时候 tmp2 是'\0',说明上面的循环走到底了,也就是找到了
				return str1;
		}
		str1++;
	}
	return NULL;
}

当然,上面那里在找到与 str2 首元素相同的元素之后,也可以使用 strcmp 函数进行比较。

11. strtok 函数的使用

strtok 函数用于分割字符串

char * strtok ( char * str, const char * sep);
  1. sep参数指向一个字符串,定义了用作分隔符的字符集合(标记)
  2. 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  3. strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。
  4. strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。
  5. strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  6. strtok函数的第一个参数为 NULL,函数将在上一次传递的字符串中被保存的位置开始,查找下一个标记。(也就是说, strtok 函数可以连续调用,连续调用时第一个参数传 NULL
  7. 如果字符串中不存在标记,则返回 NULL

示例:

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "192.168.6.111";
	char* sep = ".";
	char* str = NULL;
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	return 0;
}

结果:
结果

12. strerror函数的使用

char* strerror ( int errnum );

strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在 errno.h 这个头文件中说明的,C语言程序启动的时候就会使用一个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是 0 ,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回

举例:

#include <errno.h>
#include <string.h>
#include <stdio.h>
//我们打印一下0-10这些错误码对应的信息
int main()
{
	int i = 0;
	for (i = 0; i <= 10; i++) {
		printf("%s\n", strerror(i));
	}
	return 0;
}

在我使用的环境 win11+VS2022 下输出结果为:

No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
No such device or address
Arg list too long
Exec format error
Bad file descriptor
No child processes

使用举例:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");//尝试打开名为 unexist.ent 的文件,但是并没有找到,也就是打开失败了,那么 pfile 就是 NULL
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	return 0;
}

输出:
输出
也可以了解一下 perror 函数,perror函数相当于一次将上述代码中的第9行完成了,直接将错误信息
打印出来。perror 函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
 FILE * pFile;
 pFile = fopen ("unexist.ent","r");
 if (pFile == NULL)
 perror("Error opening file unexist.ent");
 return 0;
}

输出结果同上。

如果喜欢的话不妨顺手点个赞,收藏,评论,关注!
我会持续更新更多优质文章!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1953270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何获得某个Window画面所属包名packageName和用户userId

在安卓上获得某个Window画面所属包名packageName和用户userId的方法 1&#xff0c;用到的工具如下&#xff1a; adb androidSDK里的monitor工具 adb shell dumpsys window animator adb shell dumpsys window命令 jdk 1.8已在安卓14模拟器上测试通过。 以AOSP的launcher中的m…

Nacos适配达梦数据库并制作镜像

背景&#xff1a;因项目需要信创&#xff0c;需将原本的mysql数据库&#xff0c;改成达梦数据库 一、部署达梦数据库 1.1 部署达梦数据库服务 可参考&#xff1a;Docker安装达梦数据库_达梦数据库docker镜像-CSDN博客 1.2 创建nacos数据库 create user SAFE_NACOS identifi…

pythonGame-实现简单的贪食蛇游戏

通过python简单复现贪食蛇游戏。 使用到的库函数&#xff1a; import pygame import time import random 游戏源码&#xff1a; import pygame import time import randompygame.init()white (255, 255, 255) yellow (255, 255, 102) black (0, 0, 0) red (213, 50, 80…

【秋招突围】2024届秋招笔试-美团笔试题-第一套-三语言题解(Java/Cpp/Python)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新 美团 春秋招笔试题**汇总~ 👏 感谢大家的订阅➕ 和 喜欢💗 01.LYA 的音乐播放列表 问题描述 LYA 有一个包含 n n n 首歌曲的音乐播放列表,歌曲编号从 1 1

护网紧急情况应对指南:Linux 应急响应手册

继上一篇&#xff1a;护网紧急情况应对指南&#xff1a;Windows版v1.2全新升级版 之后 收到小伙伴后台要Linux应急手册&#xff0c;今天给大家安排上。 《Linux应急手册》是一本为Linux系统管理员和运维工程师量身打造的实用指南&#xff0c;旨在帮助他们快速应对各种突发状况…

电测量数据交换DLMSCOSEM组件第10部分:智能测量标准化框架

1.GB/T 17215.6XY系列 IEC 62056 DLMS/COSEM组件标准已经由IEC/TC 13完成制定,用于电测量的目的。有些标准——特别是COSEM数据模型——也已经被其他非电量测量的技术委员会使用。IEC62056-X-Y系列标准对应转换国标GB/T 17215.6XY系列(电测量数据交换DLMSCOSEM组件)。如下图…

Electron的入门介绍与使用(1)共30节

Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。 入门指南​ Electron 是网页应用 …

repo 工具安装和使用教程(windows+gitee)

repo是什么 官方的定义&#xff1a;Repo是谷歌用python脚本写的调用git的一个脚本&#xff0c;可以实现管理多个git库。 Android的源代码使用Repo 命令行工具来管理多个git仓库&#xff0c;大概有百多个。要想克隆和管理百多个 Git 仓库&#xff0c;不是一件简单的事情。Repo 命…

Java集合——HashMap的底层实现

HashMap将数据以键值对的形式存储&#xff0c;是线程不安全的&#xff08;即在多线程中若不用concurrentHashMap会导致结果错误&#xff09;。 // concurrentHashMap编程示例 import java.util.Map; import java.util.concurrent.ConcurrentHashMap;public class HashMapThrea…

快手电商Android一面凉经(2024)

快手电商Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《快手电商Android一面凉经(2024)》。 面试职位: Android工程师 技术一面 面试形式…

新手小白的pytorch学习第十四弹------十一、十二、十三弹卷积神经网络CNN的习题

习题编号目录 No 1No 2No 3No 4No 5No 6No 7No 8No 9No 10No 11No 12No 13 练习题主要就是 写代码&#xff0c;所以这篇文章大部分是代码哟~ No 1 What are 3 areas in industry where computer vision is currently being used? No 2 工业异常检测&#xff0c;目标检测 Sea…

C语言 -- sizeof和strlen的用法

C语言 -- sizeof和strlen的用法 1. sizeof和strlen的对比​1.1 sizeof​1.2 strlen​1.3 sizeof 和 strlen的对比​ 2. 数组和指针笔试题解析2.1 一维数组​2.2 字符数组​2.3 二维数组 3. 指针运算笔试题解析3.1 题目1&#xff1a;3.2 题目2​3.3 题目3​3.4 题目4​3.5 题目5…

力扣高频SQL 50题(基础版)第十九题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十九题1211. 查询结果的质量和占比题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十九题 1211. 查询结果的质量和占比 题目说明 Queries 表&#xff1a; …

学习c语言第十五天(初阶测评)

选择题 1.下列程序输出结果为 67 2.下列程序输出结果为 死循环打印 3.i 和j的值分别为什么 2 1 4.k的终值是什么 90 5.输出结果是什么 1 6.正确的是 C 7. C语言规定main函数位置 C 8.不正确的是 D 9.正确的是 c 库函数独立c语言之外 10…

comsol在静水压力下的地应力平衡

comsol在静水压力下的地应力平衡 条件: 水深3m,粗砂层厚度10m,模型长度100m土体的饱和密度1950kg/m3水的密度1000kg/m3浮重度 γ ′ = γ s a t − γ w \gamma=\gamma_{sat}-\gamma_w γ

GD32 PWM输入捕获

前言 通过本次的学习&#xff0c;了解定时器的分类和配置&#xff0c;高级定时器&#xff0c;通用定时器等不同等级的定时器拥有的功能&#xff0c;学习定时器的输入捕获与输出比较功能&#xff08;PWM&#xff09;脉冲宽度调制&#xff0c;来控制电机等外设。 定义 定时器的分…

Pytorch学习笔记——在GPU上进行训练

文章目录 1. 环境准备2. 导入必要库3. 加载数据集4. 定义简单的神经网络模型5. 检查和设置GPU设备6. 定义损失函数和优化器7. 训练模型8. 全部代码展示及运行结果 1. 环境准备 首先&#xff0c;确保PyTorch已经安装&#xff0c;且CUDA&#xff08;NVIDIA的并行计算平台和编程模…

go-kratos 学习笔记(6) 数据库gorm使用

数据库是项目的核心&#xff0c;数据库的链接数据是data层的操作&#xff0c;选择了比较简单好用的gorm作为数据库的工具&#xff1b;之前是PHP开发&#xff0c;各种框架都是orm的操作&#xff1b;gorm还是很相似的&#xff0c;使用起来比较顺手 go-kratos官网的实例是ent&…

鸿蒙UI系统组件10——菜单(Menu)

果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下面名片&#xff0c;关注公众号。 Menu是菜单接口&#xff0c;一般用于鼠标右键弹窗、点击弹窗等。 1、创建默认样式的菜单 菜单需要调用bindMenu接口来实现。bindMenu响应绑定组件的点击事件&am…

ModuleNotFoundError: No module named ‘py3langid‘ 以及如何将包安在虚拟环境下

前提&#xff1a;已经安装过改包&#xff08;pip install py3langid&#xff09;&#xff0c;但仍报错 原因&#xff1a;安装在其他目录下了 解决办法&#xff1a; 1、再次在终端输入pip install py3langid 显示安装位置 Requirement already satisfied: py3langid in c:\…