一篇文章了解系统眼中的键盘--以一个简单的系统分析从按键的输入到字符的显示

键盘输入

实现使用的设备

intel架构32位CPU, 思路为嵌入式系统工程师,使用的操作系统是《30天自制操作系统》里面的系统进行讲解

硬件实现

按键

image-20231014094206723

使用单片机等的引脚可以获取电平状态从而获得按键的状态(单片机是一种集成到一块硅片上构成的一个小而完善的微型计算机系统, 用在一些不需要很高的性能的地方, 比如遥控器, 手表等)

image-20231011232437371

image-20231011232350591

一般使用轮询的方式获取矩阵键盘上面的按键的状态

例: 在上方第一个端口通高电平同时检测右侧四个端口, 这时候如果某一个端口为低电平表示按键按下, 检测之后再在上侧端口2进行通电然后检测, 以此类推, 快速循环即可获得按键状态

单片机的速度很快, 所以你看来就会认为这些按键是同时检测的

hw2

实际的键盘原理图(稚晖君作品), 会加入一些其他的处理

如何让电脑识别为键盘

电脑识别按钮为键盘按键

  1. 获取到的信号会通过USB HID协议传输到电脑, 从调试数据分析USB通信协议——USB键盘鼠标【HID类设备】(四)_usb键盘协议-CSDN博客, 由于USB协议很复杂不过多讲解, 感兴趣同学自行了解(可以直接使用现成的键盘驱动板)
  2. 电脑采集信号后使用软件进行转化, 例如通过串口发送一个数据, 电脑上启动一个程序, 接收到这个数据以后就会转化为对应的按键
import pyautogui
if(按键按下)# 模拟按下'A'键pyautogui.press('a')# 模拟按下组合键Ctrl+Cpyautogui.hotkey('ctrl', 'c')
  1. Linux在设备树上面添加节点时候进行设置(在操作系统中进行专门设置)

注: 以下解释的是使用USB进行通讯的方式

软件

一般来说电脑的硬件会自带一些处理的设备, 我们不需要关心底层的时序, 只需要对处理过的信息进行采集就行了

不同情况下获取按键的状态的方式

  • 方式1使用BIOS(16位模式), 在电脑刚上电的时候没有加载操作系统时候使用, 类似于C语言函数的调用
; 相当于int get_status(int data);
MOV		AH,0x02			;设置参数, 把参数保存在AH寄存器里面
INT		0x16 			; 调用BIOS函数获取状态,使用的是16号函数
MOV		[LEDS],AL		; 进行存储, 返回的信息保存在AL寄存器里面

这里的AL, AH等是Intel架构下面的寄存器, 是一块可以和CPU的速度相匹配的内存, 用于在计算机计算的时候暂时保存数据

这一个调用主要用于获取键盘的指示灯的状态(大小写切换等)

BIOS: 可以理解为一个固定在主板上面的程序, 帮助初始化一些内容, 在电脑刚上电的时候使用的是16位的模式, 需要在初始化一些内容以后切换为32位模式, 这样做的主要目的是兼容之前的CPU

在示例使用的系统中此时候读取的信息会被记录, 用于之后的按键输入判断

  • 其他时候获取按键的按下情况(32位), 使用汇编指令读取对应的端口获取信息
_io_in8:	; int io_in8(int port);实际调用的时候相当于这一个C语言代码调用;传入读取的端口, 返回读取到的值MOV		EDX,[ESP+4]		; port, 从栈中获取传进来的参数MOV		EAX,0			;对于另一个参数进行设置IN		AL,DX			;使用IN汇编指令对对应的端口进行读取, EAX会保存返回值RET_io_out8:	; void io_out8(int port, int data);MOV		EDX,[ESP+4]		; portMOV		AL,[ESP+8]		; dataOUT		DX,AL			; 向DX保存的端口写入AL的数值RET

使用的两个外设端口读取的函数, 一般会在中断中进行调用, 检测是哪一个按键按下

中断
image-20231024231022592

中断发生后, 原先的程序执行会被打断, 之后CPU会检查发生的是哪一个中断, 从中断向量表中获取到对应的处理函数, 跳转到对应的函数进行执行

