【线程】锁池和等待池

锁池 概念: 假设线程A已经拥有了某个对象的(不是类)锁 ,而其他线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的所目前正被线程A拥有,所以这些线程就进入了该对象的锁池中,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到锁后会进入就绪队列进行等待cpu分配 等待池 概念: 简写一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了对象的等待池中,等待池中的线程不会去竞争同步锁,用过另外一个线程调用了相同对象的notifyAll(),那么处于该对象等待池中的线程就回全部进入该对象的所持中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify方法,那么仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池 参考文章: 膜拜!华为大牛透彻讲解Java面试100道必考题,不管你工作几年,都得看看!现在免费分享给大家!_哔哩哔哩_bilibili java中的锁池和等待池_emailed的专栏-CSDN博客_锁池和等待池

Hive如何删除、添加、修改表中的字段

1、添加字段: alter table table_name add columns (column_1 string,column_2 string) 添加之后字段由于hive底层是文件和系列化的设计因此查数据会发现新增的列在所有已有列的后面 2、修改字段 alter table table_name change column 已有列 修改名称 类型 comment ''; 3、删除列 Hive不能直接删除列,不然底层系列化就乱了,我们可以通过replace语句来替换整张表的字段,达到同样的效果 alter table table_name replace columns(column_1 string); 语句中只写想要保留的字段就可以

Spring Boot配置Swagger增强工具

参考knife4j官方网站:1.6 快速开始 | knife4j 一、话不多说,先看效果 1.Swagger页面这样,这里就不多做介绍 2.增强后的首页 3.API列表 4.API请求参数 5.API响应参数 个人觉得增强后看着很清晰,比原来的要好很多。 二、开始配置 1.pom.xml 配置 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.10.5</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>2.0.7</version> </dependency> 配置中遇到的问题: springfox-swagger2这个的版本号不同会导致无法导入@EnableSwagger2WebMvc这个注解 2.配置Swagger2Config import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; /** *@类名 Swagger2Config *@描述 配置Swagger2 *@版本 1.0 *@创建人 XuKang *@创建时间 2021/7/16 13:29 **/ @Configuration @EnableSwagger2WebMvc public class Swagger2Config { /** * @method createRestApi * @desc 配置SWAGGER参数 * @version V1.

解决 深度学习docker 端口连接被对方重设

在容器中启动 jupyter notebook 的时候,请指定 ip 为 0.0.0.0 aka: 原因 docker启动的时候,我们的命令是 -p 8888:8888 比如: sudo nvidia-docker run -it -p 8888:8888 --ipc=host -v /home/paul/code:/code --name paul-notebook my_tf:2.0 此时 使用 docker ps 来看,就是: 如你所见, 此时, 映射的 是 ::: 0.0.0.0 这个地址 这个地址实际上 和 127.0.0.1 不一样 所以,你启动 jupyter 的时候,请使用 ip 指定到 0.0.0.0 吧: jupyter notebook --ip 0.0.0.0 --allow-root

node 升级 PostCSS received undefined instead of CSS string

解决node 升级 node 升级 PostCSS received undefined instead of CSS string // 一般node 升级 css 预编译期都要重新更新 npm rebuild node-sass npm i 或者删除后重新 install 以 sass 预编译器为例,less 同理,这里就先暂时略过,本人主要使用sass预编译器

ARMv7架构学习

本文参考至ARM的官方手册 ARM® Architecture Reference Manual ARMv7-A and ARMv7-R edition 文章目录 0.ARM概念1.ARM的基本设定2.ARM处理器的工作模式1.有哪些模式2.设置这些模式的原因 3.ARM寄存器介绍1.通用寄存器2.SP/LR/PC特殊寄存器3.CPSR/SPSR寄存器 4.常用汇编指令1.指令和伪指令2.ARM汇编指令特点1.LDR/STR架构2.寻址方式3.条件后缀4.多级指令流水线 0.ARM概念 ARM(Advanced RISC Machines)是一种32位微处理器体系结构,当前被广泛应用于消费电子、无线通信、工业控制和手持设备等领域。 ARM公司是专门做RISC芯片设计开发的公司,作为知识产权供应商,ARM本身并不直接从事芯片生产,而是转让设计许可,由合作公司生产各具特色的芯片。 1.ARM的基本设定 ARMv7 采用的是32位架构 Byte: 8 bitsHalfword: 16bits(2 byte)Word: 32 bits(4 byte) ARM core提供: ARM指令集(32 bits)Thumb 指令集(16bits),thumb指令集是由于原来内存很贵,为了节省memory就用16bits方式来开发,现在已经基本不这么用了Thumb2 指令集(16&32bits),M系列的可能还有用thumb2模式来开发的,还是为了节约成本 2.ARM处理器的工作模式 1.有哪些模式 在官方手册上看到,有这么一些模式,但实际上有些模式是在特定情况下才能存在的,比如Monitor、Hyp模式,我们这里只选择基本工作模式来分析: 同时我们能看到有一栏是权限栏(privilege level),这些PL0、PL1、PL2的定义是: 所以我们可以总结下ARM的工作模式 2.设置这些模式的原因 设置这些模式的原因还是为了能够适配软件操作系统,因为软件操作系统提出了 userspace和kernelspace,因此硬件的设计考虑了软件的需要。 3.ARM寄存器介绍 ARMv7一共有37个寄存器,如下图所示: user模式下一共有r0~r15加上cpsr,17个寄存器,FIQ模式下灰色部分是自己的,即r8 ~ r14和spsr寄存器是自己的,其他的是usermode的,以此类推; 1.通用寄存器 r0 ~ r12是通用寄存器,就是计算的时候会用到,没有特殊的含义 2.SP/LR/PC特殊寄存器 SP是堆栈指针寄存器,用于保护现场,存储上下文的信息 LR是返回寄存器,存储一些返回地址的信息 PC是程序运行计数器,往pc里写值,将会导致跳转到该地址 3.CPSR/SPSR寄存器 CPSR是当前程序的状态寄存器,里面存储着很多信息,包括当前的指令模式(是thumb还是arm),以及当前的处理器模式(user/fiq/…) SPSR全程是 saved program status reg,它的目的就是作为CPSR的一份备份,那备份这个有什么用呢,文档里说了就是为了一旦异常发生,能够用到这个备份的值 4.常用汇编指令 1.指令和伪指令 指令:CPU机器指令的助记符,经过编译之后会得到机器码,CPU通过读取这些机器码然后执行相应的动作;伪指令:本质上并不是指令,只是指导编译的过程,最终也不会生成机器码; 所以两者的差异在于是否是指令,以及最后编译完成是否会生成机器码; 2.ARM汇编指令特点 1.

查找算法总结

二分查找 1. 适用场景 适用于对排好序的序列的指定值进行查找 2. 复杂度 O(logN) 3. 代码 /*cpp*/ int binarySearch(int[] a, int key) { int a_len = sizeof(a)/sizeof(a[0]); int lo = 0, hi = a_len - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (key < a[mid]) hi = mid - 1; else if (key > a[mid]) lo = mid + 1; else return mid; } return -1; } 快速查找 1.

方法练习2

1. 用户输入一个数,求对应数的阶乘(考虑数据延展性) /* 1. 求15的阶乘 数据在运行过程中,演变过程,以及数据当前情况和储存数据类型是否能支持 15的阶乘 int 类型结果显然不对 要考虑数据的演变过程 尤其是数据在整个变化过程中,增长性极大 */ /* 封装方法小技巧 1. 一框 找核心代码 2. 二看 上看找参数 找出核心代码块中未定义的变量,在核心代码之外定义,该变量就是方法参数 下看找返回 直接考虑删除核心代码,如果代码可以正常运行,正常编译,当前方法没有返回值,如果 已经不具备正常运行,正常编译情况,当前方法有返回值,注意必要数据的类型 3. 三封装 完成方法声明,剪切核心代码到方法体中,同时根据所需,补充 return 语句。如果没有返回值 无需补充。 正常使用,调用操作 方法分析: 参数: (int num) 返回值类型: double 方法名: factorial 方法声明: public static double factorial(int num) */ class Demo1 { public static void main(String[] args) { int num = 10; double ret = factorial(num); System.out.println(ret); } /** * 计算用户指定数据的阶乘结果 * * @param num 用户指定阶乘所需数据, int 类型 * @return 考虑数据的延展性和后期的拓展情况,返回值类型选择 double 类型 */ public static double factorial (int num) { double ret = 1; for (int i = 1; i <= num; i++) { ret *= i; } return ret; } } 2.

R语言入门(3)-内置数据集

1.数据集使用指南、查看内置数据集包括哪些数据、通过直接输入名称进行数据集的引用 > help(package="datasets") > data() > AirPassengers 2.在命名时尽量不要与内置数据集内的数据名称相同,否则输入的数据可能会被置换 > rivers <- c(1,2,3) > rivers [1] 1 2 3 > data("rivers") > rivers [1] 735 320 325 392 524 450 1459 135 465 600 330 336 280 315 870 [16] 906 202 329 290 1000 600 505 1450 840 1243 890 350 407 286 280 [31] 525 720 390 250 327 230 265 850 210 630 260 230 360 730 600 [46] 306 390 420 291 710 340 217 281 352 259 250 470 680 570 350 [61] 300 560 900 625 332 2348 1171 3710 2315 2533 780 280 410 460 260 [76] 255 431 350 760 618 338 981 1306 500 696 605 250 411 1054 735 [91] 233 435 490 310 460 383 375 1270 545 445 1885 380 300 380 377 [106] 425 276 210 800 420 350 360 538 1100 1205 314 237 610 360 540 [121] 1038 424 310 300 444 301 268 620 215 652 900 525 246 360 529 [136] 500 720 270 430 671 1770 3.

方法练习1

1. //封装一个方法,返回两个整数里的较大那个 public static int maxOfNumber(int num1, int num2) { } 2. //封装一个方法,返回两个浮点数里的较小那个 public static double minOfNumber(double num1, double num2) { } 3. //封装一个方法,来判断一个字符是否是大写字母,如果是,返回true,否则返回false public static boolean isMyUpper(char c) { } class FangFa123 { public static void main(String[] args) { int m = maxOfNumber(5,10); System.out.println("较大的是:" + m); double n = minOfNumber(3.14,6.18); System.out.println("较小的是:" + n); boolean ret = isMyUpper('a'); System.out.println("大写字母判断:" + ret); } /** * 返回两个整数较大的那个 * * @param num1 用户提供的第一个 int 类型数据 * @param num2 用户提供的第二个 int 类型数据 * @return int 比较参数两个 int 类型数据的大小关系,返回较大值 */ public static int maxOfNumber(int num1, int num2) { /* 尽量减少return 的使用 return 关键字可以终止方法的运行,如果一个方法中出现多次return 有可能会导致当前代码的阅读性较差,影响结果判断 if (num1 > num2) { return num1; } else { return num2; } */ /* 参照物思想 int max = num1; if (max < num2) { max = num2; } return max; */ /* 三目运算符(简配版的if else 语句) 条件 ? 结果1 :结果2 条件为 true 整个表达式结果为结果1 条件为 false 整个表达式结果为结果2 */ return num1 > num2 ?

MyBatis-Plus——超详细讲解配置文件

📢📢📢📣📣📣 哈喽!大家好,我是【一心同学】,一位上进心十足的【Java领域博主】!😜😜😜 ✨【一心同学】的写作风格:喜欢用【通俗易懂】的文笔去讲解每一个知识点,而不喜欢用【高大上】的官方陈述。 ✨【一心同学】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。 ✨如果有对【后端技术】感兴趣的【小可爱】,欢迎关注【一心同学】💞💞💞 ❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️ 目录 前言 1、mapperLocations 2、typeAliasesPackage 3、typeHandlersPackage 4、typeEnumsPackage 5、checkConfigLocation 6、executorType 7、configurationProperties 8、configuration 8.1 mapUnderscoreToCamelCase 8.2 defaultEnumTypeHandler 8.3 aggressiveLazyLoading 8.4 autoMappingBehavior 8.5 autoMappingUnknownColumnBehavior 8.6 localCacheScope 8.7 cacheEnabled 8.8 callSettersOnNulls 8.9 configurationFactory 9、MyBatis的配置属性 10、globalConfig 10.1 banner 10.2 enableSqlRunner 10.3 superMapperClass 10.4 dbConfig 10.4.1 idType 10.4.2 tablePrefix 10.4.3 schema 10.4.4 columnFormat 10.4.5 propertyFormat 10.4.6 tableUnderline 10.4.6 capitalMode 10.4.7 logicDeleteField 10.4.8 logicDeleteValue 10.4.9 logicNotDeleteValue 10.4.10 insertStrategy 10.4.11 updateStrategy 10.4.12 selectStrategy

分支和循环练习题

1. 判断用户输入的字符是不是英文字符,如果是输出yes,不是 输出no /* 分析: 1.用户输入的是字符类型,准备字符类型变量 char ch1 2.输入操作 需要使用 Scanner 来完成 Scanner sc = new Scanner(System.in); 3. if - else 分支结构 */ import java.util.Scanner; //导包,使代码具有使用 Scanner 的能力 class Work1 { public static void main(String[] args) { //创建 Scanner 变量 Scanner sc = new Scanner(System.in); //定义一个 char 类型变量,用于储存用户输入的数据 char ch = '\0'; //提示用户输入 System.out.println("请输入字符:"); ch = sc.nextLine().charAt(0); // if - else 结构判读数据情况 if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') { System.

C语言实现去除字符串中重复的字符

程序: #include<stdio.h> #include<string.h> void handle(char *p); int main(void){ char p[100]; scanf("%s",p); del(p); printf("%s\n",p); } //方法1:找到重复的字符后将其后面的字符全部向前移一位,删除该重复的字符 void del(char *p){ int len=strlen(p); for(int i=1;i<len;i++) for(int j=0;j<i;j++) if(p[i]==p[j]){ for(int k=i;k<len;k++) p[k]=p[k+1]; len--; //提高代码执行效率,降低时间复杂度 i--; //不加这一句的话,会跳着比较查找,漏掉一些字符 } } //方法2:让每一个字符i和它后面的字符j比较,如果不一样就保存字符j void del_1(char *p){ int len=strlen(p); for(int i=0;i<len;i++){ int k=i+1; for(int j=i+1;j<len;j++){ if(p[j]!=p[i]) p[k++]=p[j]; } p[k]='\0'; //添加字符串结束标志 } }```

uniCloud初体验

本文章主要分为3个部分的内容,从而从3个角度了解uniCloud的简单应用 云函数实现hello world云数据库实现联系人demoschema2code快速产出进阶联系人demo 1、云函数实现hello world 创建云服务-创建服务空间uniCloud提供30天免费的云服务专属服务器,在到期之前可在该服务器上创建服务空间列表,只需要注册Dcloud账号并且进行开发者认证即可申领。创建好云服务空间后,下一步就可以创建本地项目,选择默认模板,并且勾选中启用uniCloud,选择对应申请的服务器的运营商,点击创建即可 创建好项目后,右键uniCloud文件夹,选择"关联云服务空间或项目",勾选中刚刚创建的云服务空间,点击确定 右键cloudfunctions,新建云函数,在新建的云函数中写代码,然后右键helloWorld文件夹上传部署云函数,上传成功后可以在云函数列表中看到已上传的云函数 在页面上调用云函数,代码如下: /pages/index/index.vue uniCloud.callFunction({ name: "helloWorld", success: (e) => { this.title = e.result } }) 点击预览,可以看到函数的调用情况 为了验证云函数是否生效,可以在本地修改函数中返回的字符串,保存后上传部署,刷新页面为新返回的内容,证明云函数调用成功 2、云数据库实现联系人demo 第一步,在云空间中新建一个库表,新建完成后向里面添加一条数据,其中_id是库表自动生成的索引值 点击表结构可以看到该库表的schema代码,我们可以点击编辑按钮,在页面上直接修改库表属性,也可以将库表的schemaCode下载到编辑器中修改后再上传到服务器,这里我们采用第二种方式 右键目录中的database,下载所有DB schema 接下来演示通过标签进行查表的方法 创建list页面,编译以下代码,保存后发现我们没有读表的权限,我们需要将下载的DB Schema中的read属性改为true(其中的permission对应该表的增删改查四种权限后续将不在进行单独说明),上传脚本后刷新页面,可以看到数据已经成功获取到库表中的数据,我们去服务器直接修改库表数据,刷新页面可以发现页面上的数据也相应发生了变化,证明确实是成功获取到库表的数据 <template> <view> <unicloud-db v-slot:default="{data, loading, error, options}" collection="contact-demo"> <view v-if="error">{{error.message}}</view> <view v-else> {{ data }} </view> </unicloud-db> </view> </template> 稍微美化一下demo,将uni-ui导入项目,导入后将列表渲染,然后新建详情页,并且在列表页带参跳转到详情页 <template> <view> <unicloud-db v-slot:default="{data, loading, error, options}" collection="contact-demo"> <view v-if="error">{{error.message}}</view> <view v-else> <uni-list> <uni-list-item v-for="item in data" :key="

大厂面试题丨你将遇到的百万级数据表分页查询

今天的话题,要给大家分享的是:百万级数据表分页查询,为什么不是千万,亿级呢,你想啊,单表千万上亿,你不是疯了吗,早就分表了,单表数据量通常都是控制在几百万左右,真正实际项目中,都是百万级数据,维恩老师今天要给大家讲的前提是,项目 redis 缓存策略,架构都搭完了,查询命中了数据库表来查询的情况,如何进行优化,所以我们要搞明白这三个问题: 没有百万级以上数据查询分页经验怎么办? 提升海量数据查询效率的关键因素? 三年工作经验了,如何突破晋升下一阶段? 互联网大厂关于海量数据查询的面试题 互联网大厂必问题 :通常出现在 阿里/京东 等电商平台面试机率大。 百万级数据表分页查询做过吗? 你们的查询时间要求是多少? 有没有遇到慢查询的情况? 你是如何解决的? 注意面试官的预期 面试开始,接招吧 面试官:做过海量数据查询及分页的项目吗?你是怎么优化的? 面试者:是的,在之前的一个项目中的订单业务中涉及到大数据量查询问题 案例场景 1、一个订单业务系统中,在mysql库中订单表(ordertb)500万条记录: SELECT COUNT(id) FROM ordertb 更多面试资料领取,微信扫码 2、现在的需求是,要筛选:2010全年数据约为200万条,要求分页显示: 大数据量分页查询产生的问题 我们要如何解决这个问题呢? 咱们来讲一讲,建索引的几大原则: 1、最左前缀匹配原则非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a =1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 2、=和in可以乱序比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。 3、尽量选择区分度高的列作为索引区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。 4、索引列不能参与计算,保持列“干净”比如from_unixtime (create_time)= 2014-05-29’就不能使用到索引,原因很简单,b+ 树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’); 5、尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。 更多面试资料领取,微信扫码 使用explain查看执行计划 添加索引后的效果

实践数据湖iceberg 第五课 hive catalog特点