中断向量表: 可以理解一个记录有各个中断处理函数的数组, CPU会根据发生的中断的编号到对应的位置进行查找对应的处理函数

Intel使用的中断处理使用的是PIC(这里不进行过多解释)一文读懂多架构的中断控制器 - 知乎 (zhihu.com)

_asm_inthandler21:PUSH	ESPUSH	DSPUSHADMOV		EAX,ESPPUSH	EAX				;对使用的寄存器保存数据, 防止中断返回的时候程序无法正常运行MOV		AX,SS			MOV		DS,AX			MOV		ES,AX			;准备C语言需要的环境(这几个段寄存的数值要求一样)CALL	_inthandler21	; 调用实际的处理函数POP		EAX				;复原使用的寄存器POPADPOP		DSPOP		ESIRETD

中断发生后会自动运行这一个函数(地址保存在中断向量表里面), 我们需要在里面进行中断事件的处理

首先需要将之前程序运行的状态保存在栈里面, 之后调用对键盘数据的读取函数

这里使用的PUSH以及POP指令是对于栈的操作

(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。使用这一种结构对数据进行保存, 在调用其他函数之间会将数据入栈, 返回以后出栈

image-20231024230846603
void inthandler21(int *esp)
{int data;io_out8(PIC0_OCW2, 0x61);	/* IRQ-01接收完毕的通知 */data = io_in8(PORT_KEYDAT);	//向键盘端口发送一个信号fifo32_put(keyfifo, data + keydata0);	//从键盘的端口获取一个键盘的数据,之后保存起来return;
}

实际的中断处理函数

多按键先后输入处理

由于可能在短时间内有多个按键进行输入, 事件短于处理函数, 所以使用FIFO(First in First out)的数据结构对传入的数据进行存储(实际的操作系统中用来进行多个事件先后的处理)

/* fifo.c */
struct FIFO8 {unsigned char *buf;		//一个数组, 实际数据存储的位置int p, q, size, free, flags;	//记录读指针, 写指针以及总大小, 剩余空间, 是否初始化的标志 
};

实际的C语言实现

image-20231026123344012 image-20231026123401203

image-20231019104538913

具体实现的代码(仅参考不进行讲解)
/* FIFO实现 */#include "bootpack.h"#define FLAGS_OVERRUN		0x0001void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
/* FIFO的初始化 */
{fifo->size = size;fifo->buf = buf;fifo->free = size; /* 为空 */fifo->flags = 0;fifo->p = 0; /* 写指针记录初为始位置 */fifo->q = 0; /* 读指针记录为初始位置 */return;
}int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* FIFO写入一个数据 */
{if (fifo->free == 0) {/* 没有位置进行写入了 */fifo->flags |= FLAGS_OVERRUN;return -1;}fifo->buf[fifo->p] = data;fifo->p++;if (fifo->p == fifo->size) {fifo->p = 0;}fifo->free--;return 0;
}int fifo8_get(struct FIFO8 *fifo)
/* FIFO获取一个数据 */
{int data;if (fifo->free == fifo->size) {/* 没有可以获取的数据 */return -1;}data = fifo->buf[fifo->q];fifo->q++;if (fifo->q == fifo->size) {fifo->q = 0;}fifo->free++;return data;
}int fifo8_status(struct FIFO8 *fifo)
/* 获取当前没有读取的数量数据 */
{return fifo->size - fifo->free;
}

信号的处理

  • 在中断中获取信号
  • 对信号进行解析

使用按键数值表进行获取实际按下的按键

IMG_20231012_091035

系统需要做的就是回上面的信息进行处理

static char keytable0[0x80] = {
0,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0,   0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0,   0,   'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', 0,   0,   ']', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', ',', '.', '/', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0x5c, 0,  0,   0,   0,   0,   0,   0,   0,   0,   0x5c, 0,  0
};
static char keytable1[0x80] = {
0,   0,   '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', 0,   0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', 0,   0,   'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', '+', '*', 0,   0,   '}', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   '_', 0,   0,   0,   0,   0,   0,   0,   0,   0,   '|', 0,   0
};

使用两个数组对这些数据进行判断, 有两组的原因是, 在Shift和Capslock状态不同的输入的数据是不同的

  • 处理
  1. 对于普通的按键获取到实际的按键数值, 之后系统进行处理或者传递给应用
  2. 对于切换状态的按键, 对之前保存的状态进行切换, 在之后的输入的时候对事件进行处理

实际按键的处理函数(修改版)

//key_shift等是全局变量, 用来保存一些按键是不是按下了
i = fifo8_get($Key_FIFO);		//获取一个按键
if (i < 0x80) { /* 判断输入的是什么 */if (key_shift == 0) {//判断shift按键的状态, 进而获取实际输入的字符//获取字母以及数字s[0] = keytable0[i];} else {//获取字母以及标点符号s[0] = keytable1[i];}
} else {s[0] = 0;
}
if ('A' <= s[0] && s[0] <= 'Z') {	/* 分析输入的文字 */if (((key_leds & 4) == 0 && key_shift == 0) ||((key_leds & 4) != 0 && key_shift != 0)) {s[0] += 0x20;	/* 大小写字母的换算 */}
}
if (s[0] != 0 && key_win != 0) { /* 通常文字、显示文字的窗口存在、Enter *///对于任务存在时候发送数据到任务...
}
if (i == 0x0f && key_win != 0) {	/* Tab *///某个按键有特殊作用时候单独处理...
}
//状态类的按键的处理
if (i == 0x2a) {	/* 左侧Shift ON */key_shift |= 1;
}
....if (i == 0x45) {	/* NumLock */...
}
//组合键的处理
if (i == 0x3b && key_shift != 0 && key_win != 0) {	/* Shift+F1 */...
}

文字的显示

  • 字符编码

ASCII只有255个字符并且只能显示英文, 中文也有自己的字符编码

补充常用的中文字符集

GB2312: 兼容ASCII前127位, 两个大于127的数字连用表示一个汉字, 一共有八千多个符号, 包含6763个汉字, ASCII之中的的有的也是用两个字节进行编码, 叫做全角, 一般使用半角, 一般使用0xa1开始表示

GBK: 在GB2312的基础上增加了14240个新的汉字, 由于原来的格式原来的格式已经存放不下, 所以要求第一个编码大于127就可以了

GB18030: 在GBK上面进一步扩展, 第二个字节未使用的0x30-0x39编码表示扩充为四个字节, 兼容前几个, 只要增加了中日韩统一汉字编码扩充

Unicode: 各个国家制作的统一的字符集, 兼容ASCII, 还有一些表情emoji, 只是进行编号, 没有进行编码, 就是只是用一个数字代表一个字符, 但是没有使用具体的电脑解析的规范

utf-32: 编码和编号一样, 每一个字符使用4个字节进行表示, 前面的0不能省略, 会导致ASCII等编码变长

utf-16: 使用两个或者四个字节进行编写, 由于发现Unicode没有使用0xD800到0xDBFF所以利用这片空间表示映射 , 使用这一空间达到变长的目的

utf-8: 网页上使用的比较多, 也是一种变长的编码格式, 第一位设置为0, 就可以兼容ASCII的0-127, 其他的字符, 两个字节的时候第一个字节使用110开头, 第二个字节使用10开头, 三个字节的时候会使用1110开头, 之后是使用10开头, 以此类推, 将Unicode编码填入空位, 从最后一个字节开始填写, 不够的在前面加0

具体获取你输入的是哪一个字符是输入软件的工作

  • 字模数据

只有数字编码你只知道这是某一个汉字, 但是这个字是怎么显示的呢?

实际上字符可以是一个图形, 可以保存在内存中, 记录了每一个像素的显示

使用取模软件获取一个字符的字模

image-20230713154917469

使用这个软件进行生成,

GB2312: Addr = (((CodeH - 0xA0-1)*94)+(CodeL - 0xA0 -1))*16*16/8

(0)
{0x09,0x00,0x08,0x80,0x1F,0xFC,0x10,0x80,0x30,0x80,0x5F,0xF8,0x90,0x80,0x10,0x80,0x1F,0xF8,0x10,0x80,0x10,0x80,0x1F,0xFC},
{0x10,0x00,0x48,0x88,0x44,0x44,0x84,0x44},/*"焦",0*/
image-20231014094421615

0x09 => 0000 1001 0x00 => 0000 0000

详细讲解: 【单片机】野火STM32F103教学视频 (配套霸道/指南者/MINI)【全】(刘火良老师出品) (无字幕)-虚神疯-稍后再看-哔哩哔哩视频 (bilibili.com)

编码和字模数据的使用关系, 一般会可以按照字符编码的顺序进行保存字模数据, 在之后进行显示的时候按照字符编码对对应的字模数据进行寻找, 可以使用这个软件进行生成

image-20231026090753019

**注: **之后使用的时候可以按照字符编码进行一些运算以后在字模表里面寻找对应的字模

  • 示例GB2312的解析

实际使用的编码位是0xA1A1-FEFE, 汉字的区域是B0A1-F7FE, 原因是为了兼容ASCII, 最高位为1, 并且预留0x20个控制位

GB2312编码对收录的字符进行分区, 分为94个区, 每一个区有94个位, 一共有8836个码位

第16个区开始是汉字, 0-9是682个汉字以外的字符

10-15是空白区域没有使用

16-55是一级汉字, 按照拼音进行排序

56-87区收录3008个二级汉字, 按照部首进行排序

88-94是空白区没有使用

实际使用的时候就是: 区码加位码+A0A0, 比如’‘啊’'是16区第一个, 就是0x1001+0xA0A0

实际在使用字模库的时候由于没有进行兼容偏移, 所以使用的是字符的在总个数的排序

Addr = (((ColdH - 0xA0 - 1)* 94) + (CodeL - 0xA0-1))*32*32/8

Addr 地址偏移

CodeH区码

CodeL位码

32: 字模的长和宽

  • 显存

一块用于记录屏幕信息的内存, 直接堆内存进行读写就可以控制一块屏幕的显示

此模式下使用8位表示一个像素的颜色

由于8只能表示255种颜色=>使用色盘(一个保存颜色的数组, 比如0位置保存红色, 我向显存写入0之后会先从色盘获取红色, 然后把红色显示在对应的像素上面)

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{//参数1: 显存的位置//参数2: 屏幕的宽度//参数3,4: 显示的位置//参数5: 显示的颜色//参数6: 显示的字的字模int i;char *p, d /* data */;for (i = 0; i < 16; i++) {p = vram + (y + i) * xsize + x;//计算某一行数据的实际对应的显存位置d = font[i];	//获取某一行的字模if ((d & 0x80) != 0) { p[0] = c; }	//按照字模数据依次写入对应的显存if ((d & 0x40) != 0) { p[1] = c; }if ((d & 0x20) != 0) { p[2] = c; }if ((d & 0x10) != 0) { p[3] = c; }if ((d & 0x08) != 0) { p[4] = c; }if ((d & 0x04) != 0) { p[5] = c; }if ((d & 0x02) != 0) { p[6] = c; }if ((d & 0x01) != 0) { p[7] = c; }}return;
}

通过循环获取字模的每一个位, 之后通过这一个位的保存的数据进行判断对显存写入的颜色

显示总结(官方术语)

外码=>内码=> 交换码=>字形码

外码: 输入码, 平常键盘拼音输入的字母, 是一个索引码

内码: 计算机存储的二进制, 输入的汉字要转化为二进制形式, 集合叫做字符集

交换码: 国标码, 和别的计算机交换信息的时候使用的编码

字形码: 汉字字模, 二进制转化为可视化的图形, 集合叫做字库

实际效果演示

image-20231026100316991

上面使用的操作系统没有保存照片, 使用一个我写的嵌入式操作系统展示字符显示的结果(由上述操作系统移植过来的ARM架构下的图形化操作系统)

XuSenfeng/jiaoOS: 一个基于stm32f103的图形化操作系统 (github.com)

总结

  • 读取按键是否按下
  • 将按键的信号传递给电脑
  • 电脑对信号进行处理获得输入的数据
  • 将信号显示在屏幕上

版权补充

引用资料:

  1. peng-zhihui/HelloWord-Keyboard (github.com)稚晖君翰文键盘部分图纸
  2. 《30天自制操作系统》部分代码
  3. 自制17键数字机械键盘(2)——电路原理图及PCB设计、元器件焊接 - 知乎 (zhihu.com)
  4. XuSenfeng (github.com)我自己的笔记
  5. 野火stm32相关教程

其他资料

Expanded Main Page - OSDev Wiki

intel编程手册

XuSenfeng (github.com)

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

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

相关文章

分享一个WPF项目

最近在学习WPF开发方式&#xff0c;找到一些项目进行拆解学习&#xff1b;本位主要分享一个WPF项目&#xff0c;叫做WPFDevelopers&#xff0c;在git上大约有1.3K星&#xff0c;话不多说&#xff0c;先看看效果&#xff1a; 这个项目开发可以编译启动后直接查看样例、Xaml、Cha…

《计算机网络简易速速上手小册》第9章:物联网(IoT)与网络技术(2024 最新版)

文章目录 9.1 IoT 架构与通信协议 - 打造智能世界的秘诀9.1.1 基础知识9.1.2 重点案例&#xff1a;使用 Python 和 MQTT 实现智能家居照明系统准备工作Python 脚本示例发布者&#xff08;灯光控制&#xff09;订阅者&#xff08;灯光状态接收&#xff09;&#xff1a; 9.1.3 拓…

快速Diff算法-Vue3

快速Diff算法 快速 Diff 算法在实测中性能最优。它借鉴了文本 Diff 算法中的预处理思路&#xff0c;先处理新旧两组子节点中相同的前置节点和相同的后置节点。当前置节点和后置节点全部处理完毕后&#xff0c;如果无法简单地通过挂载新节点或者卸载已经不存在的节点来完成更新…

AD24-Class、飞线、PCB Nets的管理及添加、层的管理

一、Class 1、Class介绍 2、Class添加与显示 ①添加 ②显示通过Panels-PCB&#xff0c;即可将创建的类显示再左上方窗口 3、Class的编辑管理 ①概述 ②颜色更改 二、飞线 1、概述 2、 飞线的打开、关闭 打开&#xff1a;Alt左上角滑动 N&#xff1a;可以针对性的显示和隐…

深度学习环境配置:Anaconda 安装和 pip 源

conda是一种通用包管理系统&#xff0c;与pip的使用类似&#xff0c;环境管理则允许用户方便地安装不同版本的python并可以快速切换。 Anaconda则是一个打包的集合&#xff0c;里面预装好了conda、某个版本的python、众多packages、科学计算工具等等&#xff0c;就是把很多常用…

Vue3 - 从 vue2 到 vue3 过渡,这一套就够了(案例 + 效果演示)(二)

目录 一、组合式 API 的使用 1.1、watch 函数 1.2、watchEffect 函数 1.3、toRef 和 toRefs 1.3.1、toRef 1.3.2、toRefs 1.4、vue3 的声明周期 一、组合式 API 的使用 1.1、watch 函数 与 vue2.x 中的 watch 配置功能一致&#xff0c;但是多了一些坑&#xff1a; 这…

【蓝桥杯】环形链表的约瑟夫问题

目录 题目描述&#xff1a; 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解法一&#xff08;C&#xff09;&#xff1a; 解法二&#xff08;Cpp&#xff09;&#xff1a; 正文开始&#xff1a; 题目描述&#xff1a; 据说著名犹太历史学家 Josephus 有过以下故事&a…

Pytest测试用例参数化

pytest.mark.parametrize(参数名1,参数名2...参数n, [(参数名1_data1,参数名2_data1...参数名n_data1),(参数名1_data2,参数名2_data2...参数名n_data2)]) 场景&#xff1a; 定义一个登录函数test_login,传入参数为name,password&#xff0c;需要用多个账号去测试登录功能 # …

JAVA方法引用:

方法引用的出现原因在使用Lambda表达式的时候&#xff0c;我们实际上传递进去的代码就是一种解决方案&#xff1a;拿参数做操作那么考虑一种情况&#xff1a;如果我们在Lambda中所指定的操作方案&#xff0c;已经有地方存在相同方案&#xff0c;那是否还有必要再写重复逻辑呢&a…

Java SWT Composite 绘画

Java SWT Composite 绘画 1 Java SWT2 Java 图形框架 AWT、Swing、SWT、JavaFX2.1 Java AWT (Abstract Window Toolkit)2.2 Java Swing2.3 Java SWT (Standard Widget Toolkit)2.4 Java JavaFX 3 比较和总结 1 Java SWT Java SWT&#xff08;Standard Widget Toolkit&#xff…

02 使用jdk运行第一个java程序:HelloWorld

使用jdk运行第一个java程序 1 HelloWorld小案例1.1 编写流程1.2 错误示例 首先在CMD命令行里面&#xff0c;使用javac xxxx.java&#xff0c; 进行编译&#xff0c;其中会有报错&#xff1b; 然后生成xxxx.class 文件&#xff0c;然后使用java xxxx.class 进行运行。 1 HelloWo…

瑞_23种设计模式_抽象工厂模式

文章目录 1 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;1.1 概念1.2 介绍1.3 小结1.4 结构 2 案例一2.1 案例需求2.2 代码实现 3 案例二3.1 需求3.2 实现 4 总结4.1 抽象工厂模式优缺点4.2 抽象工厂模式使用场景4.3 抽象工厂模式VS工厂方法模式4.4 抽象工厂…

node.js基础--01

Author nodes&#xff1a;&#xff08;题记&#xff09; node.js is an open-source&#xff0c;cross-platform JAVAScript runtime environment。 node.js是一个开源&#xff0c;跨平台的js运行环境 common commands&#xff08;常用指令&#xff09; 1、C: enter hard …

javaScript的序列化与反序列化

render函数的基本实现 javaScript的序列化与反序列化 一&#xff0c;js中的序列化二&#xff0c;序列化三&#xff0c;反序列化四&#xff0c;总结 一&#xff0c;js中的序列化 js中序列化就是对象转换成json格式的字符串&#xff0c;使用JSON对象的stringify方法&#xff0c;…

新书速览|Python数据科学应用从入门到精通

系统教授数据科学与Python实战&#xff0c;涵盖线性回归、逻辑回归、决策树、随机森林、神经网 本书内容 随着数据存储、数据处理等大数据技术的快速发展&#xff0c;数据科学在各行各业得到广泛的应用。数据清洗、特征工程、数据可视化、数据挖掘与建模等已成为高校师生和职场…

STL——空间配置器

空间配置器是STL六大组件之一&#xff0c;它和其他五个组件相互配合&#xff0c;起着很关键的作用。 容器&#xff1a;各种数据结构、如vector、list、stack、deque、queue、set、map、unordered_map等等算法&#xff1a;各种算法&#xff0c;如sort、serach、copy、erase 提供…

C#用正则表达式验证格式:电话号码、密码、邮编、手机号码、身份证、指定的小数点后位数、有效月、有效日

正则表达式在程序设计中有着重要的位置&#xff0c;经常被用于处理字符串信息。 用Regex类的IsMatch方法&#xff0c;使用正则表达式可以验证电话号码是否合法。 一、涉及到的知识点 Regex类的IsMatch方法用于指示正则表达式使用pattern参数中指定的正则表达式是否在输入字符串…

计算机网络_1.6.2 计算机网络体系结构分层的必要性

1.6.2 计算机网络体系结构分层的必要性 一、五层原理体系结构每层各自主要解决什么问题1、物理层2、数据链路层3、网络层4、运输层5、应用层 二、总结三、练习 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 本节主要介绍实现计算机网络需要解决哪些问题&#xff1f;以…

这种学习单片机的顺序是否合理?

这种学习单片机的顺序是否合理&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01…

随着网络的快速发展,网络安全问题也日益凸显,遇到攻击该如何处理,如何抉择合适的防护方案

DexunCloud 经过研究发现当今世界&#xff0c;随着网络的快速发展&#xff0c;网络安全问题也日益凸显。其中&#xff0c;DDoS&#xff08;分布式拒绝服务&#xff09;攻击被认为是网络安全领域里最为严重的威胁之一。毫无疑问&#xff0c;DDoS攻击不仅可以导致网络服务中断&am…