系列文章目录 @[TOC](文章目录) 前言 不以完美记录做为目标。以记录自己学习过程为主线,进行记录,记录通过实践,来了解原理,并把这个实践过程分享给后来需要学习的同学。 本节:了解hive catalog与hadoop catalog的区别,进行认识 1. 建立 hive catalog,建表 创建hive_catalog与表 CREATE CATALOG hive_catalog4 WITH ( 'type'='iceberg', 'catalog-type'='hive', 'uri'='thrift://hadoop101:9083', 'clients'='5', 'property-version'='1', 'warehouse'='hdfs:user/hive/warehouse/hive_catalog4' ); 在hive_catalog下创建数据库 use catalog hive_catalog4; CREATE TABLE `hive_catalog4`.`default`.`sample` ( id BIGINT COMMENT 'unique id', data STRING ); 2. 查看hdfs [root@hadoop101 ~]# hadoop fs -ls -R /user/hive/warehouse/sample/ drwxr-xr-x - root supergroup 0 2022-01-18 15:23 /user/hive/warehouse/sample/metadata -rw-r--r-- 2 root supergroup 1150 2022-01-18 15:23 /user/hive/warehouse/sample/metadata/00000-a426dad1-4ec5-43d0-a7c8-f82601c3d055.metadata.json 3. 写入数据到iceberg Flink SQL> insert into `hive_catalog4`.

elasticsearch-6.5.4集群部署(图文详细)及常见错误

目录 服务器规划 部署包下载 服务器初始化(所有节点都要操作) 内核参数修改 /etc/security/limits.conf修改 /etc/sysctl.conf文件修改 禁用selinux 关闭防火墙 创建es用户 安装jdk es部署 单节点(节点1)部署 部署包上传解压 配置文件修改 修改文件属组 部署包分发 单节点(节点2)部署 配置文件修改 修改文件属组 单节点(节点3)部署 配置文件修改 修改文件属组 启动ES集群 验证 单节点验证 查看集群状态 查看集群效果 本次测试环境服务器系统:Red Hat Enterprise Linux Server release 7.3 (Maipo),其他版本系统在服务器初始化环节可能存在差异,请自行查找对应命令 服务器规划 服务器ipjdk节点端口192.168.54.131jdk1.8node1319200192.168.54.132jdk1.8node1329200192.168.54.133jdk1.8node1339200 部署包下载 官网下载地址Elasticsearch 6.5.4 | Elastic 下载对应系统的部署包 服务器初始化(所有节点都要操作) 内核参数修改 /etc/security/limits.conf修改 修改/etc/security/limits.conf文件,末尾追加如下内容(需退出重新登录用户或直接重启系统才会生效) * soft nofile 65536 * hard nofile 65536 * soft nproc 4096 * hard nproc 4096 否则es启动会报错::bootstrap checks failed [1]: max file descriptors [65535] for elasticsearch

软件测试03:用例执行以及缺陷管理的学习,附禅道下载使用流程

系列文章目录 软件测试功能到自动化学习路线图,2022年最新版技术栈 软件测试01:从了解测试岗位职能和测试流程开始,附作业 软件测试02:6大实际案例手把手教你设计测试点 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录今天目标一、用例执行二、缺陷2.1 定义2.2 缺陷标准3.3 缺陷产生的原因3.4 缺陷的生命周期3.5 缺陷核心要素3.6 缺陷提交要素3.7 缺陷类型 三、缺陷管理3.1 excel示例3.2 缺陷跟踪流程3.3 提交注意事项3.4 缺陷管理工具3.5 禅道(项目管理工具)3.6 缺陷标题扩展 作业讲解(day02)今晚作业 今天目标 执行用例(课上案例编写的用例) 缺陷相关知识 能够说出软件缺陷判定标准 能够说出项目中缺陷的管理流程 能够使用Excel对于缺陷进行管理 能使用工具管理缺陷 一、用例执行 说明:执行结果与用例的期望结果不一致(含义),为缺陷。 执行失败的用例 提示:用例执行不通过为缺陷,需要进行缺陷管理 二、缺陷 2.1 定义 软件中存在的各种问题,都为缺陷,简称bug; 2.2 缺陷标准 1、少功能 2、功能错误 3、多功能 4、缺少隐性功能 5、易用性(软件测试人员专业角度) 3.3 缺陷产生的原因 1、需求文档 2、架构设计 3、编码实现 4、环境(硬件、软件) 3.4 缺陷的生命周期 1、回归测试: ①常规项目回归:项目本次发布新增2个模块,最基本要测新增模块功能及新增模块关联的旧模块。 ②非常规项目(银行、部队、航天):新增功能,必须全部复测。 2、回归bug:上一个版本发现的缺陷,开发修复完毕,在下个版本进行重新验证。 3.5 缺陷核心要素 3.6 缺陷提交要素 3.7 缺陷类型 1、功能错误 2、UI页面错误 3、兼容性 4、数据(数据库) 5、易用性 6、建议 7、架构缺陷 工作流程(小结) 设计用例->执行用例(执行测试)->缺陷(提交、验证、关闭) 缺陷定义:任何问题(Bug) 缺陷标准:多功能、少功能、错误、缺少隐性功能、易用性 描述缺陷重点:缺陷标题、前置条件、复现步骤、预期结果、实际结果、附件备注 提交缺陷信息:指派人、缺陷等级、修复优先级、类型、状态(统计缺陷) 三、缺陷管理 3.

Nginx反向代理基本设置的21个指令

1,proxy_pass 设置被代理服务器的地址,可以主机名,IP加端口号的形势,语法位:proxy_pass URL,下面举例说明: 1 2 3 4 5 6 7 8 9 10 11 12 upstream proxy_sers { server 192.168.123.1/URI; server 192.168.123.2/URI; server 192.168.123.3/URI; } server{ listen 80; server_name www.form1.cn; location / { proxy_pass http://proxy_sers; #server中指明 http:// 在proxy_pass就不需要指定 } } proxy_pass中URL是否包含URI的问题,当访问 www.form1.cn/server 1 2 3 4 location /server/ { server_name www.form1.cn; proxy_pass http://192.168.123.2; } 由于proxy_pass中URL不包含URI,所以转向的地址为 http://192.168.123.2/server 1 2 3 4 location /server/ { server_name www.form1.cn; proxy_pass http://192.168.123.2/loc; } 由于proxy_pass中URL指明了URI,所以转向的地址为 http://192.

AOP+注解+SpringEL表达式记录操作日志

在后台管理系统中,日志是不可或缺的,通常,在开发的时候我们会设置日志级别为debug或info,上线后再设置为error,这些系统日志在帮助我们排查系统问题时作用非常大。但还有一类日志也是非常重要的,那就是操作日志,也就是记录用户在某个时间点干了什么事的日志。这篇文章就简单聊聊如何更好的记录操作日志。 在参与过不少项目的开发后,大概总结出了下面这么几种记录日志的方式。 第一种就是直接调用日志方法,类似下面这种,这类代码在以前的老系统中非常常见,好处是代码清晰,但太冗余了,总不可能每次要记录日志都复制一遍,如果不需要记录日志了,那又得挨个删除,特别麻烦。 void doSomething() { String userName = WebUtil.getUserName(); logService.saveLog(userName, "${userName} did something at ${date}..."); } 第二种就是目前非常流行的注解加切面的方式,也是今天要介绍的。但我发现,在不少项目中,日志的功能都比较单一,记录的结果大都是“[xxx]执行了[xxx]操作”之类的,但在执行这个操作的时候,参数以及目标数据的信息等,日志中是看不出来的,这时候可能又会怀念上面的第一种方式,但实则大可不必。 用过spring缓存的应该都记得spring缓存中的非常常用的几个注解,如@Cacheable、@Cacheput等,在使用这些注解的时候,我们都会活用SpringEL表达式来操作缓存,如下面的代码,通过key = #id来动态设置key的值 @Cacheable(value = "user", key = "#id") public UserInfo getById(Long id) { return userMapper.selectById(id); } 那么在日志记录的时候,我们也可以模仿spring缓存,让日志注解支持SpringEL表达式,如此一来就可以非常方便的记录我们需要的日志了。 首先,新建日志注解LogRecord,代码如下 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface LogRecord { /** * 日志内容,支持SpEL表达式 */ String content(); /** * 业务标识,支持SpEL表达式 */ String bizNo(); /** * 类别 */ String category() default ""; /** * 日志记录条件,支持SpEL表达式 */ String condition() default "

【opencv】在图片上画角并且进行角度检测

【opencv】在图片上画角并且进行角度检测 import cv2 import math path ='test.jpg' img = cv2.imread(path) pointsList = [] #鼠标点 def mousePoints(event,x,y,flags,params): if event == cv2.EVENT_LBUTTONDOWN: size = len(pointsList) if size != 0 and size % 3 != 0: cv2.line(img,tuple(pointsList[round((size-1)/3)*3]),(x,y),(0,0,255),2) cv2.circle(img,(x,y),5,(0,0,255),cv2.FILLED) pointsList.append([x,y]) print(x,y)#打印坐标点 #梯度 def gradient(pt1,pt2): return (pt2[1]-pt1[1])/(pt2[0]-pt1[0]) #获得角度 def getAngle(pointsList): pt1, pt2, pt3 = pointsList[-3:] m1 = gradient(pt1,pt2) m2 = gradient(pt1,pt3) angR = math.atan((m2-m1)/(1+(m2*m1))) angD = round(math.degrees(angR)) cv2.putText(img,str(angD),(pt1[0]-40,pt1[1]-20),cv2.FONT_HERSHEY_COMPLEX, 1.5,(0,0,255),2) while True: if len(pointsList) % 3 == 0 and len(pointsList) !

Linux中ps命令详解

Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。 ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而 ps 命令就是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。 ps 为我们提供了进程的一次性的查看,它所提供的查看结果并不动态连续的;如果想对进程时间监控,应该用 top 工具。 linux上进程有5种状态 1 2 3 4 5 1. 运行(正在运行或在运行队列中等待) 2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号) 3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生) 4. 僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放) 5. 停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行) ps工具标识进程的5种状态码 1 2 3 4 5 D 不可中断 uninterruptible sleep (usually IO) R 运行 runnable (on run queue) S 中断 sleeping T 停止 traced or stopped Z 僵死 a defunct (”zombie”) process 语法 1 ps [option]

Pyinstaller打包成使用了pyonnet包的exe时报错System.IO.FileNotFoundException:Unable to find assembly ‘XXX.dll‘。

首先pyonnet安装要确保无误,不然会报OSError: cannot load library ''; 当前版本是python39,此时pip install pythonnet 报错没有wheel, 需要自行下载,附上大多数wheel的下载链接https://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 下载完成后pip install 路径+wheel名。 因为我用到了pythonnet的import clr;clr.AddReference('dll名') 且在打包完成后dist文件夹的exe同级目录没有这个dll, 所以报错System.IO.FileNotFoundException:Unable to find assembly 'XXX.dll'。最后自行添加在同级目录即可。

DM-设置定期收集统计信息作业

当你要根据表的变动情况进行统计信息的收集,可以设置定时作业进行收集。 1.创建临时表 --记录最大trxid create table trxid_jilu (OWNER VARCHAR2(20),TABLE_NAME VARCHAR2(20),trx_id int,table_cnt int,riqi date); --记录数据变化 create table gather_tab (OWNER VARCHAR2(20),TABLE_NAME VARCHAR2(20),table_cnt_bh int,table_cnt int,flag_gather int default 0); 2.插入数据,需要保留前一天的max(trxid),获取当天的max(trxid),当天的全表数据量,可以在当天晚上进行定时收集,将以下脚本放到定时作业里 begin for tb in ( select owner, table_name from dba_tables where owner not like 'SYS%' and owner<>'CTISYS' and table_name not like 'GATHER_TAB%' ) loop execute immediate 'insert into trxid_jilu(OWNER,TABLE_NAME,trx_id,table_cnt,riqi) select '''||tb.OWNER||''','''||tb.TABLE_NAME||''', (select max(trxid) from "'||tb.owner||'"."'||tb.table_name||'"), (select count(*) from "'||tb.owner||'"."'||tb.table_name||'"),SYSDATE from dual'; commit; --对于前天收集的数据清理,保留当前的max(trxid) execute immediate 'delete from trxid_jilu where riqi=to_char(sysdate-2,''yyyy-mm-dd'')';

Linux中tar压缩命令详解

语法 1 # tar [-cxtzjvfpPN] File or directory 参数 1 2 3 4 5 6 7 8 9 10 11 -c :建立一个压缩文件的参数指令(create 的意思); -x :解开一个压缩文件的参数指令! -t :查看 tarfile 里面的文件!特别注意,在参数的下达中, c/x/t 仅能存在一个!不可同时存在!因为不可能同时压缩与解压缩。 -z :是否同时具有 gzip 的属性?亦即是否需要用 gzip 压缩? -j :是否同时具有 bzip2 的属性?亦即是否需要用 bzip2 压缩? -v :压缩的过程中显示文件!这个常用,但不建议用在背景执行过程! -f :使用档名,请留意,在 f 之后要立即接档名!不要再加参数!例如使用『 tar -zcvfP tfile sfile』就是错误的写法,要写成『 tar -zcvPf tfile sfile』才对 -p :使用原文件的原来属性(属性不会依据使用者而变) -P :可以使用绝对路径来压缩! -N :比后面接的日期(yyyy/mm/dd)还要新的才会被打包进新建的文件中! --exclude FILE:在压缩的过程中,不要将 FILE 打包! 范例一:将整个 /etc 目录下的文件全部打包成为 /tmp/etc.

GitHub clone克隆或下载一个仓库某个文件夹

git学习之git clone 克隆或下载一个仓库单个文件夹 应用场景:比如GitHub整个源代码非常大,好几个G,或者对源代码中大量的测试代码没有兴趣,不想全部clone,只想克隆项目的某个文件夹下面的所有源码。 1、如果是想克隆别人的项目或者自己的 很简单的一个网站就解决了。DownGit: 只需要找到仓库中对应文件夹的url,输入之后,点击 download 自动打包下载: DownGit链接:https://www.itsvse.com/downgit/ 2、克隆自己的项目 注意:本方法会下载整个项目,但是,最后出现在本地项目文件下里只有需要的那个文件夹存在。类似先下载,再过滤。 有时候因为需要我们只想gitclone 下仓库的单个或多个文件夹,而不是全部的仓库内容,这样就很省事,所以下面就开始教程啦 在Git1.7.0以前,这无法实现,但是幸运的是在Git1.7.0以后加入了Sparse Checkout模式,这使得Check Out指定文件或者文件夹成为可能。 举个例子: 现在有一个test仓库https://github.com/mygithub/test 你要gitclone里面的tt子目录: 在本地的硬盘位置打开Git Bash 转载: https://www.itsvse.com/thread-7086-1-1.html

DM数据库归档挖掘技术学习-DBMS_LOGMNR 包

一、背景介绍 在做数据库操作中,我们有时遇到对数据库误操作的情况,在DM数据库中闪回技术尚未成熟的情况下,可以使用归档挖掘来对数据进行修复。 二、准备条件 dm.ini中的ARCH_INI=1以及RLOG_APPEND_LOGIC 选项置为 1 或 2 测试表 create table test11 (c1 int,c2 int); begin for i in 1..10000 loop insert into test11 values (i,i); end loop; commit; end; 假设想更新c2=1的c1列为100001 update test11 set c1=1000001; 但是未写条件将全表更新了。 查出归档文件路径 select first_time,name from v$archived_log; 将需要挖掘的归档添加到包里,如下 call dbms_logmnr.add_logfile ('/home/dmdba/arch/dm/ARCHIVE_LOCAL1_0x17465BD4[0]_2022-01-12_17-15-11.log'); 分析归档文件,指定开始时间和结束时间,也可以不指定 --DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130); DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130 , STARTTIME=>TO_DATE('2022-01-11 14:00:00','YYYY-MM-DD HH24:MI:SS') , ENDTIME=>TO_DATE('2022-01-13 18:00:00','YYYY-MM-DD HH24:MI:SS')); options的解释如下: 分析的结果记录在V$LOGMNR_CONTENTS中 select * from v$LOGMNR_CONTENTS; 拿出你要修复的语句,一般可以在SQL_REDO中看到 将语句改写修复 UPDATE "SYSDBA"."TEST11" SET "C1" = 1 WHERE "

【C语言】打印数字金字塔

题目:打印如下图所示的数字金字塔(注意观察数字分布的规律)。 思路:运用循环的嵌套控制输入行以及每一行的具体内容,其中不同的内循环控制具体内容——递增的数字,空格,递减的数字 代码: #include<stdio.h> int main() { int n,i=1,num;// n为总行数,i为每行最大的数,num为数字 printf("请输入金字塔行数n:"); scanf("%d",&n); for(num=1;i<=n;i++) { for(num=1;num<=2*(n-i);num++) printf(" ");//打印空格 for(num=1;num<=i;num++) printf("%d ",num);//打印递增的数字 for(num=i-1;num>=1;num--) printf("%d ",num);//打印递减的数字 num=i; printf("\n"); } return 0; } 运行结果:

老毛子的二级路由,通过无线中继方式设置与主路由在同一网段

以极路由作为示例root过程各有不同step3开始其他路由器也适用 **Step1: **路由器复位,有线/无线连接路由器进入后台(192.168.199.1/hiwifi.com),设置WiFi联网 **Step2: **极路由ROOT local-ssh利用工具 (hiwifi.wtf) 提交就会得到一个端口号 Step3: 使用winSCP连接路由器进行文件传输,将(breed-mt7620-hc5761.bin)breed扔进tmp文件夹 Step4: 打开putty配置连接进行命令操作 带A的写入命令是mtd -r write /tmp/breed-mt7628-hiwifi-hc5661a.bin u-boot(不带A的写入的命令是mtd -r write /tmp/breed-mt7620-hc5761.bin u-boot),直接复制过去按回车键,怕你们敲错了。 Step5: 1.先不要给路由器充电,将网线一端接lan口,注意是lan口,另一端接电脑端口; 2.用一只手尖锐物一直按住路由器后面的reset孔,同时另一只手给路由器通电,通电后5秒钟左右,就可以松开按住reset的那只手了。可以观察到路由器左边的电源灯有节奏的跳动, 3.在浏览器输入192.168.1.1回车出现breed-web界面,选择老毛子RT-N14U-GPIO-12-xiaodu-ji1S-128M_3.4.3.9-099.trx Step6: 输入192.168.123.1就可进入老毛子系统 Step7: 老毛子的二级路由,通过无线中继方式设置与主路由在同一网段 先将操作模式设为AP模式 2、设置成功后,将不会在有wan外网设置的页面,如下图,只有lan内网设置。然后设置默认网关为主路由器IP,并且关掉DHCP服务器即可。 所需文件:链接:链接:https://pan.baidu.com/s/1qqdA8oOcLAoc6VOJHEFCJQ 提取码:5998 参考文章: https://blog.csdn.net/qq_39257487/article/details/108534567 https://www.bilibili.com/video/BV1K64y1i7da?from=search&seid=12721104330378151569&spm_id_from=333.337.0.0 https://www.cnblogs.com/housestudy/p/11408439.html?-%E5%85%B6%E5%AE%83%E4%BB%A3%E7%A0%81%E7%B1%BB%E8%B5%84%E6%BA%90-CSDN%E6%96%87%E5%BA%93

Yarn常用命令

Yarn常用命令 文章目录 1.yarn application 查看任务2.yarn logs 查看日志3.yarn applicationattempt 查看尝试运行的任务4. yarn container查看容器5.yarn node 查看节点状态6.yarn rmadmin 跟新配置7.yarn queue 查看队列7.yarn queue 查看队列 1.yarn application 查看任务 (1)列出所有Application yarn application -list (2)根据 Application 状态过滤:yarn application -list -appStates (所有状态:ALL、NEW、NEW_SAVING、SUBMITTED、ACCEPTED、RUNNING、FINISHED、FAILED、KILLED) yarn application -list -appStates FINISHED 2.yarn logs 查看日志 (1)查看Application 日志 yarn logs -applicationId <ApplicationId> (2)查询Container 日志 yarn logs -applicationId <ApplicationId> -containerId <ContainerId> 3.yarn applicationattempt 查看尝试运行的任务 (1)列出所有 Application 尝试的列表 yarn applicationattempt -list <ApplicationId> (2)打印 ApplicationAttemp 状态 yarn applicationattempt -status <ApplicationAttemptId> 4.

python 读取TIFF信息

读取TIFF的行列数、投影转换信息、地图投影信息以及栅格像元值。 1、读取TIFF 用到的是GDAL模块的gdal.Open()函数。 dataset = gdal.Open(filepath) 2、获取TIFF的行列数 主要用到RasterXSize和RasterYSize来获取栅格数据的行列数。 col = dataset.RasterXSize row = dataset.RasterYSize 3、获取投影转换信息 用到GetGeoTransform()函数。 geotrans = dataset.GetGeoTransform() 4、获取地图投影信息 用到GetProjection()函数。 proj = dataset.GetProjection() 5、获取栅格像元值 用到ReadAsArray()函数。 data = dataset.ReadAsArray() 全部代码: def readtif(filepath): dataset = gdal.Open(filepath) col = dataset.RasterXSize row = dataset.RasterYSize geotrans = dataset.GetGeoTransform() proj = dataset.GetProjection() data = dataset.ReadAsArray() del dataset return [col, row, geotrans, proj, data]

微波工程吐血总结

微波工程 课本《微波工程》pozar 《微波技术与天线》王新稳 首先是电磁场的基本理论,这些和《电磁场与电磁波》和《高等电磁》的第一章一样的,都是介绍电磁场基本知识。先从麦克斯韦方程,然后边界条件(包括一般分界面的,理想介质的,pec的,pmc的) 然后是解麦克斯韦方程,怎么解呢,直接解解不了,把它化成波动方程,只保留e或者h。在无源的情况下,它是亥姆霍兹方程,是平面波球面波或者柱面波。平面波的时候,可以分析它的入射和反射(垂直入射和斜入射,理想导体和理想介质,全反射和平面波)柱面波和球面波主要是入射反射,和平面波的表达形式之间互相转换(高等电磁) 有源的时候,分静电场和波动场,静电场可以用镜像法,分离变量法,格林函数法等等,波动场可以用达朗贝尔方程,把e和h转换成fai和pusai(红和蓝不好求,把它转成黄和绿,求出黄和绿,再由黄和绿求出红和蓝) 以上是微波工程第一章 然后第二章,可以结合微波技术与天线,主要是无源传输线上波节波腹特点。还有Smith原图和四分之波长变换线 第三章是各种传输线,各种传输线的模式,相速,相位常数,波导波长之类的。波导(矩形波导,圆波导的主模,截止波长相速度,波导波长,高次模,场分布)同轴线(主模tem,高次模,主模传输条件)接地介质版上的表面波(te和tm,截止频率) (不熟)带状线(主模tem,特征阻抗,近似的静电解?)微带线(主模准tem,频率依赖效应和高次模)横向谐振法(不熟)波速和色散(定义,群速)其他类型传输线和波导(脊波导,介质波导,槽线,共面波导,覆盖微带线) 第三章还需要仿真,在hfss或者cst里建立各种传输线的模型,看他们传播常数之类的 第四章微波网络 首先是阻抗矩阵和导纳矩阵,掌握定义,能够通过简单结构直接计算出来。互易网络和无耗网络的zy矩阵特点(zij=zji,re(zij)=0)然后是重点散射矩阵s矩阵,定义,互易无耗网络特点(s=st,幺正),参考平面移动,功率波和广义散射参量(不熟)。然后是传输abcd矩阵,掌握定义,是一个端口的电压电流和另一个端口的电压电流之间的关系,掌握简单电路根据定义求解abcd矩阵,掌握abcd矩阵和s矩阵,z矩阵,y矩阵之间的互换关系(参考王新稳书)。然后是信号流图和网络分析仪校正使用(不熟,具体再问问失网那儿的人,具体感受一下怎么校正)。然后是不连续性和模分析(不熟)。最后是波导的激励,类似于仿真中的波端口和同轴馈电,分为电流和磁流模激励以及小孔耦合(不熟,仿真体验一下) 第五章阻抗匹配和调谐,这一章需要仿真感受一下。首先书集总元件lc匹配,分解析解法和Smith圆图法。然后是单短截线调谐和双短截线调谐,这个在圆图上具体怎么走,要不要转换成导纳圆图,还需要再查查,最好不要用导纳圆图。然后是四分之波长变换器,老生常谈了。进一步是小反射理论,分为单节变换器和多节变换器,主要思想就是各级反射互相抵消。根据小反射理论,有两种经典形式,二项式多节匹配和切比雪夫多节匹配,这两个非常类似于二项式和切比雪夫滤波器,都是查表的形式。具体流程要掌握。然后是渐变传输线,这个有利于宽带,分三种,指数渐变,三角渐变和klopfenstein渐变,这三种都不熟。要把渐变传输线和多节匹配变换器仿真一下体验体验。最后是播的-fano约束条件,这个是经常用的约束条件,表示回波损耗和带宽之间的制约的关系。 第六章是谐振器,主要有串并联谐振电路,传输线谐振器,波导谐振器(圆波导矩形波导),介质谐振器这几种,最后讲了谐振器咋激励的和腔的微扰。串并联谐振器是理想谐振器模型,包括串联谐振器和并联谐振器,掌握他们的阻抗随频率变化关系(串联谐振时阻抗最低,并联谐振时阻抗最高。可以考虑为谐振时相当于谐振元件不存在了,串联谐振谐振元件不存在相当于串联的元件变少了,阻抗降低,并联谐振反之)。然后是有载q和无载q,注意他们之间的关系,以及无载q和外部q的计算公式。 然后是传输线谐振器,分别为短路二分之波长谐振线,短路四分之波长谐振线和开路二分之波长谐振线。第一个等效为串联电路,后两个为并联电路,短路四分之波长可看做开路二分之波长取了一半。 然后是波导腔谐振器,包括矩形波导谐振器和圆波导谐振器,掌握谐振基模和谐振频率(矩形波导基膜te101,圆波导基膜可能不是te111,也可能是tm010,根据半径决定) 然后是介质谐振器,它主模是te01Δ模,掌握它的特点,进行仿真 谐振器的激励,掌握四种经典激励方式,微带传输线缝隙耦合到微带馈线,同轴探针馈送到矩形波导谐振腔,圆柱谐振腔小孔耦合到矩形波导,介质谐振器耦合到微带馈线。掌握耦合系数的定义以及欠耦合临界耦合过耦合。掌握缝隙耦合和小孔耦合等效电路。 这一章要再仿真体验一下,特别是介质谐振器 第七章是功分器(三端口,一分二)和定向耦合器(四端口),这一章最重要的是每种结构的s参数要记住 首先是功分器和耦合器基本特征,这里是用s参数推导,得出来的结论是 三端口网络不能同时满足无耗、互易和全部端口匹配这三个条件。如果非互易,就是环形器,一般用铁氧体,s参数。如果不全部匹配,只有两个端口匹配,就是。如果有耗,就是电阻性功分器。 四端口网络,也叫定向耦合器,有两种,分别为对称耦合器和非对称耦合器,注意它们各自的s参数特点。对称耦合器的栗子是90度混合网络,反对称耦合器的栗子是180度混合网络,比如魔t混合环。注意耦合度,方向性,隔离度,插入损耗的定义。 下面是具体的功分器和耦合器 首先是t形结功分器(无耗)掌握它的结构,从输出端口看进去失配。然后是有耗的电阻性功分器,掌握它的结构,s参数。 然后是Wilkinson功分器,掌握结构。它是输出端口隔离的,了解奇偶模分析法。掌握不等功分和n路Wilkinson的结构,各个z0和互连电阻r。 下面是四端口的定向耦合器 首先是波导定向耦合器,这个不熟,还得看看,最好仿真一下。 然后是正交90度混合网络,注意结构和s参数,了解奇偶模分析 然后是耦合线定向耦合器,一般接功率计探头的通道a接的就是多节耦合线定向耦合器,耦合系数一般是20dB或者30dB,耦合度比较小,带宽2-4GHZ。 然后是lange桥,这个应用非常多,一般用到功放里用来增加带宽,注意它的结构和宽带工作原理 最后是180度混合网络,主要包括环形混合网络,魔t,渐变传输线,掌握它们的s参数(是非对称耦合器,根据这个容易写出它的s参数)和结构(注意1234端口顺序和哪个是和差端口) 最后还有一些其他耦合器,主要也是波导里的。 要仿真鸭 第八章滤波器以后再总结,主要看徐兴福ads设计那本书,镜像参量法也要了解 第九章铁氧体以后再总结,要掌握移项器环形器隔离器的结构 后面都以后再说 需要仿真的: 第三章,第五章,第六章,第七章,第八章,第十三章 放大器一章,首先介绍三种增益,这个一定要搞清楚,也是后面的基础,分别是增益,资用功率增益,转移功率增益。增益是负载吸收功率和二端口输入功率之比,和zs无关,和zl有关。可以想象为南水北调,从南京调到北京,从南京水库出来到河道,从河道到北京水库,各自都会有反射的回流的水。增益就是从河道的输入到北京的这一部分。资用功率增益是二端口可用功率之比和信号源最大输出功率之比,和zl无关,和zs有关。可以想象为从南京水库到北京水库入口的部分。转移功率增益是传输到负载的功率和源的可用功率之比。可以想象为到北京水库的水和出南京水库的水总比。掌握各个增益的公式 其中转移功率增益最有用,它能分成输入匹配网络gs,晶体管本身g0和输出匹配网络增益gl三部分相乘。可以理解为从南京水库进入河道的水的比例(因为zs和z0不同,有一部分会反射,类似于南京水库出水口和河道入口宽度不同,有一部分反射了),河道的增益和从河道进入北京水库的水。记住每个增益的公式。注意gs下面是γin,gl下面是s22 当管子是单向的,s12等于0的时候,γin=s11,γout=s22,gs,g0和gl带入得到的新公式要知道。 然后是稳定性圆,在圆外是稳定的。稳定性圆在Smith圆外面时是绝对稳定否则是条件稳定。也可以用稳定性系数k计算,大于1绝对稳定,反之条件稳定,要知道k的计算公式。 然后是几种不同的放大器,小信号有最大增益放大器,等增益放大器,低噪放,大信号有功放。 最大增益放大器的γs和γl都是固定的,用公式计算即可,它的原理是γs和γin共轭匹配的时候能量能最大传输,同理适用于输出端。用公式计算。注意单向的时候综合单向的γin=s11和最大增益γin=γs共轭,得到单向最大增益γs=s11共轭,同理输出γl=s22共轭 等增益圆要记住圆心和半径 低噪放是用噪声系数来衡量,和增益不是一个体系,就像增益衡量的是南京水库到北京水库的水量比例,噪声衡量的是含沙量。噪声只和γs有关,和γl无关,可以理解为一条河的含沙量从源头就决定了。当γs=γopt时,此时一些工作电流能抵消噪声电流,噪声系数最小。不等时,噪声系数增大。记住计算噪声系数的公式。 上面就是单级晶体管设计,包括最大增益,等增益,低噪声 下面是宽带放大器,包括平衡放大器,分布式放大器,差分放大器,负反馈放大器,阻抗匹配,补偿匹配等等。他们各自的优缺点要记住。p469和梁毕设 然后是功率放大器,主要分清楚abcdef各类型放大器的特点。可以参考《射频与微波晶体管放大器基础》 这就是放大器一章所有内容 然后是振荡器和混频器一章,这一章比较多,相当于以前好几章内容。首先是射频振荡器,频率比较低的:首先有考毕兹和哈特莱两种,都是从源极引一个反馈回栅极,两个电容一个电感的是考毕兹,两个电感一个电容的是哈特莱。要知道他们的结构(源接地,反馈回路中两个电容或者两个电感之间接地)。然后是(石英)晶体振荡器,q值高,可以用到考毕兹或者哈特莱电路里替代电感。 然后是射频振荡器,频率更高,不能用集总电容电感,得用微带线等分布参数。有用二极管和晶体管两种和介质谐振器。如果用二极管微波振荡器,满足Rl+Rin=0,Xl+Xin=0。由zin得到zl,在设计阻抗匹配电路。用晶体管做振荡器,rs=-rin/3,xs=-xin,注意这个电阻是那个的三分之一。用介质谐振器,注意一般用到晶体管的反馈电路里,有并联结构和串联结构两种,记住电路。它的原理就是作为高q滤波器将一部分晶体管输出返回到输入。 然后是振荡器相位噪声,相位噪声是本来谐振器振荡频率就是一个单频点,但是实际有点便宜,表现为本来该在频谱是一条向上直线,结果变成一段弧线,左右都有。记住相位噪声的定义,可以用周围1h的误机率来理解。记住振荡器相位噪声的lesson模型,注意三段,分别为18db的f3分之一,12或6db的f2或f分之一,平坦。 然后是频率倍增器,不熟 最后是混频器,首先是混频器的一些指标,不再赘述,然后是具体混频器,首先是楞怼的单端二极管和晶体管混频器,这都是rf和lo楞怼上去的。这种楞怼方法不好,可以改成平衡混频器,这样带宽点,要记得平衡混频器的结构嗷,rf和lo怼到混合网络上,输出接俩反向二极管,正好,然后接一块,连到低通滤波器上。 还有个差分和希尔波特滤波器,希尔波特滤波器很屌,四个管子当开关,两个管子当放大器 双平衡混频器也挺屌的,只是平常用的时候,比较少用二极管,而是用晶体管替代二极管使用 然后是他妈的镜像抑制混频器,这个更扯淡了,它不是为了混频,它是吧混频之后的上边带和下边带混合信号给分开。在平衡混频器基础上再接个混合网络。然后是些杂牌滤波器,不再赘述。 平心而论,平时用的比较多的混频器,是单端混频器,双平衡混频器(混频器环mixer ring),和差分或吉尔波特混频器。单平衡混频器和镜像抑制混频器其实用的比较少 第十一章是有源射频及微波器件 包括二极管,晶体管(双极结管和场效应管),简单介绍了一下mmic和mems,最后是微波管,微波管以前考过,是重点 二极管包括肖特基二极管,PIN,耿式,impatt。肖特基二极管是半导体-金属结,主要有个小信号特性,此时是平方率区,输入功率再增大到了饱和区就不变了。利用它的小信号特性,进行频率变换,主要有三种:整流,检波,混频。混频器里用的二极管,比如单端二极管混频器,平衡混频器,双平衡混频器都是肖特基二极管,主要利用它的小信号近似。 然后是PIN二极管,没啥好说的,当开关用。把它用在移相器里有三种形式,开关线,加载线和反射移相器。要记住每种的结构,目前只熟悉开关线型的。 变容二极管是产生一个随偏置电压平滑变化的结电容。知道就行。 耿式二极管会用到电子管里,主要用的它的负阻效应。它是砷化镓或者磷化铟材料 impatt二极管主要利用在高电压下反偏雪崩击穿效应。利用雪崩击穿时产生的负阻。

win10桌面突然显示未激活

1.以管理员身份打开cmd,"C:\Windows\system32\cmd.exe" 2.输入sfc/scannow命令自动修复系统文件 sfc/scannow 3.修复完成后重启电脑。

2021年低压电工考试及低压电工考试报名

题库来源:安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通:低压电工考试参考答案及低压电工考试试题解析是安全生产模拟考试一点通题库老师及低压电工操作证已考过的学员汇总,相对有效帮助低压电工考试报名学员顺利通过考试。 1、【单选题】尖嘴钳150mm是指( )。( B ) A、其绝缘手柄为150mm B、其总长度为150mm C、其开口150mm 2、【单选题】运输液化气,石油等的槽车在行驶时,在槽车底部应采用金属链条或导电橡胶使之与大地接触,其目的是( )。( B ) A、中和槽车行驶中产生的静电荷 B、泄漏槽车行驶中产生的静电荷 C、使槽车与大地等电位 3、【单选题】干粉灭火器可适用于( )kV以下线路带电灭火。( C ) A、10 B、35 C、50 4、【单选题】万用表实质是一个带有整流器的( )仪表。( A ) A、磁电式 B、电磁式 C、电动式 5、【单选题】一般情况下220V工频电压作用下人体的电阻为( )Ω。( C ) A、500至1000 B、800至1600 C、1000至2000 6、【单选题】测量接地电阻时,电位探针应接在距接地端( )m的地方。( B ) A、5 B、20 C、40 7、【单选题】钳形电流表使用时应先用较大量程,然后在视被测电流的大小变换量程。切换量程时应( )。( B ) A、直接转动量程开关 B、先退出导线,再转动量程开关 C、一边进线一边换挡 8、【单选题】通电线圈产生的磁场方向不但与电流方向有关,而且还与线圈( )有关。( B ) A、长度 B、绕向 C、体积 9、【单选题】事故照明一般采用( )。( B ) A、日光灯 B、白炽灯 C、高压汞灯 10、【单选题】更换熔体或熔管,必须在( )的情况下进行。( B )

关于F704UM与PICO调试记录

PICO使用的是Arduino IDE开发的,之前也试过使用micropython,但是坑太多,很多解决方案找不到 使用Arduino操作PICO需要先安装主板驱动,可以再工具->开发板管理里面搜索pico进行安装,第一个结果就行,安装完毕后,将pico上的按钮按着不放,插入USB,然后就可以去上传代码。 关于串口通讯,我的通讯模块是正常的,指示灯也正常,在这之前,我已经通过电脑调试过F704UM模块,一切正常。 问题:后面我想通过F704UM的串口和PICO进行通讯,所以我在PICO的串口上倒腾了很多时间。。因为很多尝试都失败了,就是没法通讯 解决:最后使用的UART SER(16, 17, -1, -1);竟然成功了,这里的16和17我原本以为是指得PIN,但是实际上指得是GPIO,为了方便调试,我将pico通过usb-ttl连接电脑进行测试,测试后可以正常通讯了,就是速度有点慢,可能是这个usb转ttl模块的问题 问题:随后我将PICO和F704UM的TX/RX连接,但是还是没法通讯,我就纳闷了,之前用usb-ttl连接的F704UM模块是正常的,现在换了个pico的串口又不行了,就出鬼,然后我将F704UM的串口和USB-TTL的串口连接,电源保持不变,发现还是真连接不上了,我怀疑是供电问题,文档上写着F704UM需要5v,2A的电源进行供电,我使用的是5v,2.5a的电源供电,却不行,我也不清楚啥问题,可能是这个充电头的问题,随后,我将电源移到usb-ttl的5v接口上就正常了 解决:这个我也不清楚,但是肯定得是,电源影响了串口通讯,模块的网络是正常的,一直在和服务器交互数据,所以如果遇到串口无法通讯一定要尝试更换电源,官方说5-12v,最好5v朝上更稳定。 通讯成功后发现速度还是蛮快的,比在电脑上调试还快些。 最后附上一些简单的转发代码: #include "Arduino.h" UART SER(16, 17, -1, -1); void setup() { SER.begin(115200); Serial.begin(9600); while (!SER) { } Serial.println("ready"); } void loop() { if (SER.available() > 0) { Serial.println(SER.readStringUntil('\n')); } if (Serial.available() > 0) { SER.println(Serial.readStringUntil('\n')); } delay(100); } 还有就是readStringUntil('\n')要比readString()快很多。

Java双指针技巧

在我们练习Java算法的时候,难免会遇到一些题目利用一些技巧性的解题方法比不用要强很多,这次主要分享一下有关双针的技巧。 双指针一般分为两类:一类是快慢指针,一类是左右指针。前者解决主要链表中的问题,比如典型的判断链表中是否包含环;后者主要解决数组(或字符串)中的问题,比如二分查找。 一、快慢指针 1. 判断链表中是否含有环 链表中要想不含环,那么这个指针最终会遇到空指针 null 表示链表到头了,这还好说,可以判断该链表不含环 boolean hasCycle(ListNode head) { while (head != null) { head = head.next; } return false; } 如果链表中存在环了,那么上述的代码就会陷入死循环,因为环形数组中没有 null 指针作为尾部节点。 经典解法就是用两个指针,一个跑的快,一个跑的慢,如果不含有环,跑的快的那个指针最终会遇到 null,说明链表不含有环。如果快指针最终会超满指针一圈,和慢指针相遇,说明链表含有环。 boolean hasCycle(ListNode head) { ListNode fast = head, slow = head; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; // 如果两个指针相遇,说明构成了环,返回 true if (fast = slow) { return true; } } return false; } 2.

树莓派安装conda(miniconda)

## 日常更新 sudo apt update sudo apt upgrade # 我看到说要先安装python3,不懂为啥,因为我之前安过了,所以如果有人失败了那就试试这个 # sudo apt install -y python3 # 下载miniconda 32位安装包 curl "https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-armv7l.sh" -o "Miniconda.sh" # 安装(其中有些地方需要用户交互确认,信任证书和安装路径还有系统路径等) bash ./Miniconda.sh !注意要重开一个terminal才能使用

字符串(string),向量(vector)和数组学习总结

字符串(string),向量(vector)和数组 string和vector是两种最重要的标准库类型,string支持可变长字符串,vector表示可变长集合数组,与之相配套的标准库类型中的迭代器,属于string和vector的配套类型,经常用于访问string中的字符或者vector中的元素 命名空间的using声明 在C++的初步学习之中,我们往往会敲这样的一段代码, using namespace std; 一开始的学习之中,我们也不知道其中的意思,只需要明白照着敲就行 简单来说,到目前为止,我们目前所能用到的库函数基本上都属于命名空间std,即C++标准程序库,程序也显式地将这一点表示出来了。 例如: 如果不想敲这段代码using namespace std;而使用cin函数(cin表示从标准输入中读取内容),此处使用作用域操作符(::)的含义:编译器应从操作符左侧名字所示的作用域中寻找那个名字。 因此,std::cin的意思就是要使用命名空间std中的名字cin。 有了using声明就无须专门的前缀(形如命名空间::)也可以使用所需要的名字了。 using声明具有如下的形式: using namespace::name; 一旦声明了上述的语句,就可以直接访问命名空间中的名字; 1.不使用using声明的代码: #include<iostream> int main() { int ans; std::cin>>ans; std::cout<<ans<<std::endl; return 0; } 2.使用using声明的代码: #include<iostream> using std::cin; using std::cout; using std::endl; int main() { int ans; cin>>ans; cout<<ans<<endl; return 0; } 注:按照规定,每个using声明引入命名空间中的一个成员 头文件不应该包含using声明 位于头文件的代码一般来说不应该使用using声明。这个是因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明。则对于某些程序来说,由于不经意间包含了一些名字,反而会产生一些始料未及的名字冲突 **注意事项:**using声明引入的名字所遵循的作用域规则:它的有效范围从using声明的地方开始,一直到using声明所在的作用域结束为止 using指示 using指示(using directive)和using声明类似的地方是,我们可以使用命名空间名字的简写形式;但是,和using声明不同的地方是,我们没有办法控制哪些名字是可见的,因为所有名字都是可见的。 using指示以关键字using开始,后面是关键字namespace以及命名空间的名字(三大部分) 例如: using namespace std; using指示使得某个特定的命名空间中所有的名字都可见,这样我们就无须再为他们添加任何前缀限定符了。 简写的名字从using指示开始,一直到using指示所在的作用域结束都能使用 3.使用using指示的代码: #include<iostream> using namespace std; int main() { int ans; cin>>ans; cout<<ans<<endl; return 0; } 标准库类型string 标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件

Javascript根据属性从对象数据中删除元素

/** * 按Key删除 * 参数val最好限于数字或字符串 */ removeByKey(array,key,val){ if(ArrayUtils.isNotEmptyByProto(array)){ let len = array.length; let ind = 0; let currentInd = 0; while (ind < len){ let currentObj = array[currentInd]; if(currentObj!=null && currentObj[key] == val){ array.splice(currentInd, 1); }else { currentInd++; } ind ++; } } return array; },

如何防止因哈希碰撞引起的DoS攻击

如何防止因哈希碰撞引起的DoS攻击 理解哈希什么是哈希哈希和数组哈希算法哈希碰撞鸽巢原理为什么不能避免哈希碰撞 哈希算法的特点如何解决哈希碰撞开放寻址法线性探测线性探测法适用场景二次探测双重散列 链地址法链地址法适用场景 哈希碰撞攻击 总结 理解哈希 哈希大家在开发中可以说是接触的非常多,在 Java 中的 HashMap,HashSet 等都是哈希的应用,但是到底什么是哈希呢?对哈希我们是否真的非常了解呢? 什么是哈希 哈希(Hash)也称为散列,就是把任意⻓度的输⼊,通过哈希(散列)算法,变换成固定⻓度的输出,这个输出的值就是哈希(散列)值。 哈希和数组 哈希表(Hash table,也叫散列表),是一种可以用 O(1) 时间复杂度来随机读取元素的一种数据结构。前面我们学了数组,链表,栈,队列这些基础数据结构,似乎只有数组在随机访问元素的时间复杂度达到了 O(1) 级别,而哈希表也达到了 O(1) 级别,我们是不是可以猜测这两种数据结构有什么联系呢? 其实哈希表就是利用了数组可以随机访问元素的特性,可以说没有数组就没有哈希表,哈希表是数组的一种扩展。 比如我们现在有一个数组,长度为 8,现在我们想把一个整数 100 存入数组内,这时候假设我们选择了通过取模的算法来决定当前元素存入数组哪个位置,通过 100%8=4,这时候我们就把 100 存入数组内下标为 4 的位置,如下图所示: 在这个例子当中,取模运算就是哈希函数(或者散列函数,Hash 函数),元素 100 称之为 key 或者关键字,而经过哈希函数计算后得到的下标 4 称之为哈希值(散列值),而这个存储数据的数组就称之为哈希表。 哈希算法 想要将一个 key 对应的 value 存入哈希表,必须先经过哈希函数得到数组的下标(散列值),取值的的时候也必须先经过哈希运算才能取值,所以哈希函数一定不能太复杂,否则计算哈希就会带来大量性能的损耗,其次就是如果 key1==key2,那么 hash(key1)==hash(key2) 也一定成立,否则存进去的数我们无法准确的取出来 另外哈希函数还有一个特点就是经过哈希运算后所得到的哈希值必须是一个非负整数,因为数组的下标是从 0 开始的。 综上所述,我们可以得到如果需要将一个数据存入哈希表,那么这种哈希函数有以下三个特点: 哈希函数并不是越复杂越好,要综合考虑时间成本,否则存取数据时哈希计算会带来大量性能消耗。经过哈希函数得到的哈希值是一个非负整数,因为数组的下标是从 0 开始的。如果 key1==key2,那么 hash(key1)==hash(key2)。 哈希碰撞 如果 key1 != key2,但是 hash(key1) == hash(key2),那么我们就称之为发生了哈希碰撞,或者说哈希冲突。比如说我们上面示例中的取模运算这个哈希函数,100 经过哈希运算后得到的哈希值是 4,其实还有 12,20,等关键字经过哈希函数运算之后得到的都是 4,这就发生了哈希碰撞了。

switch case 、循环结构

目录 1. switch case 结构 1.1 基本语法结构 1.2 switch - case 代码展示 1.3 注意事项 2. 循环结构 2.1 循环结构引入 2.2 while 循环结构 2.3 do - while 循环结构 2.4 for 循环结构【重点】 1. switch case 结构 1.1 基本语法结构 // 格式 switch (/* 匹配条件变量 */) { case 常量1: 处理方式1; break; case 常量2: 处理方式2; break; case 常量3: 处理方式3; break; default: 未匹配 case 选择的最终处理方式; break; } 流程: 当程序运行到 switch case 结构,取值 switch 之后小括号内容数据情况,匹配 case 之后的常量,如果有匹配项执行对应的处理方式,利用 break 关键字终止 switch case 结构,如果没有任何一个 case 匹配,执行 default 对应处理方式。

状态机-面向对象编程

状态机-面向对象编程 #include "stdio.h" //状态 typedef struct State { const struct State *(* const state_press)(const struct State *pThis); const struct State *(* const state_release)(const struct State *pThis); } State; //事件--改变状态 void initialize(void); void press_button(void); void release_button(void); //调用函数 static const State *pCurrentState=NULL; static const State *ignore(const State *pThis); static const State *press(const State *pThis); static const State *release(const State *pThis); //迁移过程 const State IDLE = { press, ignore }; const State PRESS = { ignore, release }; const State RELEASE = { press, ignore }; //事件具体实现--改变状态 void initialize(void) { pCurrentState = &IDLE; } void press_button() { pCurrentState = pCurrentState->state_press(pCurrentState); } void release_button() { pCurrentState = pCurrentState->state_release(pCurrentState); } static const State *ignore(const State *pThis) { printf("

Spring Boot Admin,贼好使

Spring Boot Admin(SBA)是一个开源的社区项目,用于管理和监控 Spring Boot 应用程序。应用程序可以通过 http 的方式,或 Spring Cloud 服务发现机制注册到 SBA 中,然后就可以实现对 Spring Boot 项目的可视化管理和查看了。 Spring Boot Admin 可以监控 Spring Boot 单机或集群项目,它提供详细的健康 (Health)信息、内存信息、JVM 系统和环境属性、垃圾回收信息、日志设置和查看、定时任务查看、Spring Boot 缓存查看和管理等功能。接下来我们一起来搭建和使用吧。 它最终的展示效果如下: 1.搭建SBA监控端 我们需要创建一个 Spring Boot Admin 项目,用来监控和管理我们的 Spring Boot 项目,搭建的方式和创建普通的 Spring Boot 项目类似,具体步骤如下。 使用 Idea 创建一个 Spring Boot 项目: 这里需要注意,需要添加 Spring Boot Admin(Server)服务端框架的支持,如下图所示: 也就是创建的 Spring Boot 项目需要添加以下两个重要的框架支持: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> 复制代码 1.1 开启SBA服务 创建完项目之后,需要在启动类上开启 SBA 服务: import de.

7-1 哈夫曼编码(实验) 最全代码解析

给定一段文字,如果我们统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串"aaaxuaxz",容易得到字母 'a'、'x'、'u'、'z' 的出现频率对应为 4、2、1、1。我们可以设计编码 {'a'=0, 'x'=10, 'u'=110, 'z'=111},也可以用另一套 {'a'=1, 'x'=01, 'u'=001, 'z'=000},还可以用 {'a'=0, 'x'=11, 'u'=100, 'z'=101},三套编码都可以把原文压缩到 14 个字节。但是 {'a'=0, 'x'=01, 'u'=011, 'z'=001} 就不是哈夫曼编码,因为用这套编码压缩得到 00001011001001 后,解码的结果不唯一,"aaaxuaxz" 和 "aazuaxax" 都可以对应解码的结果。本题就请你判断任一套编码是否哈夫曼编码。 输入格式: 首先第一行给出一个正整数 N(2≤N≤63),随后第二行给出 N 个不重复的字符及其出现频率,格式如下: c[1] f[1] c[2] f[2] ... c[N] f[N] 其中c[i]是集合{'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}中的字符;f[i]是c[i]的出现频率,为不超过 1000 的整数。再下一行给出一个正整数 M(≤1000),随后是 M 套待检的编码。每套编码占 N 行,格式为: c[i] code[i] 其中c[i]是第i个字符;code[i]是不超过63个'0'和'1'的非空字符串。 输出格式: 对每套待检编码,如果是正确的哈夫曼编码,就在一行中输出"Yes",否则输出"No"。 注意:最优编码并不一定通过哈夫曼算法得到。任何能压缩到最优长度的前缀编码都应被判为正确。 输入样例: 7 A 1 B 1 C 1 D 3 E 3 F 6 G 6 4 A 00000 B 00001 C 0001 D 001 E 01 F 10 G 11 A 01010 B 01011 C 0100 D 011 E 10 F 11 G 00 A 000 B 001 C 010 D 011 E 100 F 101 G 110 A 00000 B 00001 C 0001 D 001 E 00 F 10 G 11 输出样例: Yes Yes No No 在完成这道题之前, 首先要对构建哈夫曼树和哈夫曼编码的框架有一定的理解。

https与http混用出现的问题解决办法

https域下请求http的接口,调用时页面报混用错误。 解决方法:页面端适配当前域名,根据当前地址变化来决定调用https接口或http接口(因为测试环境是http,而生产环境是https)。 后端接口用统一的一个requestmapping前缀。服务器端在nginx端配置该前缀的转发,由nginx负责转发至http请求,转发的地址配置到目的ip的端口号即可

maven之spring-boot-dependencies篇

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> 这个spring-boot-dependencies的作用主要是起到约束的作用,在这个包里面声明了各种版本号,供子项目去引用。spring-cloud-dependencies和spring-cloud-alibaba-dependencies则是去声明cloud和cloud-alibaba组件的版本。具体可以点进去看看就知道了。 type和scope的作用就是为了解决maven 单继承的问题,使用这种方式,子模块不仅可以继承parent标签中的模块,也可以继承dependencyManagement中的其它模块。像这样: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

有关Maven仓库下载pom依赖相关内容。

pom依赖下载不了一直报红,这是由于仓库中没有这个版本,或者权限问题。 这时我们可以进行本地下载。 操作如下: 首先打开你的本地仓库,进入所要下载依赖中,将要下载的jar包放到此路径中,在此路径cmd进入命令行窗口。 进入命令行复制这段代码,把相应的jar包名字,及maven配置,版本修改成自己的就可以了。 mvn install:install-file -Dfile=.\ojdbc14-10.2.0.4.0.jar -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging=jar --settings=C:\Users\Gavin\.m2\setting.xml

C语言归并排序递归与非递归模板

这个我是拿来理解和背诵的,选自于胡凡的《算法笔记》。归并排序是一种nlogn的时间复杂度算法,不断地利用空间倒腾去完成,一般也用在排序算法上。 递归版本 将array数组当前区间[left,right]进行归并排序 void mergeSort(int A[],int left,int right) { if(left<right) { int mid = (left+right)/2; mergeSort(A,left,mid); mergeSort(A,mid+1,right); merge(A,left,mid,mid+1,right); } } void merge(int A[],int L1,int R1,int L2,int R2){ int i = L1,j = L2;//i指向A[L1],j指向A[l2] int temp[maxn],index = 0;//temp临时存放合并后的数组,index为其下标 while(i<=R1&& j<=R2){ if(A[i] <= A[j]){ //如果A[i] <= A[j] temp[index++] = A[i++]; //将A[i] 加入序列temp }else{ //如果A[i] > A[j] temp[index++] = A[j++]; //将A[j] 加入序列temp } } while(i<=R1) temp[index++] = A[i++]; while(j<=R2) temp[index++] = A[j++]; for(i=0;i<index;i++){ A[L1+i] = temp[i];//将合并后的序列赋值回数组A } } int main() { int A[5] ={3,2,1,5,0}; mergeSort(A,sizeof(A)/sizeof(int)); for(int i =0;i<5;i++) printf("

3.STM32CubeMX学习笔记三:STM32的中断系统与外部中断基础

一、STM32的中断系统 理解中断、 中断源、 中断向量、中断优先级、 中断服务函数…等基础概念。 ARM Cortex M3内核支持256个中断,包括16个内核中断和240个外设中断,拥有256个中断优先级别。 STM32的中断通道可能会由多个中断源共用。这就意味着,某一个中断服务函数也可能被多个中断源所共用。所以,在中断服务函数的入口处,需要有一个判断机制,用以辨别是那个中断触发了中断。 STM32微处理器的内核中有一个NVIC(嵌套向量中断控制器)的设备,它对中断进行统一的协调和控制,其中最主要的工作就是控制中断通道的使能和确定中断的优先级。 STM32中有2个优先级的概念:抢占优先级和响应优先级,每个中断都需要指定这两种优先级。 如果两个抢占优先级相同的中断同时到达,NVIC会根据他们的响应优先级高低来决定先处理哪一个。如果两个同时到达的中断的抢占优先级和响应优先级都相等,则根据中断的自然排位顺序来决定响应哪一个。 二、STM32的外部中断 外部中断EXTI是STM32微处理器实时处理外部事件的一种机制,由于中断请求主要来自GPIO端口的引脚,所以称为外部中断。 STM32F013微处理器有19个能产生事件/中断请求的边沿检测器,每个输入线可以独立地配置成输入类型(脉冲或挂起)和对应的触发事件(上升沿、下降沿或双边沿触发),也可以独立地屏蔽。 EXTI0~EXTI15:GPIO端口引脚。 EXTI16:PVD输出,可编程电压监测。 EXTI17:RTC闹钟。 EXTI18:USB唤醒。 STM32芯片有16个外部中断源EXTIO~EXTI15,分别对应着7个中断向量,也就是对应着7个中断服务函数。 EXTIO、EXTI1、EXT12、EXTI3、EXTI4:专用。 EXTI5~EXTI9:共用。 EXTI10~EXTI15:共用。 EXTI0的连接引脚是:PAO~PG0,即每个端口组的0号引脚。 以此类推。 外部中断触发条件:上升沿触发、下降沿触发或双边沿触发。 注意:不能配置成高电平触发和低电平触发。 三、外部中断的程序设计思路 传统STM32外部中断设计步骤: 【1】将GPIO初始化为输入端口。 【2】配置相关I/O引脚与中断线的映射关系。 【3】设置该I/O引脚对应的中断触发条件。 【4】配置NVIC,并使能中断。 【5】编写中断服务函数。 基于STM32CubeMX的外部中断设计步骤: 【1】在STM32CubeMX中指定引脚,配置中断初始化参数。 选择GPIO引脚的功能,设置中断信号触发条件,使能NVIC对应的中断通道。 【2】重写该I/O引脚对应的中断回调函数。 四、外部中断初始化配置 【1】将GPIO设置为:GPIO_EXTI功能。 【2】设置中断触发条件:上升沿、下降沿、上升沿或下降沿。 【3】使能相关的NVIC通道。(千万别忘了) 五、外部中断初始化函数剖析 //外部中断初始化相关的操作在gpio.c文件中的MX_GPIO_Init()函数完成。 void MX_GPIO_Init(void) { /*===================== 此处省略引脚设置的基本代码。 ======================*/ /* EXTI interrupt init 外部中断初始化*/ HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0); //设置EXIT3中断的优先级 HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能EXIT3中断通道 HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI4_IRQn); } 六、外部中断服务函数的编写 七、练习:外部中断信号控制LED灯开关 在STM32F103ZET6开发板上,利用STM32CubeMX和Keil5协同开发,完成以下功能: (1)将KEY1,即 PE3置为外部中断输入, 下降沿触发,在中断服务函数中, 切换LED1的开关状态。 (2)将KEY0,即 PE4置为外部中断输入, 上升沿触发,在中断服务函数中, 切换LED0的开关状态。 由于在前两节的学习笔记中,已经介绍过开发板的原理图,与上拉电阻的原理,因此在此处,查找原理图部分不做详细介绍。详见:基于HAL库开发的STM32学习笔记二:STM32的按键开发基础

数据结构——使用C语言 折半查找

1.递归结构折半查找 int BSearch(int a[], int x, int low, int high) { int mid; if (low > high) return -1; mid = (low + high) / 2; if (a[mid] == x) return mid; else if (x > a[mid]) return BSearch(a, x, mid + 1, high); else return BSearch(a, x, low, mid - 1); } 2.循环结构折半查找 循环结构折半查找利用顺序表完成 需声明如下函数 /*初始化顺序表*/ void ListInitiate(seqlist* s); /*插入顺序表元素*/ //插入成功返回1,否则返回0 int ListInsert(seqlist* s, int index,int x); /*折半查找*/ //查找成功返回该元素位置,否则返回-1 int BinarySearch(seqlist s, DataType x); 对顺序表的定义在只之前文章已经写过,此处只介绍折半查找算法

IntersectionObserver(交叉观察器)

文章目录 1.IntersectionObserver1.1 observe 方法1.2 unobserve 方法1.3 disconnect 方法1.4 takeRecords 方法1.5 callback 参数1.6 options 2. IntersectionObserverEntry 对象3. 图片懒加载4. 元素吸顶、吸底5. 加载更多 1.IntersectionObserver IntersectionObserver可以用来自动监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器" const io = new IntersectionObserver(callback, option); IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数: callback:可见性发现变化时的回调函数option:配置对象(可选)。 构造函数的返回值是一个观察器实例。实例一共有4个方法: observe:开始监听特定元素unobserve:停止监听特定元素disconnect:关闭监听工作takeRecords:返回所有观察目标的对象数组 1.1 observe 方法 该方法需要接收一个target参数,值是Element类型,用来指定被监听的目标元素 // 获取元素 const target = document.getElementById("dom"); // 开始观察 io.observe(target); 1.2 unobserve 方法 该方法需要接收一个target参数,值是Element类型,用来指定停止监听的目标元素 // 获取元素 const target = document.getElementById("dom"); // 停止观察 io.unobserve(target); 1.3 disconnect 方法 该方法不需要接收参数,用来关闭观察器 // 关闭观察器 io.disconnect(); 1.4 takeRecords 方法 该方法不需要接收参数,返回所有被观察的对象,返回值是一个数组

5.类和对象的创建

文章目录 1.面向过程和面向对象的理解2.类和对象的理解3.类和对象的创建 1.面向过程和面向对象的理解 1)二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。 2)面向对象的三大特征:封装、继承、多态。 2.类和对象的理解 1)类:是对一类事物的描述,是抽象的、概念上的定义。 2)对象:是实际存在的该类的事物的每个个体,因而也称为实例。 3.类和对象的创建 1)步骤: ①创建类,设计类成员。 ②创建类对象。 ③通过“对象.属性” 或 “对象.方法” 调用对象的结构。 2)如果创建一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的)。意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。 //1.创建类,设计类成员 class Person{ //属性 String name; int age = 1; boolean isMsle; //方法 public void eat() { System.out.println("人可以吃饭"); } public void sleep() { System.out.println("人可以睡觉"); } public void talk(String language) { System.out.println("人可以说话" + language); } } public class PersonTest { public static void main(String[] args) { //2.创建类对象 //创建Person类的对象 Person p1 = new Person(); //3.

Mac OS 终端命令失效

如果你遇到了如下问题 (或类似问题) zsh: command not found: open 可以尝试在终端运行 1 PATH=/bin:/usr/bin:/usr/local/bin:${PATH} 2 export PATH 这个时候你再尝试,可能发现命令行已经生效了,但这只是暂时的,我们继续输入 3 (这一步是防止.bash_profile不存在,如你确定已存在,直接执行第四步) touch .bash_profile 4 这一步会应该打开一个文件 open .bash_profile 5 将1、2步的内容粘贴至文件末尾处,随后保存,关闭 6 回到命令行 (这一步是使刚才的配置生效) source .bash_profile 这时候你再尝试,大功告成啦

后端接口通过nginx代理后,表格下载到一半就中断了的原因

今天在导出表格时,表格下载一半就无法下载了 nginx配置代理如下 location /api/ { proxy_pass http://127.0.0.1:8088; } 经查验,发现在代理时默认情况下proxy_max_temp_file_size值为1024MB,也就是说后端服务器的文件不大于1G都可以缓存代理硬盘中,这里由于缓存满了,所以导致下载中断。 因此修改配置文件如下 location /api/ { proxy_pass http://127.0.0.1:8088; proxy_max_temp_file_size 0k; } 设置成0表示不缓存。

计算机网络通信--交换机、路由器

转载自 如果让你来设计网络 你是一台电脑,你的名字叫 A 很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。 直到有一天,你希望与另一台电脑 B 建立通信,于是你们各开了一个网口,用一根网线连接了起来。 用一根网线连接起来怎么就能"通信"了呢?我可以给你讲 IO、讲中断、讲缓冲区,但这不是研究网络时该关心的问题。 如果你纠结,要么去研究一下操作系统是如何处理网络 IO 的,要么去研究一下包是如何被网卡转换成电信号发送出去的,要么就仅仅把它当做电脑里有个小人在开枪吧~ 反正,你们就是连起来了,并且可以通信。 第一层 集线器hub 有一天,一个新伙伴 C 加入了,但聪明的你们很快发现,可以每个人开两个网口,用一共三根网线,彼此相连。 随着越来越多的人加入,你发现身上开的网口实在太多了,而且网线密密麻麻,混乱不堪。(而实际上一台电脑根本开不了这么多网口,所以这种连线只在理论上可行,所以连不上的我就用红色虚线表示了,就是这么严谨哈哈~) 于是你们发明了一个中间设备,你们将网线都插到这个设备上,由这个设备做转发,就可以彼此之间通信了,本质上和原来一样,只不过网口的数量和网线的数量减少了,不再那么混乱。 你给它取名叫集线器,它仅仅是无脑将电信号转发到所有出口(广播),不做任何处理,你觉得它是没有智商的,因此把人家定性在了物理层。 由于转发到了所有出口,那 BCDE 四台机器怎么知道数据包是不是发给自己的呢? 首先,你要给所有的连接到交换机的设备,都起个名字。原来你们叫 ABCD,但现在需要一个更专业的,全局唯一的名字作为标识,你把这个更高端的名字称为 MAC 地址。 你的 MAC 地址是 aa-aa-aa-aa-aa-aa,你的伙伴 b 的 MAC 地址是 bb-bb-bb-bb-bb-bb,以此类推,不重复就好。 这样,A 在发送数据包给 B 时,只要在头部拼接一个这样结构的数据,就可以了。 B 在收到数据包后,根据头部的目标 MAC 地址信息,判断这个数据包的确是发给自己的,于是便收下。 其他的 CDE 收到数据包后,根据头部的目标 MAC 地址信息,判断这个数据包并不是发给自己的,于是便丢弃。 虽然集线器使整个布局干净不少,但原来我只要发给电脑 B 的消息,现在却要发给连接到集线器中的所有电脑,这样既不安全,又不节省网络资源。 第二层 交换机 如果把这个集线器弄得更智能一些,只发给目标 MAC 地址指向的那台电脑,就好了。 虽然只比集线器多了这一点点区别,但看起来似乎有智能了,你把这东西叫做交换机。也正因为这一点点智能,你把它放在了另一个层级,数据链路层。 如上图所示,你是这样设计的。 交换机内部维护一张 MAC 地址表,记录着每一个 MAC 地址的设备,连接在其哪一个端口上。 MAC 地址端口bb-bb-bb-bb-bb-bb1cc-cc-cc-cc-cc-cc3aa-aa-aa-aa-aa-aa4dd-dd-dd-dd-dd-dd5 假如你仍然要发给 B 一个数据包,构造了如下的数据结构从网口出去。

SSM项目报错Could not autowire. No beans of ‘xxxService‘ type found.

前言: 在Java开发过程中我们经常需要用到@Autowired这个注解,我们的正常使用会在Controller层注入service,在service中注入mapper。本人在使用SSM框架开发时遇到了无法注入bean的问题,现已解决供大家参考。由于每个人的错误原因可能不同,因此我把我解决好的方法放在了第一个。 1.@Autowired后加参数(required=false)(本人最终靠此方法解决) @Autowired(required=true):当使用@Autowired注解的时候,默认就是表示注入的时候,该bean必须存在,否则就会注入失败。 @Autowired(required=false):表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。 未加之前: 加之后: 2.降低 Autowired 检测的级别,将 Severity 的级别由之前的 error 改成 warning 或其它可以忽略的级别 3.导入 @Service 包的时候导入包错误造成的: spring auto scan 配置,在编辑情况下,无法找不到对应的bean,于是提示找不到对应 bean 的错误。常见于 mybatis 的 mapper,如下: <!-- mapper scanner configurer --> <bean id="mapperScannerConfig" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.adu.spring_test.mybatis.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> 解决办法: 错误导包: import com.alibaba.dubbo.config.annotation.Service; 正确导包: import org.springframework.stereotype.Service;

VS2017编译libcurl(zlib+openssl+libssh2)

所有步骤全程亲自操作,成功编译并可复现每一步。 文章目录 前言一、 zlib编译二、 OpenSSL 3.0.1编译三、 libssh2编译四、 curl编译总结 前言 在UE4.26.2中内置了libcurl库,版本是curl-7.55.1及之前的,项目中使用该库后出现几百个漏洞,在最新版本中已将漏洞修复,故更新libcurl最新版本库。使用libcurl库需要zlib和openssl和libssh2的支持,在网上看了其它大神写的文章,有的使用已编译好的.lib,有的步骤有缺失,现记录libcurl库及需它支持的库的完整编译方案。 提示:以下是本篇文章正文内容,下面案例可供参考 一、 zlib编译 环境准备 zlib:zlib-1.2.11 http://www.zlib.net/ 步骤 1) 导入zlibvc.vcxproj (目标所在目录: xxx\zlib-1.2.11\contrib\vstudio\vc14) 2) 更改zlibvc部分属性: 配置管理器为:release x64 【常规】->【目标文件名】内容改为zlib_zip1211; 【C/C++】->【预处理器定义】更改ZLIB_WINAPI–>ZLIB_DLL (为了编译出的dll可以使用zip相关接口) 3) 编译 编译成功 lib库目录如下: include包含目录如下: 二、 OpenSSL 3.0.1编译 环境准备 openssl:OpenSSL 3.0.1 直接从github上clone仓库:git clone https://github.com/openssl/openssl.git zlib:zlib-1.2.11 (上述zlib编译结果) 步骤 编译前仔细阅读以下INSTALL 和 NOTES.WIN 这两个文件,版本1.0.2对应的文件叫 INSTALL.W32和INSTALL.W64,也不一样,编译过程基本上都在这两个文件里面。 1)安装perl和汇编工具,按照NOTES.WIN里面推荐的去安装 ActivePerl和nasm,一般安装最新版即可。 2)以管理员身份打开VS2017的命令行程序,本文编译x64版本环境,打开 “适用于VS 2017的X64本机工具命令提示”。在命令行中切换至OpenSSL 3.0.1解压根目录,按照如下命令开始编译 >> perl Configure VC-WIN64A --prefix=F:\soft\curl7.80_3\openssl\build --with-zlib-include=F:\soft\curl7.80_3\zlib-1.2.11 --with-zlib-lib=F:\soft\curl7.80_3\zlib-1.2.11\contrib\vstudio\vc14\x64\ZlibDllRelease\zlibwapi.lib zlib-dynamic # VC-WIN64A:64位版本编译 # --prefix:编译结果安装目录 # --with-zlib-include:zlib库包含目录 # --with-zlib-lib:zlib库导入库文件 # zlib-dynamic:zlib以dll形式引入 >> nmake >> nmake test >> nmake install >> nmake clean nmake编译配置

H5项目友盟埋点上报实践

使用友盟平台做H5统计分析的话可以使用小程序统计下的H5统计分析 使用步骤: 1、注册/登录平台 2、创建H5应用 3、集成SDK 4、查看统计数据 这里主要需要开发支持的步骤是集成SDK这一步骤,下面来具体讲一下这一步 首先在我们创建平台应用成功后,可以拿到该应用生成的AppKey,然后就可以进行集成SDK的操作在项目的最外层(文档上是在页面head标签内)加入集成代码,确保aplus_queue不被污染 // 友盟统计初始化 (function(w, d, s, q, i) { w[q] = w[q] || []; var f = d.getElementsByTagName(s)[0],j = d.createElement(s); j.async = true; j.id = 'beacon-aplus'; j.src = 'https://d.alicdn.com/alilog/mlog/aplus/' + i + '.js'; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'aplus_queue', '203467608'); //集成应用的appKey aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['appKey', 'xxxxxxx'] }); 配置好之后就可以进行事件埋点,在进行埋点之前,首先介绍一下action的各个属性值的用法 3.1 setMetaInfo 用于变更SDK的默认设置 const {aplus_queue} = window; aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: [metaName, metaValue, mode] }); /** metaName 为可配置的元数据项,具体可配置选项及取值可参考下文链接 * metaValue 为对应的元配置项取值 * metaName 及 metaValue 对应表:https://developer.

yolov5网络结构学习

(注:原文链接是深入浅出Yolo系列之Yolov5核心基础知识完整讲解,我觉得这篇文章写的很好,所以自己手敲了一遍,并修改了很小一部分的细节,或者加了一些来自作者另一篇文章深入浅出Yolo系列之Yolov3&Yolov4&Yolov5核心基础知识完整讲解中的内容) (更:参考yolov5深度可视化解析,从loss设计和anchor生成两方面深入理解yolov5的核心思想) 1. yolov5 网络架构 上图是yolov5s的网络结构,它是yolov5系列中深度最小、特征图宽度最小的网络。后面的m、l、x都是在此基础上不断加深、加宽的。 网络主要分为输入端、Backbone、Neck、Prediction四个部分。 它和yolov3主要不同的地方: (1)输入端:Mosaic数据增强、自适应锚框计算、自适应图片缩放 (2)Backbone:Focus结构、CSP结构 (3)Neck:FPN+PAN结构 (4)Prediction:GIOU_Loss 下面从这四个方面入手进行比较,同时和yolov4进行对比。 2.输入端 (1)Mosaic数据增强 yolov5的输入端采用了和yolov4一样的Mosaic数据增强的方式。 Mosaic数据增强提出的作者也是来自yolov5团队的成员。它是采用4张图片,随机缩放、随机裁剪、随机排布的方式进行拼接,对于小目标的检测效果还是很不错的。 为什么要进行Mosaic数据增强呢? 在平时项目训练时,小目标的AP一般比中目标和大目标低得多。而coco数据集中也包含大量的小目标,但比较麻烦的是小目标的分布并不均匀。 首先看下小、中、大目标的定义: 可以看到小目标的定义是目标框的长宽0*0~32*32之间的物体。但是在coco中的分布是怎么样的呢?看下表: 在整体的数据集中,小、中、大目标的占比并不均衡。上表中,coco数据集中小目标占比达到41.4%,数量比中目标和大目标都要多,但是在所有的训练集的图片中,只有52.3%的图片有小目标而中目标和大目标的分布相对来说更加均匀一些。 针对这种状况,yolov4的作者采用了Mosaic数据增强的方式。 主要有几个优点: 丰富数据集:随机使用4张图片,随机缩放,再随机分布进行拼接,大大丰富了检测数据集,特别是随机缩放增加了很多小目标,让网络的鲁棒性更好。减少GPU:可能会有人说,随机缩放,普通的数据增强也可以做,但作者考虑到很多人可能只有一个GPU,因此Mosaic增强训练时,可以直接计算4张图片的数据,使得Mini-batch大小并不需要很大,一个GPU就可以达到比较好的效果。 (2)自适应锚框计算 在yolo算法中,针对不同的数据集,都会有初始设定长宽的锚框。 在网络训练中,网络在初始锚框的基础上输出预测框,进而和真实框groundtruth进行比对,计算两者差距,再反向更新,迭代网络参数。 因此初始锚框也是比较重要的一部分,比如yolov5在coco数据集上初始设定的锚框: 在yolov3、yolov4中,训练不同的数据集时,计算初始锚框的值是通过单独的程序运行的。 但yolov5中,将此功能嵌入到代码中,每次训练时,自适应的计算不同训练集中的最佳锚框值。 当然,如果觉得计算的锚框效果不是很好,也可以在代码中将自动计算锚框功能关闭。 上面的代码在train.py中,store_true表示触发时为真,不触发则为假,所以随意传入一个值都会默认true,即开启noautoanchor,关闭自动计算锚框。 (3)自适应图片缩放 在常用的目标检测算法中,不同的图片长宽也不相同,因此常用的方式是将原始图片统一缩放到一个标准尺寸,在送入检测网络中。 比如yolo算法中常用416*416,608*608等尺寸,比如对下面800*600的图像进行缩放。 但yolov5代码中对此进行了改进,也是yolov5推理速度能够很快的一个不错的trick。 作者认为,在项目实际使用时,很多图片的长宽比不同,因此缩放填充后,两端的黑边大小都不同,而如果填充的比较多,则存在信息冗余,影响推理速度。 因此在yolov5的代码中datasets.py的letterbox函数进行了修改,对原始图像自适应的添加最少的黑边。 图像高度上两端的黑边变少了,在推理时,计算量也会减少,即目标检测速度会得到提升。 通过这种简单的改进,推理速度得到了37%的提升,可以说效果很明显。 但是如何进行计算的呢? 第一步:计算缩放比例 原始缩放尺寸是416*416,都除以原始图像的尺寸后,可以得到0.52和0.69两个缩放系数,选择小的缩放系数。 第二步:计算缩放后的尺寸 原始图片的长宽都乘以最小的缩放系数0.52,宽变成了416,而高变成了312。 第三步:计算黑边填充数值 将416-312=104,得到原本需要填充的高度。再采用numpy中np.mod取余数的方式,得到8个像素,再除以2,即得到图片高度两端需要填充的数值。 此外,需要注意的是: 这里图中填充的是黑色,即(0,0,0),而yolov5中填充的是灰色,即(114,114,114),都是一样的效果。训练时没有采用缩减黑边的方式,还是采用传统填充的方式,即缩放到416*416大小。只是在测试、使用模型推理时,才采用缩减黑边的方式,提高目标检测,推理的速度。为什么np.mod函数的后面用32?因为yolov5的网络经过5次下采样,而2的5次方,等于32。所以至少要去掉32的倍数,再进行取余。 3.Backbone (1)Focus结构 源码如下: Focus结构如下,在yolov3、yolov4中并没有这个结构,其中比较关键是切片操作。 比如上图右边的切片示意图,4*4*3的图像切片后变成2*2*12的特征图。 以yolov5s的结构为例,原始608*608*3的图像输入Focus结构,采用切片操作,先变成304*304*12的特征图,再经过一次32个卷积核的卷积操作,最终变成304*304*32的特征图。 需要注意的是:yolov5s的Focus结构最后使用了32个卷积核,而其他三种结构,使用的数量有所增加。 (2)CSP结构 yolov4网络结构中,借鉴了CSPNet的设计思路,在主干网络中设计了CSP结构。 yolov5与yolov4不同点在于,yolov4中只有主干网络使用了CSP结构。 而yolov5中设计了两种CSP结构,以yolov5s网络为例,CSP1_X结构应用于Backbone主干网络,另一种CSP2_X结构则应用于Neck中。 可以看一下yolov4中的CSPNet——CSPDarknet53: CSPDarknet53是在yolov3主干网络Darknet53的基础上,借鉴2019年CSPNet的经验,产生的Backbone结构,其中包含了5个CSP模块。 每个CSP模块前面的卷积核的大小都是3*3,stride=2,因此可以起到下采样的作用。 因为Backbone有5个CSP模块,输入图像是608*608,所以特征图变化的规律是:608->304->152->76->38->19 经过5次CSP模块后得到19*19大小的特征图。 而且,作者只在Backbone中采用了Mish激活函数,网络后面仍然采用Leaky_relu激活函数。 为什么要采用CSP模块呢? CSPNet全称是Cross Stage Paritial Network,主要从网络结构设计的角度解决推理中计算量很大的问题。

nn.parallel.DistributedDataParallel报错,一块GPU时不报错

Warning: Traceback of forward call that caused the error: File "train_YnetV2_dist.py", line 273, in <module> pred_waypoint_map,pred_traj_map, pred_traj, pred_goal = model(feature_input,V_obs,A_obs,gt_waypoint_map) File "/gs/home/wangrp/anaconda/envs/w01/lib/python3.6/site-packages/torch/nn/modules/module.py", line 550, in __call__ result = self.forward(*input, **kwargs) File "/gs/home/wangrp/anaconda/envs/w01/lib/python3.6/site-packages/torch/nn/parallel/distributed.py", line 447, in forward output = self.module(*inputs[0], **kwargs[0]) File "/gs/home/wangrp/anaconda/envs/w01/lib/python3.6/site-packages/torch/nn/modules/module.py", line 550, in __call__ result = self.forward(*input, **kwargs) File "/gs/home/wangrp/Multimodal1229_noaug/model/model_heatmapv2_dist.py", line 307, in forward inter_feat, adjA = self.inter_features(V_obs, A_obs) File "/gs/home/wangrp/Multimodal1229_noaug/model/model_heatmapv2_dist.py", line 342, in inter_features inter_represent = self.

如何裁剪动态图片?教你三步快速裁剪GIF

图片成为我们日常生活工作中的必备品了,在使用图片的时候自然少不了需要处理图片的情况。比如,当我们从网上下载了一张GIF动态图片,但是图片上却有水印,很影响图片的美观,那么我们应该如何裁剪GIF动图的水印呢?这时候,大家就可以使用gif裁剪工具来裁剪GIF动图中的水印,具体的操作方法一起来看看吧! 打开GIF5工具网,点击gif裁剪功能,支持添加网络图片(复制网络图片的链接地址,点击添加图片即可)和上传本地动图两种方式。 图片上传之后,可以拉动上下左右裁剪框的四个边角来选择裁剪的位置,也可以输入想要裁剪的尺寸,生成指定尺寸的裁剪框,拖动裁剪框来选择位置,点击裁剪按钮。 裁剪完成后,点击下载按钮,下载图片到电脑即可。 GIF图片裁剪完成后大家就可以看到动图中的水印被裁掉了,非常的方便无需下载任何软件在线即可操作。同时,GIF5工具网还支持视频转GIF制作、图片合成GIF、GIF压缩以及GIF拼图的功能,如果觉得好用还可以推荐给身边的小伙伴使用。

程序员最真实现状

最近,在刷某乎看到这样一个提问: 程序员为什么要一直写 bug ,不能一次性写好吗? 我满脸问号,为什么会有这样的问题?看到这个问题,我就想发出这几个疑问(对不起, etc 自动抬杠了): 写字为什么要一直写错用橡皮擦/透明胶,不能一次性些好吗?人为什么要经常吃饭,不能一次性吃饱吗?情侣为什么要分手,不能直接恋爱结婚吗?…… 其实人写出 bug 是必然的,毕竟人无完人,总会有注意力不集中的时候,哪怕是水平高的程序员也难以避免,就比如之前讨论度很高的漏洞——Apache Log4j 2 远程代码执行漏洞。 只不过有的 bug 致命,有的 bug 危害小罢了! 这里,我忍不住想分享这位程序员部分“断送未来”的故事(当做一个娱乐消遣,请勿模仿!内容出自某乎:拂髯客)。 事故一 毕业了直接进了某运营商工作,有一天接了一个任务,要求写一个群发通知短信的代码。当天大脑短路没改代码,接上了生产环境直接撸,代码循环所有运营商内手机号发送最新优惠活动。 编码→测试→完蛋,写成死循环了。发现问题了之后及时停止程序,给数据库中的第一个号码发了一千条短信。 第二天失业。 事故二 进到了某智能卡卡商公司开发食堂智能卡,代码测试都很正常 就是并发承载能力一般,不过同时刷个四五笔还是可以的。 本着实用主义精神、性能无所谓能用就行的态度,以及咨询了食堂情况,信心满满下发到某高校食堂直接使用。 第二天失业,因为学生发现在食堂吃饭刷了一定笔数之后,吃饭突然不花钱了。 看完这两个事故,你是不是秉着吃瓜的心态?所以,bug 虽然危险需谨慎,但也并不一定能 100% 避免的,更别说刚出社会、没有实战项目经验的应届毕业生了。 大家看到 bug 都会窝火,有的人会抱怨“这是谁写的”,好巧不巧,写出这个 bug 的人就是自己,小丑就是自己。 这几张图,也完美地展现了程序员发现 bug 的心境(头皮发麻): 这时候,我就不得不再提一次 Apache Log4j 2 远程代码执行漏洞。 这个漏洞在 2021 年 12 月 10 日被公开,让很多安全工程师、安全厂商、各企业的安全人员都难以入眠,立刻进入“战斗防御状态”。 蓝桥云课免费复现了这个漏洞,介绍该漏洞原理、利用方式、挖掘方式和修复方法。这个实验提供了可动手操作的在线实验环境,帮助你更加深刻的理解该漏洞。 如果你想深入理解 Apache Log4j 2 远程代码执行漏洞,免费学习哦~ Apache Log4j 2 远程代码执行漏洞详解

Markdown常用数学符号的 (LaTeX) 表示方法

(以下内容主要摘自“一份不太简短的 LATEX2e 介绍”) 1、指数和下标可以用^和_后加相应字符来实现。比如: 2、平方根(square root)的输入命令为:\sqrt,n 次方根相应地为: \sqrt[n]。方根符号的大小由LATEX自动加以调整。也可用\surd 仅给出 符号。比如: 3、命令\overline 和\underline 在表达式的上、下方画出水平线。比如: 4、命令\overbrace 和\underbrace 在表达式的上、下方给出一水平的大括号。 5、向量(Vectors)通常用上方有小箭头(arrow symbols)的变量表示。这可由\vec 得到。另两个命令\overrightarrow 和\overleftarrow在定义从A 到B 的向量时非常有用。 6、分数(fraction)使用\frac{…}{…} 排版。一般来说,1/2 这种形式更受欢迎,因为对于少量的分式,它看起来更好些。 7、积分运算符(integral operator)用\int 来生成。求和运算符(sum operator)由\sum 生成。乘积运算符(product operator)由\prod 生成。上限和下限用^ 和_来生成,类似于上标和下标。 以下提供一些常用符号的表示方法

使用el-upload文件缩略图 删除指定图片

饿了吗ui的文档上面的删除只给了一个方法,没有具体的操作。 百度上的有些方法是只能删除全部图片 //获取上传组件的ref删掉他的全部数据 this.$refs.upload.clearFiles() 但是后面发现这样写不行,对用户太不友好了 所以后面又用这一种写法实现了指定删除 使用ref的uploadFiles 获取当前组件中的图片数据 let arr = this.$refs.upload.uploadFiles 在通过indexOf来获取当前指定图片的索引值 let index = arr.indexOf(file) 最后通过splice删除 console.log(index) arr.splice(index, 1) 主要的逻辑代码 js handleRemove(file) { //使用indexOf把点击的图片的索引位置找出来删掉 let arr = this.$refs.upload.uploadFiles let index = arr.indexOf(file) console.log(index) arr.splice(index, 1) }, 组件 <el-upload action="#" list-type="picture-card" :on-change="handleChange" :before-upload="beforeAvatarUpload" :auto-upload="false" :file-list="fileList" ref="upload" :on-exceed="masterFileMax" :limit="limit" > <i slot="default" class="el-icon-plus"></i> <div slot="file" slot-scope="{ file }"> <!--显示上传图片 --> <img class="el-upload-list__item-thumbnail img" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span v-if="

预备内容 STM32CubeMX安装与快速入门

STM32CubeMX软件的安装分为三个部分: 1.安装JRE,JAVA运行环境。 2.安装STM32CubeMX 软件。 3.安装芯片的固件支持包,也就是HAL库。 4.Keil 5 MDK版本(最好开启自动补全功能) 注:STM32Cubemx是提供一个图形化的配置基础架构(如引脚一类的公共部分),具体编写还是需要Keil软件

Java中的clone()方法

clone()方法的约定 Cloneable接口的目的是作为对象的一个混合接口,表明这样的对象允许克隆(clone),但是这个接口却没有定义clone(),这是它的缺陷:无法约束子类实现clone()方法。Object定义了一个受保护的clone()方法。Cloneable虽然没有定义clone()方法,但是却影响了Object.clone()方法的行为:如果一个类实现了Cloneable,调用Object的clone()就会返回该对象的逐域拷贝,否则抛出CloneNotSupportedException。这真是一种非常规的用法,Cloneable接口没有规定实现类的视图,却改变了父类受保护方法的行为。调用clone()会创建并返回对象的拷贝,看看JDK文档中对clone()方法的约定: (1)x.clone() != x; 克隆对象与原对象不是同一个对象 (2)x.clone().getClass() == x.getClass(); 克隆的是同一类型的对象 (3)x.clone().equals(x) == true,如果x.equals()方法定义恰当的话 注意,上面的三条规则要求不是绝对的,一般来说前两条是必需的,第三个也应该尽量遵守。 实现Cloneable接口的类和其所有超类都必需遵守一个复杂、不可实施、且没有文档说明的协议,由此得到一种语言之外的机制:无需调用构造器就可以创建对象。然而,“不调用构造器”的规定有些僵硬,行为良好的clone()方法可以调用构造器创建对象,比如final类,它不会有子类,所以在它的clone()方法中调用构造器创建对象是一种合理的选择。 使用clone()的规则 “如果你覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone()而得到的对象”,这是使用clone()方法的规则,如果不遵守这条规则,在clone()方法中调用了构造器,那么就会得到错误的类。如代码所示: class A implements Cloneable { //类A的clone()直接调用构造器 public A clone() { return new A(); } } class B extends A { public B clone() { try { return (B) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } } } 类 B 的 clone() 方法就不会得到正确的对象,因为 super .clone() 返回的是使用 A 的构造器创建的类 A 的对象。如果类 B 的 clone 方法想得到正确的对象,那么 A 的 clone 方法应该这样写:

C语言--打印正金字塔和倒金字塔

举个栗子 如 我们要打10层的金字塔 #include<stdio.h> #define n 10 int main() { int i,j,k; for(i=1;i<=n;i++) { for(k=i;k<n;k++) { printf(" "); } for(j=1;j<2*i;j++) { printf("*"); } printf("\n"); } } 然后 我们打印倒叙的金字塔 #include<stdio.h> int main() { int n=10; for(int i=1;i<=n;i++) { for(int j=0;j<i;j++) { printf(" "); } for(int k=i;k<2*n-i-1;k++) { printf("*"); } printf("\n"); } }

String和StringBuffer之间的相互转换

一,String-->StringBuffer String n1="你好呀" ①,StringBuffer s1=new StringBuffer(n1); ②,StringBuffer s2=new StringBuffer(); s2.append(n1); 二,StringBuffer-->String StringBuffer n2=new StringBuffer("我还好"); String s1=n2.toString(); String s2=new String(n2); 补充:三,StringBuffer的构造器 ①,StringBuffer(); 构造一个不带字符的字符串缓冲区,其初始容量为16个字符 ②,StringBuffer(int capacity); 构造一个不带字符,但具有指定初始容量的字符串缓冲区,即对char[]大小进行指定。 ③,StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。 

OpenFeign简介

openFeign概念: OpenFeign 是 Spring Cloud 家族的一个成员, 它最核心的作用是为 HTTP 形式的 Rest API 提供了非常简洁高效的 RPC 调用方式 使用方式: 调用者中加入依赖: <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> </properties> ... <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ... <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> 接口类上:@FeignClient("被调用的服务器名") 启动类上:@EnableFeignClients 然后在需要使用的地方@Resource注入 OpenFeign的用途及实现原理 openfeign的用途:服务发现,负载均衡,服务调用 openfeign的实现原理:基于@EnableFeignClients 将所有被@FeignClient注解的类 注册到容器中。当这些被@FeignClient注解的类被调用时会创建一个动态代理的对象为我们创建被调用类的实例,然后都会被统一转发给 Feign 框架所定义的一个 InvocationHandler , 由该 Handler 完成后续的 HTTP 转换, 发送, 接收, 翻译HTTP响应的工作。

使用VueCLI把项目编译成组件库

在package.json的script段加上: "build:lib": "vue-cli-service build --target lib --dest lib --name myLib src/index.js", 然后在终端输入: npm run build:lib 即可生成UMD模式的js文件。 在需要引用这个组件库的项目的public/index.html文件里加上 <script src="/lib/myLib.js"><script> 在项目的vue.config里设置外部库: configureWebpack: { // 别的设置 externals: { 'myLib': 'myLib' }, // 别的设置 } 好了,现在就可以愉快地使用组件了

C语言关于一行代码可以分两行写的解答

#include<stdio.h> int main(void) { printf("Here's one way to print a "); printf("long string.\n"); printf("Here's another way to print a \ long string.\n"); printf("Here's the newest way to print a " "long string.\n"); printf("谢谢你对我的帮助!"); return 0; } 敲完这个代码后,我明白一行代码怎样才可以分两行写,""之间是不能直接断开的,正确的方式如上所示。

Python入门之 str.format()方法、f-strings(格式化字符串字面值)、字符串拼接和input()函数

print()函数只能输出用指定符号分隔的值,当需要用更多的控制输出格式时,可用str.format()方法、f-strings(格式化字符串字面值)和字符串拼接的方法 1.str.format()方法 可以灵活方便的将待输出的变量格式化为期望的格式 格式为<模板{替换域}字符串>.format(<逗号分隔的参数>) 这个方法的模板字符串由字符串和嵌入在字符串中的一个或多个大括号表示的替换域组成,用来控制修改字符串中嵌入值出现的位置,该方法执行时,会将format()中出现的参数按顺序的填入替换域中,每个替换域可以包含一个位置参数的数字索引,format()中的每个参数的数字序号从0开始,程序运行时,将format()中的参数按数字序号依次填入到替换域中 每个替换域可以包含一个关键字参数的名称,用来接收format()函数中关键字参数的值 2.f-strings(格式化字符串字面值) 使用方法与str.format()方法类似,不同的是去掉了format()部分,在字符串前加引导符号f,将放置str.format()方法中的变量或参数表达式直接填入字符串中的替换域中,在程序执行时,直接在替换域的位置输出变量值或参数表达式的值。 str.format()和f-strings都支持在在大括号中加冒号和“格式限定符”进行输出格式的进一步控制 ":.mf" 控制输出浮点数小数点后m位数字 3.字符串连接:用'+’将多个字符串拼接为一个字符串,若参与拼接的变量为整数或其他数据类型时,需要用str()函数将其转换为字符串类型在与其拼接 input(['提示性文字']) 从标准输入设备(键盘)获取用户输入 无论用户输入的是字符类型还是数值,均返回字符串类型,如果要返回数值类型,则需要进行数值转换

WPS创建多级级联菜单

百度了很多,发现写的有些误导。本文解决多级菜单中的创建错误:“列表源”必须是划定分解后的数据列表,或是对单一行或者一列的引用。 一、需要实现的效果: 数据分为大类和小类,大类为一级下拉菜单,小类为二级菜单。当大类选定后,小类能够依据大类选择的值,进行下拉选择。左图为数据源,右图为实现后的效果 二、具体步骤分三步 1、设定一级菜单数据源:选择大类的多行,或者整个列,然后选择数据->有效性,进行下拉数据源的设定。在设定界面中,选择序列,勾选忽略空值,提供下拉箭头。来源选择一二三(大类)就是$B1:$D1;这样一级菜单OK了 2、二级菜单分两步 2.1 设定二级菜单数据源:选择好数据;选择菜单功能公式->指定;出现指定名称的弹框,勾选首行,然后确定。成功后,通过名称管理器可以发现新增了3行引用关系。 名称管理器新增的数据关联。 2.2 建立二级菜单: 注意:建立二级菜单之前,要先将所有的一级菜单值都选出来,不然会报 然会报 引用失败。 首先选择需要建立二级菜单的表格(小类下的绿框),选择菜单 数据->插入下拉列表 ,选择从单元格选择下拉列表 数据填写 公式 =indirect($B$1:$D$6);公式内的数据选择二级菜单的全部数据。 附:如一级菜单值未全部选择(有留空白)会报错 后记:主要是百度里面多级菜单用的都是数据有效性来操作的,这样就只能创建一行,非常难扩展。后来综合百度多文的描述,实践了下,发现按照如上就可以了。

Java:面向对象(类与对象)

面向对象思想引入:前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。 面向对象思想概述:完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。 在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。 那么什么是面向过程开发呢? 面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。 面向过程的代表语言:C语言 事物: 属性:用来描述事物的信息 行为:用来事物能够做什么 类:是一组相关对象的集合 成员变量:事物的属性 成员方法:事物的行为 定义一个类其实就是定义它的成员变量和成员方法; 对象:是该类事物的具体表现 举例: 类:学生(属性:性别、姓名、年龄、、、、、;行为:学习、吃饭、睡觉、、、、); 对象:班长 类:成员变量:gender,name,age......;成员方法:study(),eat(),sleep()....... 定义成员变量:位置:在类中,方法外;格式:数据类型 变量名 定义成员方法:位置:在类中;格式:修饰符 返回值类型 方法名 参数列表{ 方法体;}; 成员变量与局部变量的区别: 1、定义的位置不同: 成员变量定义在类中,方法外; 局部变量定义在类中,方法内; 2.内存位置也不同: 成员变量在栈内存中; 局部变量在堆内存中; 3.初始化值不同: 成员变量系统会给予默认值; 局部变量系统不会给予默认值,必须在定义的时候赋值,或者在方法使用中赋值,才能使用; 4.生命周期不同: 成员变量随着对象的创建而创建,随着对象的消失而消失; 局部变量随着方法的调用而创建,随着方法的消失而消失; class Bianliang { String name; int age; public void show(int n){ n=10; int a=20; System.out.println(n); System.out.println(a); String name="车厘子"; System.out.println(name); } } public class TestBianliang1{ public static void main(String[] args) { Bianliang s1=new Bianliang(); s1.

Python入门之print()函数

Python利用print()函数将结果输出到标准输出设备(即显示器)上 print()函数主要有以下几个参数 1.print(*objects) *objects:0个或多个输出对象; print() 拥有0个参数,输出换行 print()函数拥有一个参数时,先输出一个参数,在输出一个换行 print()函数拥有用逗号隔开的多个参数时,表示同时输出多个参数,每个参数默认用空格分隔 2.print(*objects,sep=' ') 关键字参数sep:输出多个对象之间的分隔符,默认用空格分隔 关键字参数是指如果有这个参数,必须用sep命名,参数值为字符串类型,就是说sep参数的值必须是放在引号中的字符,没有sep参数时,默认值为空格,即多个输出对象间用空格分隔 3.print(*objects,sep=' ',end='\n') end:结束时的符号,默认每个输出语句用换行符结束 end=' ’ 表示输出语句用空格结束,不换行 end=':’ 表示输出语句用冒号结束,不换行 4.print(*objects,sep=' ',end='\n',file=sys.stdout) file:设置输出设备,默认输出到标准输出设备(显示器) 此时print()函数将输出的数据将不会显示在显示器上,而会在我们显示在我们设置的输出设备上 内容如有问题,欢迎指正~

C语言浮点数的精度丢失

一-----先来看一段代码 #include<stdio.h> int main() { double test=0.1; printf("%.100lf",test); return 0; } 运行结果: 直接从现象说结果:精度丢失由于计算机二进制转化过程中因为比特位过多发生数据的截断导致的,这个结果是可以偏大也可以偏小的。 解释一下:首先要知道二进制转换为十进制的基本方法(除二取余法,乘五取余法等等),最好再了解一下浮点数的存储,这里的0.1就是一个典型的例子,对0.1乘五取余是乘不尽的,那么数据转化成的二进制序列的长度就会超出double的范围。那么多出的数据就会被截断。 二-----如何解决 如果你想的是彻底让屏幕显示出来的是0.1,那么,你不孤单,我一开始也是这样想的,但是这是不可能的。但是这个对我们的实际意义不大,毕竟有效的区段还是够的。 我在这里具体要解决的是两个问题 (1)浮点数的大小比较 #include<stdio.h> int main() { double test=0.1; if(test==(1-0.9)) { printf("正常"); } else { printf("what!!!"); } return 0; } 这段代码会输出“what!”。为什么上面已经说了0.1乘不尽,这里换成0.5是OK的,因为0.5D用二进制表示就是0.1B。每次都考虑乘不尽不烦吗? 解决方案 引入库函数<float.h>里定义的宏DBL_EPSILON。 这里后面的一段英文是他是导致x+n!=x的最小值 (n代表EPSILON伊普西隆,x是任意值) 也就是说,任意一个比EPSILON小的值,你给一个数加上,都不会改变它的值。 那么,比EPSILON小的值引起的精度丢失都是在我们的允许范围内的 #include<stdio.h> #include<float.h> #include<math.h> int main() { double test = 0.1; if (fabs(test - (1 - 0.9)) < DBL_EPSILON) { printf("正常"); } else { printf("what!!!"); } return 0; } 上图有两个点

std::enable_if_t MacOS相关问题: no template named ‘enable_if_t‘ in namespace ‘std‘

Mac 12.0.1在CMake编译某个库时遇到以下问题: error: no template named ‘enable_if_t’ in namespace ‘std’; did you mean ‘boost::enable_if_t’? reason: std::enable_if_t is not supported in C++11,but supported in C++14, so add “-std=c++14” in CMAKE_CXX_FLAGS. ###解决办法:在项目的CMakeLists.txt中添加以下两句 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 分析: std::enable_if_t 是C++14 才支持的写法, C++11 为std::enable_if std::enable_if 大概实现如下: template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; 当 enable_if 的条件为true 时,优先匹配 struct enable_if<true,T> 这个模板,因此会多一个 type 类型, 和T 保持一致。

Python 实现读取文本内容、文件重命名、替换文本内容

代码整体结构: 一、Python实现读取文本内容 # -*- coding: utf-8 -*- """ @date: 2022/01/11 18:40 @author: Anker @file_read.py:python读取文本文件中的内容 """ filepath_current = "./test.txt" # test.txt文件在当前项目工程路径下 filepath_appoint = "C:\\Users\\97571\\Desktop\\test\\test.txt" # test.txt文件在指定路径下 # 方法一: f_open = open(filepath_current, 'r+', encoding='utf-8') # 打开文本文件 content = f_open.read() # 读取文本文件中的内容 print(content) # 打印文本文件中的内容 f_open.close() # 关闭操作 # 方法二: with open(filepath_current, 'r+', encoding='utf-8') as file_read: # 打开文本文件,并读取内容 while True: lines = file_read.readline() # 读取整行数据 if not lines: # 判断是否为空,如果有空,在break break print(lines) 运行结果:

Linux c 基于UDP实现简单的多人聊天室

1.数据报格式套接字 数据报格式套接字(代码为SOCK_DGRAM),采用此方法计算机只管发送数据而不去验证发送数据的正确性,不论传输是否被接收,数据流是否有丢失,都不再重新发送,特征如下: 1.强调快速传输而非传输顺序; 2.传输的数据可能丢失也可能损毁; 3.限制每次传输的数据大小; 4.数据的发送和接收是并发的。 总之,数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。 以上总结于套接字有哪些类型?socket有哪些类型? 2.采用UDP协议 而数据报套接字采用的是(User Datagram Protocol)协议,本次聊天室采用UDP协议虽然可能会导致数据丢失,但是聊天并不去强调内容的正确性,而应该强调实时性,并且数据丢失只是小概率事件。 3.实现 流程: server 接收来自client的消息并转发给其他client, 并且自身也能发布公告 client 发送消息给server,接收来自server的信息(包括群发内容和公告) server.c 在服务器定义了两个结构体,INFO,usr_list分别去接受客户端的信息和保存接入客户端的信息。 typedef int protocol; //自己定义的协议 1为用户登录信息 2为普通信息 3为退出信息 typedef struct{ char name[13]; //用户昵称(自定义) char msg[64]; //正文内容 protocol P; //协议 }INFO; typedef struct usr_list{ struct sockaddr_in usr_addr; //保存接入用户的信息 struct usr_list *next; //指针 }usr_node,*List; 代码 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <pthread.h> typedef int protocol; //自定义协议 1登录信息 2普通信息 3退出信息 typedef struct sockaddr_in IN; int len_s = sizeof(struct sockaddr_in); IN serv_addr,clnt_addr; typedef struct{ char name[13]; char msg[64]; protocol P; }INFO; int len_info = sizeof(INFO); typedef struct usr_list{ IN usr_addr; struct usr_list *next; }usr_node,*List; List list; //创建用户链表为全局变量,之后利用线程分别实现接收转发消息和server自身发送消息 void init(); //初始化用户链表 void get_len();//得到链表长度(在测试时用,本例子无实际意义) void insert_node(IN usr_addr); //插入节点 void del_node(IN usr_addr); //删除节点 void send_log(int serv_fd,INFO info,IN clnt_addr); //发送登录信息 void send_msg(int serv_fd,INFO info,IN clnt_addr); //发送普通信息 void send_off(int serv_fd,INFO info,IN clnt_addr); //发送离线信息 void serv_send(int serv_fd,char *buf); //server发送消息 void *thread_handler(void *); //子线程任务,实现接收转发 int main(int argc, char *argv[]) { if( argc < 3){ printf("

Navicat导入文件报错解决方案

not a valid xml 在数据库中导入文档,报此错误 空数据库导入,新建表加导入文件对应字段,也报此错误。 更换数据类型,XML文件类型更换Excel文件类型 报此错误 source tables must be provided 又换txt文本类型,解决!

Windows11,通过VMware,安装MacOs系统——vmx修改

大部分流程参考 https://www.bilibili.com/video/BV1C64y1v7BC?from=search&seid=8354554040199131128&spm_id_from=333.337.0.0 使用哪个镜像基本都可以,关键是vmx的修改(底部添加如下内容,本机红米G 2021锐龙 5800H) smc.version = 0 cpuid.0.eax = 0000:0000:0000:0000:0000:0000:0000:1011 cpuid.0.ebx = 0111:0101:0110:1110:0110:0101:0100:0111 cpuid.0.ecx = 0110:1100:0110:0101:0111:0100:0110:1110 cpuid.0.edx = 0100:1001:0110:0101:0110:1110:0110:1001 cpuid.1.eax = 0000:0000:0000:0001:0000:0110:0111:0001 cpuid.1.ebx = 0000:0010:0000:0001:0000:1000:0000:0000 cpuid.1.ecx = 1000:0010:1001:1000:0010:0010:0000:0011 cpuid.1.edx = 0000:0111:1000:1011:1111:1011:1111:1111 smbios.reflectHost = TRUE hw.model = MacBookPro14,3 board-id = Mac-551B86E5744E2388

自学虚幻引擎图文笔记:如何在UE4中做积雪材质

废话不多说,直接来干货! 首先要思考一下 积雪是什么样的,比如积雪有厚的 有薄的,但是整体都可以总结为A+B,A物体,B是雪。 那么问题来了,这个积雪的材质怎么做呢?很难吗? 不!很简单~ 往下看 捋一下思路,积雪都是纵向的 ,所以在做材质时,我们只需要对Z轴进行设置,因为雪是覆盖的不是个点,是一片,所以要将一个点单个的方向,转换成世界法线方向 可以简单理解为从一个射线激光获得了这个方向,然后换成巨大聚光灯朝这个方向射出去。 但是我们是在Z轴整个的法线方向,实在太长了,所以要缩短方向 将它归一化,这是在世界坐标中,我们就将Z轴的方向缩短到0-1.但是我们在转化后世界坐标后,其它方向的面还在,但是我们只考虑Z轴,并且要把这个0-1作为蒙版 1是不透 0是透,这是需要用到点积 将转换后的向量与初始向量点积 得到这段的长度值 ,这时可以得到一个分界明确的黑白蒙版,如果需要做个过渡 则需使用,然后我们用个底图作为物体,与这个值进行lerp, 让它们互相融入,最后输出基础颜色节点。 具体操作如图所示 有什么不懂得可以私信我哈,如果看到了这里,可以点个赞吗?嘻嘻

i++和++i的区别

1、首先,单独拿出来说,i++和++i的意思是一样的,就是i = i + 1。 2、如果当做运算符来说,就是a = i++ 和 a = ++i这样的形式,情况就不一样了。 a = i++的意思是,先把i的值赋给a,即a = i,再执行i = i + 1; a = ++i是先执行 i = i+1,再把i的值赋给a; 举个例子来说,如果一开始i=4。 那么执行a=i++这条语句之后,a=4,i=5; 那么执行a=++i这条语句之后,i=5,a=5; 同理,i–和--i的用法也是一样的。 3、另外在循环体中的区别 ①for循环中,for(int i = 0;i < 6;i++)和for(int i = 0;i < 6;++i)效果一样 ② while(i++)是先用i的初始化值做循环变量再i+1 而while(++i)是先用i的初始值+1,再循环

解决html中表格线条粗细不一的问题

我们再html中使用原生的table时,为了美观往往会通过改变td的border来改变表格边框的样式,但是通常会处出现边框线条粗细不一的问题,下面代码解决了该问题 table { border-collapse: separate; border-spacing: 0; border-top: 1px solid #95B8E7; border-left: 1px solid #95B8E7; } td { border: 1px solid #95B8E7; border-top: none; border-left: none; } 通过代码可知:我们将table的border-collapse属性改成了separate,这是关键,然后td只显示下边框和右边框,最外层table边框只显示上边框和左边框,这样就解决了边框线条粗细不一的问题

【C语言】连续生成多个随机数

C语言连续生成多个随机数 在实际编程中,我们经常需要生成随机数,例如,贪吃蛇游戏中在随机的位置出现食物,扑克牌游戏中随机发牌。 在C语言中,我们一般使用 <stdlib.h> 头文件中的 rand() 函数来生成随机数,它的用法为: int rand (void); 下面是一个随机数生成的实例: #include <stdio.h> #include <stdlib.h> int main(){ int a = rand(); printf("%d\n",a); return 0; } 运行结果举例: 193 多次运行上面的代码,你会发现每次产生的随机数都一样,这是怎么回事呢?为什么随机数并不随机呢?实际上,rand() 函数产生的随机数是伪随机数,是根据一个数值按照某个公式推算出来的,这个数值我们称之为“种子”。 种子在每次启动计算机时是随机的,但是一旦计算机启动以后它就不再变化了;也就是说,每次启动计算机以后,种子就是定值了,所以根据公式推算出来的结果(也就是生成的随机数)就是固定的。 修改种子: 我们可以通过 srand() 函数来重新“播种”,这样种子就会发生改变。srand() 的用法为: int srand(void); 为了使种子是不断变化的 我们使用到了时间函数 time() (要在 time.h 头文件下使用) srand((unsigned int )time(NULL)); 代码运行(多次生成随机数): 有时候我们需要一组随机数(多个随机数),该怎么生成呢?很容易想到的一种解决方案是使用循环 方案1: #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int a, i; //使用for循环生成10个随机数 for (i = 0; i < 10; i++) { srand((unsigned)time(NULL)); a = rand(); printf("

go语言之“继承”

go语言没有本质上的“继承”;但是可以通过结构体来实现继承。 package main import "fmt" type animal struct { name string age int } type cat struct { feet int an animal } func (a animal) move() { fmt.Printf("%s会动\n", a.name) } func (c cat) miao() { fmt.Printf("%s会瞄\n", c.an.name) } func main() { a := animal{ name: "嘟嘟", age: 3, } c := cat{ feet: 4, an: animal{ name: "一一", age: 3, }, } fmt.Println(a) a.move() fmt.Println(c) c.an.move() //此处必须加an来调用,因为cat结构体里面不是匿名字段 c.miao() }

基于(LinuxC语言)的udp局域网聊天室

【使用说明与相关缺陷】 //1.关于两个文件夹的说明:(这部分是源文件压缩包的说明,这边没发所以可以忽略 ) 文件夹 udp_聊天室 的内容是本次项目的内容. 文件夹 tcp_select 的内容是基于tcp和select写的服务器转发代码. udp_聊天室的代码是通过tcp_select的代码修改和添加功能得到的. 2.在使用udp_聊天室时需要注意: 1)server端需要用命令行传参的形式输入端口号. 2)client端需要用命令行传参的形式输入服务器的ip地址和端口号. 3)本聊天室仅限于同一个网段内的通信. 3.主要功能: 1)用户名不能重复,如果重复会提示重新输入,用户登录有提示. 2)服务器转发客户端发来的消息给其他客户端. 3)当前客户端输入quit可以退出自己,并且服务器转发退出提示. 4)火力覆盖:服务器输入quit可以退出所有客户端并关闭自己. 5)定点打击:服务器输入quit 用户名 可以指定让某一个用户退出. 6)服务器会转发自己从终端输入的内容给所有客户端. 7)有两个非法用户名(客户端输入会提示重新输入)一个是 name 一个是server name:是用户名关键字. server:是服务器的用户名. 8)客户端使用Ctr+C强制退出也会被捕捉,之后进行正常的退出流程. 4.小缺陷: 1)在输入和输出的时候,如果输入一半,收到消息的话,会优先打印收到的内容. 这会导致在多人聊天的时候,输入内容不够明确的问题. (目前没想到啥好办法,好像这是终端的缺陷) 2)发送消息时,正式消息前的前缀太长,占用不必要的空间. 解决方法(可以用一个字符作为通信前缀,但是代码已经成型修改起来太过耗时) 3)目前用户名的可用长度为15个字节,比较短且还有两个非法用户名. 解决方法(可以增加用户名所占的字节数,但是会增加占用的空间) 4)如果服务器运行前还有残留的客户端没有退出的话,可能会导致服务器端崩溃 当这个不受管制的客户端退出时会给服务器发送客户端退出消息,但是服务器的 用户链表内并没有该用户,所以会访问非法空间导致段错误 解决方法(在每次服务器退出的时候使用quit使所有客户端退出) 5)服务器端使用的是select实现的多路复用,代码量与进程实现相比要大,并且不是 特别好理解,但是不需要考虑父子进程之间通信的问题。 6)客户端使用的虽然是进程实现多路复用,但是代码量也一百多行,而且由于信号和 父子进程之间退出等问题,添加了很多不好理解的代码,降低了代码的易读性。 server(客户端代码)(注释不多。。。。。。因为在下懒) #include<stdio.h> #include<string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <sys/select.h> #include <sys/time.h> #include <unistd.h> #include <stdlib.h> typedef struct node_t { char name[16]; struct sockaddr_in linkclient; struct node_t *next; }linklist; //建立有头链表的头 linklist *create(); //添加内容 int add(linklist *p,char *name,struct sockaddr_in linkclient); //删除指定的数据 int del_post(linklist *p,char *name); //查看重复 int look_name(linklist *p,char *name); //根据用户名获取保存ip的结构体 struct sockaddr_in *name_struct(linklist *p,char *name); //转发消息 void show(linklist *p,int sockfd,char *name,char *buf); int main(int argc, const char *argv[]) { if(argc!

C语言scanf()函数

scanf函数 C库包含了多个输入函数,scanf()函数是很通用的一个,因为它可以读取不同格式的数据,当然从键盘输入的都是文本,因为键盘只能生成文本字符:字母、数字、标点符号,如果要输入整数2022,就要键入字符2、0、2、2.如果要将其存储为数值而不是字符串,程序就必须把字符依次转换为数值,这就是scanf函数要做的,scanf()把输入的字符串转换成整数、浮点数、字符或字符串,而printf()正好相反,把整数、浮点数、字符和字符串转换成显示在屏幕上的文本。 scanf()也使用格式字符串和参数列表。 如果用scanf()读取基本变量类型的值,在变量名前加上一个&; 如果用scanf()把字符串读入字符数组中,不要使用&。 %c转换说明,根据%c说明,scanf()会读取每个字节,包括空白。 scanf()函数所用的转换说明与printf()函数几乎相同,主要的区别,对于float类型和double类型,printf()都使用%f %e %E %g %G转换说明,而scanf()只把它们用于float类型,对于double类型要使用l修饰符。 转换说明 含义 %c 把输入解释成字符 %d 把输入解释成有符号十进制整数 %e %f %g %E %F %G 把输入解释成浮点数 %i 把输入解释成有符号十进制整数 %o 把输入解释成有符号八进制整数 %p 把输入解释成指针 %s 把输入解释成字符串,从第一个非空白字符开始,到下一个空白字符之前的所有字符都是输入。 %u 把输入解释成无符号十进制整数 %x %X 把输入解释成有符号十六进制数 从scanf()角度看输入 假设scanf()根据一个%d转换说明读取一个整数。scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第一个非空白字符才开始读取,因为要读取整数,所以scanf()希望发现一个数字字符或者一个符号(+或-),如果找到一个数字或符号,它便保存该字符,并读取下一个字符,如果下一个字符是数字,它便保存该数字并读取下一个字符,scanf()不断地读取和保存字符,直至遇到非数字字符,,如果遇到一个非数字字符,它便认为读到了整数的末尾,然后,scanf()把非数字字符放回输入,这意味着程序在下一次读取输入时,首先读到的是上一次读取丢失的非数字字符,最后,scanf()计算已读取数字(可能还有符号)相应的数值,并将计算后的值放入指定的变量中。 如果第一个非空白字符是A而不是数字,会发生什么情况?scanf()将停在那里,并把A放回输入中,不会把值赋给指定变量,程序在下一次读取输入时,首先读到的字符是A,如果程序只使用%d转换说明,scanf()就一直无法越过A读下一个字符,另外,如果使用带多个转换说明的scanf(),C规定在第一个出错处停止读取输入。 如果要使用%s转换说明,scanf()会读取除空白以外的所有字符,scanf()跳过空白开始读取第一个非空白字符,并保存非空白字符直到再次遇到空白,这意味着scanf()根据%s转换说明读取一个单词,即不包含空白字符的字符串。当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0’,让数组中的内容为一个C字符串。在C语言中scanf()并不是最常见的输入函数,但它可以读取不同类型的数据。 格式字符串中的普通字符 scanf()函数允许把普通字符放在格式字符串中,除空格字符外的普通字符必须与输入字符串严格匹配。例如,假设在两个转换说明中添加一个逗号: scanf(“%d,%d”,&n,&m); scanf()函数将其解释成用户将输入一个数字、一个逗号,然后再输入一个数字,也就是说,用户要这样输入:3,4 实践是检验真理的唯一标准! scanf()的返回值 scanf()函数返回成功读取的项数,如果没有读取任何项,且需要读取一个数字而用户却输入一个非数值字符串,scanf()便返回0。 摘录自C Primer Plus

Element UI el-table实现只能选中一行

Element UI el-table实现只能选中一行 一种是通过更改el-table的样式,不显示全选框。还有一种是通过@select事件来控制,这里采用的是第二种。 <el-table :data="tableData" center ref="table" style="width: 100%" @select="handleSelect" stripe > handleSelect(selection,val){ //只能选择一行,选择其他,清除上一行 if(selection.length > 1){ let del_row = selection.shift() this.$refs.listTable1.toggleRowSelection(del_row,false) //设置这一行取消选中 } },

RMI, 远程方法调用

RMI = Remote Method Invocation, 远程方法调用机制: 允许一个JVM对象调用另一个JVM对象方法 这种机制特别适合分布式应用, 比如JMeter的分布式压测 1. 架构图 RMI registry, 命名空间, 供server注册RMI server, 具体的实现, 被调用的对象RMI client, 从registry的命名空间中查找注册的方法, 实现远程调用 2. 样例 角色之前的关系如下, 代码逻辑大体一致 2.1. 定义远程调用接口 import java.rmi.Remote; import java.rmi.RemoteException; /** * 定义Adder接口, 继承Remote对象 */ public interface Adder extends Remote { int add(int x, int y) throws RemoteException; } 2.2. Server实现 启动server端程序, 可以发现监听端口1099, 8801 (二者进程号一致) import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; /** * 1. 实现Adder接口 * 2.

C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service

关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关。目前写完了查看 Kubernetes Service 信息、创建 Route 和 Cluster 和绑定 Kubernetes Service。简单来说,就是完成了基础部分,配置路由和后端服务绑定,如果想实现动态路由和直接转发等功能,只需要按照官方的文档,增加中间件即可。 原本打算使用 .NET 6 的 AOT(一共40MB) ,但是打包运行会容易出现一些依赖问题和环境问题,因此放弃了,依然采用 Runtime + 应用 的方式部署,进行总共 120 MB 左右。 后端项目地址:https://github.com/whuanle/neting 前端项目地址:https://github.com/whuanle/neting-web 体验地址:http://neting.whuanle.cn:30080/ 大概界面是这样的: Route:即来源入口,支持 http/https 和 gRPC,Route 是设计进入集群前的访问者流量如何绑定后端服务,可以配置访问者 URL 的区配规则; Cluster:后端服务,一个 Cluster 可以绑定多个类型的后端服务,主要实现一个域名不同的后缀访问多个微服务,或者同一个实例负载均衡等; Service:查看 Kubernetes Service 的一些网络信息; Neting 目前只实现了简单的配置,仅供读者了解 Yarp 以及入门 Kubernetes API 开发、监控集群资源等。读者也可以从中了解 etcd 的使用,如何设计一个 Kubernetes Controller 应用。 基础功能已经做了,读者可根据需求,自行增加中间件即可。 下面介绍如何部署 Neting 项目,需要读者提前创建好集群。 谈谈对 Yarp 的看法。 首先,Yarp 的仓库地址是 https://github.com/microsoft/reverse-proxy

C语言printf()函数

请求printf()函数打印数据的指令要与待打印数据的类型相匹配。例如,打印整数时使用%d,打印字符时使用%c,这些符号被称为转换说明,它们指定了如何把数据转换成可显示的形式。 ANSI C标准为printf()提供的转换说明如下: 转换说明 输出 %a(A) 浮点数、十六进制数和p计数法 %c 单个字符 %d 有符号十进制整数 %e(E) 浮点数,e计数法 %f 浮点数,十进制计数法 %g(G) 根据值的不同,自动选择%f或%e ,%e(E)格式用于指数小于-4 %i 有符号十进制整数(同%d) %o 无符号八进制整数 %p 指针 %s 字符串 %u 无符号十进制整数 %x(X) 无符号十六进制整数,使用十六进制数0f(0F) %% 打印一个百分号 格式字符串中的转换说明一定要与后面的每个项相匹配! 如果有两个转换说明,但后面只有一个待打印项,系统不同导致的结果不同,不过这种情况最好的状况是得到无意义的值。 如果只打印短语或句子,就不需要使用任何转换说明。 待打印项可以是常量也可以是乘法表达式。 转换说明的意义 转换说明把以二进制格式存储在计算机中的值转换成一系列字符(字符串)便于显示。例如76在计算机内部的存储格式是二进制数01001100。%d转换说明将其转换成字符7和6,并显示为76;%x转换说明把相同的值(01001100)转换成十六进制计数法4c;%c转换说明把01001100转换成字符L。转换可能会误导读者认为原始值被替换成转换后的值,实际上转换说明是翻译说明,%d的意思是把给定的值译成十进制整数文本并打印出来。 转换不匹配的情况 转换说明应该与待打印值的类型相匹配,通常都有多种选择,例如,如果要打印一个int类型的值,可以使用%d、%x 、%o,这些转换说明都可用于打印int类型的值,区别在于它们分别表示一个值的形式不同。 short int 的大小是2字节,系统使用二进制补码来表示有符号整数,这种方法,数字0~32767代表它们本身,而数字32768~65535则表示负数,其中65535表示-1,65534表示-2,以此类推,-336表示为65200(65536-336),被解释成有符号int时,65200代表-336,而被解释成无符号int时,65200则代表65200.一个数字可以被解释成两个不同的值,尽管并非所有的系统都是用这两种方法来表示负整数,但不要期望%u转换说明能把数字和符号分开。 参数传递 参数传递机制因实现而异,以我们的系统为例,分析参数传递的原理: printf(“%ld %ld %ld %ld\n”,n1,n2,n3,n4); 该调用告诉计算机把变量n1,n2,n3,n4的值传递给程序(n1为float类型,n2为double类型;n3,n4为long类型)。程序把传入的值放入称为栈的内存区域,计算机根据变量类型(不是根据转换说明)把这些值放入栈中。n1被存储在栈中,占8字节,n2也在栈中占8字节,n3和n4在栈中分别占四字节,然后控制转到printf()函数,该函数根据转换说明(不是根据变量类型)从栈中读取值。%ld转换说明表明printf()应该读取4字节,所以printf()读取栈中的前4字节作为第1个值,这是n1的前半部分,将被解释成一个long类型的整数。根据下一个%ld转换说明,printf()再读取4字节,这是n1的后半部分,将被解释成第2个long类型的整数,类似的根据第3个和第4个%ld,printf()读取n2的前半部分和后半部分,并被解释成两个long类型的整数,因此对于n3和n4,虽然用对了转换说明,但printf()还是读错了字节。 printf()的返回值 大部分C函数都有一个返回值,这是函数计算并返回给主调程序的值,总之可以把返回值像其他值一样使用,printf()函数也有一个返回值,它返回打印字符的个数,如果有输出错误,printf()则返回一个负值。 打印较长的字符串 有时,printf()语句太长,在屏幕上不方便阅读,故而一条语句可以写成多行,但不能在双引号括起来的字符串中间断行(C编译器会报错,字符串常量中有非法字符),在字符串中,可以使用\n来表示换行字符,但是不能通过按下Enter键产生实际的换行符。 摘录自C Primer Plus

EFCore——原生Sql语句(12)

原生Sql语句 一、在EFCore的体系下执行原生SQL1.执行查询原生sql(FromSqlInterpolated)2.执行非查询sql(ExecuteSqlInterpolatedAsync) 二、使用ADO.NET体系下执行纯原生SQL1.为什么使用ADO.NET2.ADO.NET举例 一、在EFCore的体系下执行原生SQL 1.执行查询原生sql(FromSqlInterpolated) 1.在EFCore体系执行SQL有很大的限制,但较为方便 2.执行原生查询Sql语句 //使用了插值函数,Article表名, var list = await ctx.Articles.FromSqlInterpolated(@$"select * from T_Articles").ToListAsync(); 3.执行原生Sql语句我们可以使用FromSqlInterpolated去执行,而分页,include,过滤,排序依旧可以找EFCore实现 var list = await ctx.Articles.FromSqlInterpolated(@$"select Id,Title,Message from T_Articles").Include(b=>b.Comments).ToListAsync(); 4.FromSqlInterpolated执行原生Sql只能查找全部列,不能只查找部分列 这是FromSqlInterpolated的局限性,只能单表查询,不能使用join例: 2.执行非查询sql(ExecuteSqlInterpolatedAsync) 1.执行非查询原生查询Sql语句 例:删除 await ctx.Database.ExecuteSqlInterpolatedAsync(@$" DELETE FROM T_Articles WHERE id =13"); 插入 await ctx.Database.ExecuteSqlInterpolatedAsync(@$"insert into T_Articles (Title,Message) values( 'LTY','毕业了')"); 在EFCore执行原生Sql有局限性,应付不了复杂的情况,那么可以使用接下里的最朴素的方式。 二、使用ADO.NET体系下执行纯原生SQL 1.为什么使用ADO.NET 1.FromSqlInterpolated()只能单表查询,但在实现报表查询,Sql语句非常复杂,不仅要多表join,而且返回的查询结果不与实体对应。不能用它来查询,因此需要一种能执行任意Sql查询的机制。 例:就没对应实体,就不能用FromSqlInterpolated select Author,Count(*)from T_Books group by Author 2.EFCore允许把视图或存储过程映射为实体,因此可以把复杂查询写出视图或存储过程,然后再声明对应的实体。并且要DbSet //复杂查询 select Author,Count(*)from T_Books group by Author //需要搞一些这些不对应表的实体,这些不是实体,但在代码层面上它又是实体,会非常恶心 class BookAuthor { public string Author{get;set;} public int Count{get;set;} } 3.

第一篇博客——梦想起航

前言 今从天开始,写下我的第一篇博客,并且将持续更新我的学习笔记,总结学习方法,既是对我学习的巩固与促进,也真诚的希望与诸位共勉! 个人介绍: 本人双非本科院校,21级计算机科学与技术专业,在接触这个专业之前,对这个专业一无所知,萌新一个,但自从了解了这个专业之后,就深深的被其所吸引,感谢各位老铁能抽出时间看我的第一篇博文,初学涂鸦,白字连篇,多多包涵,以后的日子大家一起学习共同进步。 编程目标: 学习编程并非一朝一夕的,而是日积月累的结果。初学编程,我的目标是首先需要学好C语言并且精通,会灵活运用,因为打算要选择C/C++的研发方向,然后学好数据结构算法,学习C++语言,Linux系统编程,MySQL数据库,学好以上内容并且精通。同时我也会保持每天不低于六个小时的编程学习,每周不低于40个小时的编程学习。怀揣着大大的梦想选择这一条路,希望经过我的坚持与努力,将来能够进入网易工作。 学习从现在开始: 最后,希望CSDN的大佬们,多多指教,向你们看齐,共同进步。

Android的Service相关问题

Service知识点 目录 Service知识点 Service的启动 Service的生命周期? Activity如何与Service通信 IntentService是什么 说说 Activity、Intent 和 Service 是什么关系 Service优先级如何提高 Service 里面可以弹Toast吗? 是否可以在Service内进行耗时操作,会有什么后果? 一个Activty先start一个Service后,再bind时会回调什么方法?此时如何做才能回调Service的destory()方法? 前台服务是什么?和普通服务的不同?如何去开启一个前台服务? ActivityManagerService Service 和 Thread 的区别 Service 的 onStartCommand 方法有几种返回值?各代表什么意思? Service使用场景? Service的启动 Service 是 Android 中实现程序后台运行的解决方案,非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service默认运行在UI主线程中,所以不能做耗时操作,只能在Service中创建子线程来完成耗时操作。 补充说明:Service有两种启动方式,startService()和bindService(),它们之间的区别是: * startService()启动后无返回结果,startService()的生命周期有三个函数,分别是onCreate()、onStartCommand()和onDestory()。当我们首次startSerivce()启动一个service的时候,会调用service的onCreate函数创建该服务,然后调用onStartCommand函数执行操作,可以调用stopSevice/stopSelf来结束一个Service(多次调用都不会有异常)。(注:多次通过startService启动服务,onCreate只执行1次,都会调用onStartCommand。)启动后,它就独立于调用者而运行,因此任务结束后,Service需要stopSevice/stopSelf函数来停止该服务,避免消耗系统资源或电量。 * bindService()绑定Service后可以进行交互操作(发请求、获取结果或IPC通信),bindService的生命周期有四个函数,分别是onCreate()、onBind() 、onUnBind()和onDestory(),首次启动会先执行onCreate()方法创建服务,onBind() 会将调用者和服务绑定在一起,调用者退出时可以调用unbindService()进行解绑操作(方法中会调用onUnBind和onDestory)。(注:多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。) Service的生命周期? Service 的生命周期 * 回调方法含义: * onCreate:服务第一次被创建时调用; * onStartComand:服务启动时调用; * onBind:服务被绑定时调用; * onUnBind:服务被解绑时调用; * onDestroy:服务停止时调用; * 生命周期: * 只调用 startService() 启动服务:onCreate() -> onStartCommand() -> onDestory() * 只调用 bindService() 绑定服务:onCreate() -> onBind() -> onUnBind() -> onDestory()

JSON.parse(JSON.stringify(obj))实现深拷贝的缺点

我们经常用JSON.parse(JSON.stringify(obj))来实现深拷贝,但是有一天出了问题,才发现JSON.stringify的时候容易导致一些特殊类型的数据丢失和损坏。 javaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。 深拷贝和浅拷贝 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。 JSON.parse(JSON.stringify(obj))我们一般用来深拷贝,其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;序列化的作用是存储(对象本身存储的只是一个地址映射,如果断电,对象将不复存在,因此需将对象的内容转换成字符串的形式再保存在磁盘上 )和传输(例如 如果请求的Content-Type是 application/x-www-form-urlencoded,则前端这边需要使用qs.stringify(data)来序列化参数再传给后端,否则后端接受不到; ps: Content-Type 为 application/json;charset=UTF-8或者 multipart/form-data 则可以不需要 )。 我们在使用 JSON.parse(JSON.stringify(xxx))时应该注意一下几点: 1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象; var test = { name: 'a', date: [new Date(1536627600000), new Date(1540047600000)], }; let b; b = JSON.parse(JSON.stringify(test)) 2、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象; const test = { name: 'a', date: new RegExp('\\w+'), }; // debugger const copyed = JSON.parse(JSON.stringify(test)); test.name = 'test' console.error('ddd', test, copyed) 3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失; const test = { name: 'a', date: function hehe() { console.

微信小程序git提交

首先 微信平台官网上提供了开发工具的下载,地址: 下载·小程序 1.导入 在gitee上创建一个仓库 2.提交仓库 3.设置远程 1>添加远程仓库 2>设置账号密码 4.运行即可