一、查询文件在那个workspace 下被锁定 使用命令:
p4 opened -a p4文件地址
二、 解锁文件命令 删掉work space 即可解锁changelist 中被迁出的文件。
p4 -c workspace(px) unlock -fx //Project_Station/main/…/BaseMat_Orange.file.
三、清理其他人锁定的资源 p4 revert -C xxx_01_Project //Project/dev/Project/Content/ExternalActors/Maps/****/A/J4/QEC6RI7WG7INZCIRJ8C71M.uasset
###四、 还原某个的工作区的changelist 内容
p4 revert -C ***_xxx_Project //…
注意C大写。
五、查找某个steam关联的所有工作区 举例:
p4 clients -S //UnrealEngine5/main_Preview1 删掉某个steam
p4 stream -d -f //UnrealEngine/main_Preview1 清理某个仓库文件的历史记录
清理某个文件从1到80的提交记录。
p4 obliterate -y //Projec/xxxzip#1,80 记录一下。
Unity项目框架是如何设计的?有哪些原则 在设计Unity项目框架时,通常会遵循一些基本的原则和步骤。以下是主要的一些原则:
模块化:每个功能都应该被作为一个独立的模块来处理,这样可以方便修改和维护。
低耦合:不同模块之间应尽可能减少直接交互,使用事件或者接口等方式进行通信。
高内聚:每个模块应尽量只做一件事情,并且做好它。
可扩展性:设计框架时要考虑到未来可能需要添加或者修改功能的需求,所以需要有良好的扩展性。
可维护性:代码结构清晰、注释充分、命名规范等都能提高代码的可读性和维护性。
以下是一个简单的Unity项目框架设计流程:
确定游戏类型和核心玩法。
根据需求分析出需要哪些系统(比如角色系统、战斗系统、UI系统等)。
为每个系统创建相应的目录,并在目录下创建对应类文件。
设计各个类之间关系,如何交互和通信。
编写具体实现代码,并进行测试优化。
资源管理是如何做的,如何更新与打空包 在Unity中,资源管理是一个非常重要的部分。
基本步骤和原则:
资源分类:根据资源类型(如场景、音频、模型、贴图等)进行分类存放,有利于维护和查找。
使用AssetBundle:AssetBundle是Unity用于实现动态加载和更新的一种方式。你可以将游戏中的各种资源打包成AssetBundle,然后在运行时动态加载。
优化内存使用:尽量避免不必要的内存占用。比如对于不再使用的资源,应该及时销毁或者卸载。
版本控制:对项目中所有重要文件进行版本控制,以便追踪每个文件的修改历史,并在需要时回滚到之前的版本。
关于更新与打空包:
在Unity中,你可以通过构建AssetBundle来实现热更新。当有新内容需要添加或者旧内容需要修改时,只需构建出相应的AssetBundle并上传到服务器即可。客户端在启动游戏时检测到新版本后会自动下载并加载新版AssetBundles。
打空包通常指生成一个不含任何内容(或只含极少必须内容)但结构完整可运行且能从服务器下载更新数据包填充自身内容以达到完全功能状态游戏安装包,在移动平台上这样做可以显著减小安装包大小以提高用户体验。
注意这里所说“空”并非真正意义上无任何东西而是相较完全版来说去除了大部分非必须启动项而言。“空”的定义因项目需求可能会有所变化。
具体操作过程可能涉及多个环节如服务器配置、客户端代码编写等,请根据具体需求设定合适流程。
资源管理的平台,版本,渠道是如何管理的 在Unity中,资源管理的平台、版本和渠道通常需要通过一些特定的工具和策略来进行管理。以下是一些基本的方法:
平台管理: Unity提供了跨平台发布功能,可以方便地将游戏发布到不同的平台上。在项目设置中,你可以选择目标平台,并对该平台进行特定的优化设置。
同时,在代码中也可以使用预处理指令(如#if UNITY_ANDROID, #if UNITY_IOS等)来编写针对不同平台的代码。
版本管理: 版本控制系统(如Git、SVN等)是用于跟踪文件变更历史以及协同开发非常重要的工具。每次提交都会记录下当前所有文件状态,当需要时还能回滚到之前任意一个提交状态。
除此之外,Unity内部也有一个简单版控制系统——Asset Serialization模式。它允许你以文本或二进制格式保存场景和预设体等资源,并且支持多人同时编辑一个场景。
渠道管理: 对于渠道管理,你可能需要构建不同版本的应用以适配各个发行渠道(如Google Play Store, App Store, Steam等)。这可能涉及改变游戏内购买系统、广告SDK或者其他第三方插件配置等内容。
此外,在打包时候添加相应标识符区分不同渠道包也是常见做法。例如通过Scripting Define Symbols定义预处理指令区分各个市场版本,在编译时动态改变某些代码执行流程。
以上就是在Unity中进行资源管理相关操作所采取方法概述,请根据实际项目需求选择合适方式。
项目框架是如何让美术,策划,程序愉快的工作的 在一个项目中,美术、策划和程序员都有各自的专业领域和任务,而一个良好的项目框架应该能够让他们愉快地一起工作。以下是一些实现这个目标的方法:
明确角色与职责:每个团队成员都应清楚自己的角色和职责。这样可以防止工作重叠或者遗漏,并且让每个人都知道他们需要完成什么。
良好的沟通:团队成员之间需要保持开放、诚实和频繁的沟通。定期进行会议以更新项目进度,同时也要鼓励团队成员提出问题和建议。
使用合适的工具:选择适合你们团队需求的软件或平台来协同工作。例如使用版本控制系统(如Git)来管理代码,使用Trello或Jira等项目管理软件来跟踪任务进度。
规范化流程:建立并坚持执行规范化流程可以帮助提高效率并减少错误。例如设定命名规则、文件组织结构等。
模块化设计: 采用模块化设计思想,在编码时尽可能使得各部分解耦,降低依赖性,提高代码复用性。这样不仅方便程序自身开发与维护,并且在一定程度上也能让美术与策划更容易理解整体结构及其对应部分功能实现方式。
尊重专业意见: 尊重每位参与者在其专业领域能力及其意见表达权,在决策时充分考虑各方面因素以达到最优决策结果。
提供足够资源支持: 确保所有人都有足夠时间和资源去完成他们所负责任务, 这包括了必要硬件设备, 软件, 教育资料等.
真机调试,看打印日志你是如何处理的 调试移动设备上的应用并查看其打印日志,通常需要使用一些特定的工具和技术。以下是一些基本步骤:
连接设备:首先,你需要将移动设备通过USB线连接到电脑。确保你已经在设备上开启了开发者模式以及USB调试。
Android平台:
如果你正在开发的是Android应用,可以使用Android Studio或者adb(Android Debug Bridge)命令行工具来查看和过滤日志信息。在Android Studio中,打开Logcat窗口即可实时查看到连接设备输出的日志。使用adb命令行工具时,可以在终端输入adb logcat命令来显示所有日志信息。 iOS平台:
针对ABA问题做了一下总结和尝试解决 背景 在学习乐观锁、悲观锁时,了解到CAS是一种乐观锁的实现,但是从博客中看到说CAS会存在ABA问题,于是就搜索了一番。
什么是ABA问题 来自blog 考虑如下操作: 并发1(上):获取出数据的初始值是A,后续计划实施CAS乐观锁,期望数据仍是A的时候,修改才能成功 并发2:将数据修改成B 并发3:将数据修改回A 并发1(下):CAS乐观锁,检测发现初始值还是A,进行数据修改 上述并发环境下,并发1在修改数据时,虽然还是A,但已经不是初始条件的A了,中间发生了A变B,B又变A的变化,此A已经非彼A,数据却成功修改,可能导致错误,这就是CAS引发的所谓的ABA问题。
这个应该就是产生ABA问题的真正原因。
ABA问题的举例 看了几篇讲述ABA问题的博客,如下描述。
某人取款,由于机器不太好使,多点了几次取款操作。后台threadA和threadB工作,此时threadA操作成功(100->50),threadB阻塞。正好某人朋友打款50元给小牛(50->100),threadC执行成功,之后threadB运行了,又改为(100->50)。 lz钱哪去了???来自blog 个人觉得这里threadB后来继续运行后,总共是会取出两次钱的。
假设有个线程A去判断账户里的钱此时是15,满足条件,直接+20,这时候卡里余额是35。但是此时不巧,正好在连锁店里,这个客人正在消费,又消费了20,此时卡里余额又为15,线程B去执行扫描账户的时候,发现它又小于20,又用过cas给它加了20,这样的话就相当于加了两次,这样循环往复肯定把老板的钱就坑没了!来自blog 个人觉得评论中这个例子更合适,但不知道该如何在代码中复现,所以还是按照上述的逻辑进行了复现。 A线程首先获取余额是15,然后准备加上20,在A线程还没提交的时候,此时B线程进入,将余额加上20并且成功提交,此时余额为35。但是紧接着用户又消费了20,所以余额还是15,终于A线程获取到了时间片,它比对之后发现余额还是15,所以A线程就执行了。
ABA问题的本质 ABA问题的根本在于CAS在修改变量的时候,无法记录变量的状态,比如修改的次数,是否修改过这个变量。这样就很容易在一个线程将A修改成B时,另一个线程又会把B修改成A,造成CAS多次执行的问题。
示例 来自blog 一家火锅店为了生意推出了一个特别活动,凡是在五一期间的老用户凡是卡里余额小于20的,赠送20元,但是这种活动每人只可享受一次。然后火锅店的后台程序员小王开始工作了,很简单就用CAS技术,先取用户卡里的余额,然后包装成AtomicInteger,写一个判断,开启10个线程,然后判断小于20的,一律加20,然后就很开心的交差了。
多线程增加20余额时,同时有消费线程做扣减,就有可能(不是每次都会出现)出现下图所示。 add线程0通过CAS增加余额,consume线程0通过CAS减少余额后,add线程2又通过CAS增加余额。
代码如下
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; public class AtomicStampReferenceDemo { static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0); static AtomicInteger amount = new AtomicInteger(19); public static void main(String[] args) { /*new Thread(new Runnable() { @Override public void run() { provideByStamped(); } }).start(); new Thread(new Runnable() { @Override public void run() { consumeByStamped(); } }).
<avue-crud :option="option" :table-loading="loading" :data="data"ref="crud"></avue-crud> 更新完数据加上
this.$nextTick(() => { this.$refs.crud.refreshTable(); })
一、FFT的由来 首先,为什么要进行傅里叶变换?将时域的信号变换到频域的正弦信号,正弦比原信号更简单,且正弦函数很早就被充分地研究,处理正弦信号比处理原信号更简单。正弦信号的频率保持性:输入为正弦信号,输出仍是正弦信号,幅度和相位可能发生变化,但频率与原信号保持一致,只有正弦信号才拥有这样的性质。
对于傅里叶变换的类型:非周期连续信号采用傅里叶变化;周期连续信号采用傅里叶级数;非周期连续离散信号采用离散时间傅里叶变换;周期离散信号采用离散傅里叶级数。 四种信号均为 (‐∞,+∞) 上的无穷信号,而计算机只能处理离散的、有限长度的信号。四种傅里叶变换总结如下表所示。
FT、FS、DTFT,至少都有一个域不是离散的,计算机无法处理;DFS满足时域和频率离散的要求,但其时域为无穷长的周期序列;通过对DFS的推导,得到适合计算机计算的离散傅里叶变换 (DFT)。
从离散傅里叶级数 (DFS) 到离散傅里叶变换 (DFT),周期序列虽为无穷长序列,但是只要知道一个周期的内容,便可知其全貌。因此,周期序列实际上只有N个样值有信息,通过推导可得到DFT、时域和频域 (DFT) 上的有限长序列,可以用来“代表”周期序列,DFT在时域和频域上均离散,且为有限长序列,可以用计算机进行处理。
DFT虽好,但是其计算的次数太多,不利于大数据量的计算。FFT是DFT的快速算法可以节省大量的计算时间,其本质仍然是DFT。
二、MATLAB中实现FFT 的计算 MATLAB傅里叶命令有两种:
Y= fft(x) ,其中,x 为一个序列(向量),存放采集信号的数据;
另外一种Y= fft(x,n),x 的定义同上,n 定义计算数据的个数,如果n 大于x 的长度,在x 的末尾添加0,使得x 的长度等于n。如果n小于x的长度,截取x 中的前n 个数来进行计算。Y 返回fft 的结果,为一个复数序列(向量),建议采用上一种格式的用法,并且保证 x 的个数为偶数。
FFT结果的数据长度:时域N 个点,频域为N/2+1个点;x 轴频率点的设置:采样频率为Fs 时,频谱图的最高频率为Fs/2(参照采样定理)。综合上述两点,x 轴的频率点为 (0:1:N/2)*Fs/N。
现对某一时域数据为例进行MATLAB傅里叶变换:
1. 绘制时域信号
lear;clc;closeall a=textread('C:\Users\Administrator\Desktop\matlab\FFT\TIME_X.txt'); %读取时域数据 y=a(:,2); %读取时域数据 Fs=6400; %采集频率 T=1/Fs; %采集时间间隔 N=length(y); %采集信号的长度 t=(0:1:N-1)*T; %定义整个采集时间点 t=t'; %转置成列向量 figure plot(t,y) xlabel('时间') ylabel('信号值') title('时域信号') 2. fft变换
Y=fft(y); %Y为fft变换结果,复数向量 Y=Y(1:N/2+1); %只看变换结果的一半即可 A=abs(Y); %复数的幅值(模) f=(0:1:N/2)*Fs/N; %生成频率范围 f=f'; %转置成列向量 3.
Heap Space问题是Java开发中常见的内存溢出问题之一,我们需要理解其原因和表现形式,然后通过优化代码、增加JVM内存和使用垃圾回收机制等方法来解决。
一、常见报错 java.lang.OutOfMemoryError: Java heap space 二、Heap Space问题的原因 对象创建过多:如果程序中创建了大量的对象,而这些对象又不能被垃圾回收机制回收,就会导致Heap Space不足。大对象的创建:如果程序中创建了一些大对象,比如数组或者长字符串,这些对象占用的内存空间超过了JVM能提供的最大内存,也会导致Heap Space不足。 三、Heap Space问题的表现形式 Heap Space问题最常见的表现形式就是Out Of Memory错误。当程序运行到某个地方时,突然报出“java.lang.OutOfMemoryError: Java heap space”的错误,然后就程序崩溃了。
四、Heap Space问题的解决方案 优化代码:通过优化代码,减少不必要的对象创建,尤其是大对象的创建。例如,可以使用StringBuilder来代替String进行字符串拼接,因为StringBuilder内部是用字符数组来存储字符串的,不会产生新的字符串对象。增加JVM内存:可以通过调整JVM启动参数来增加最大可用内存。例如,可以使用-Xms和-Xmx参数来设置JVM初始堆内存和最大堆内存。 java -Xmx2g -jar your_application.jar 使用垃圾回收机制:Java提供了垃圾回收机制来自动回收不再使用的对象所占用的内存。可以通过调用System.gc()方法来建议JVM进行垃圾回收。 示例1代码:优化方案
public class HeapSpaceDemo { public static void main(String[] args) { // 创建一个非常大的数组,可能会导致Heap Space不足 int[] bigArray = new int[1000000]; for (int i = 0; i < bigArray.length; i++) { bigArray[i] = i; } } } 解决方案:可以将bigArray数组拆分成多个小数组,或者使用ArrayList等集合类来动态管理数组的大小。
总结,Heap Space问题是Java开发中常见的内存溢出问题之一,我们需要理解其原因和表现形式,然后通过优化代码、增加JVM内存和使用垃圾回收机制等方法来解决。
示例1代码:Java Heap Space错误
背景:Flink数据写入到stage层,然后再入ods层,中间导致hive数据实时性不强,随后做优化,Flink之间以orc格式写入到hive
问题:单表日800亿数据量,产生过多的小文件,影响Impala查询
解决:对hive小文件进行合并,
ALTER TABLE lt_ipsy_xdr_temp PARTITION (day=20230829, hour=9,type=4) CONCATENATE;
目前大数据平台hdfs数据存储情况:
数据仓库中的表:
1.stg层是把flink应用程序写入的数据load进入的;
2.ods层表名称中包含ai的表是从mysql导入的; /_SCRATCH0 这些目录是sqoop 把mysql数据导入hive时生成的临时目录,可以删除。
3.stg 和 ods 的 tb_bu_dc_monitor_day 是外部表,其他层的表都是内部表;
4.stg层是原始数据,没有经过压缩处理,ods及以后其他各层都是orc格式
使用hive concatenate (外部表不可用,内部表可用,orc可用,分桶表不可用)
使用方式:
#1.设置文件最小大小(需要设置,否则合并操作可能会不理想):
SET mapreduce.input.fileinputformat.split.maxsize=256
#对于非分区表
alter table A concatenate;
#2.对于分区表
ALTER TABLE lt_ipsy_xdr_temp PARTITION (day=20230829, hour=9,type=4) CONCATENATE;
因为分桶表不适用,所以可以采用覆写的方式
INSERT OVERWRITE TABLE table PARTITION (day=20230908,hour=9,datatype=4)
SELECT a,b,c,d,e
FROM table where day=20230908 and hour=9 and datatype=4;
【大数据】Hive 小文件治理和 HDFS 数据平衡讲解 - 掘金 (juejin.cn)
作为测试开发,对于Linux相关知识,不管是面试还是工作,多多少少你还是得会点。
再说了,现在行情极差,要求又高,linux也是面试重头戏,所以,今天给你分享Linux相关面试题和答案。
题目 1、绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?
2、怎么查看当前进程?怎么执行退出?怎么查看当前路径?
3、怎么清屏?怎么退出当前命令?怎么执行睡眠?怎么查看当前用户id?查看指定帮助用什么命令?
4、Ls 命令执行什么功能?可以带哪些参数,有什么区别?
5、建立软链接(快捷方式),以及硬链接的命令。
6、目录创建用什么命令?创建文件用什么命令?复制文件用什么命令?
7、查看文件内容有哪些命令可以使用?
8、随意写文件命令?怎么向屏幕输出带空格的字符串,比如”helloworld”?
9、终端是哪个文件夹下的哪个文件?黑洞文件是哪个文件夹下的哪个命令?
10、移动文件用哪个命令?改名用哪个命令?
11、复制文件用哪个命令?如果需要连同文件夹一块复制呢?如果需要有提示功能呢?
12、删除文件用哪个命令?如果需要连目录及目录下文件一块删除呢?删除空文件夹用什么命令?
13、Linux 下命令有哪几种可使用的通配符?分别代表什么含义?
14、用什么命令对一个文件的内容进行统计?(行号、单词数、字节数)
15、Grep 命令有什么用?如何忽略大小写?如何查找不含该串的行?
16、Linux 中进程有哪几种状态?在 ps 显示出来的信息中,分别用什么符号表示的?
17、怎么使一个命令在后台运行?
18、利用 ps 怎么显示所有的进程? 怎么利用 ps 查看指定进程的信息?
19、哪个命令专门用来查看后台任务?
20、把后台任务调到前台执行使用什么命令?把停下的后台任务在后台执行起来用什么命令?
21、终止进程用什么命令? 带什么参数?
22、怎么查看系统支持的所有信号?
23、搜索文件用什么命令? 格式是怎么样的?
24、查看当前谁在使用该主机用什么命令? 查找自己所在的终端信息用什么命令?
25、使用什么命令查看用过的命令列表?
26、使用什么命令查看磁盘使用空间?空闲空间呢?
27、使用什么命令查看网络是否连通?
28、使用什么命令查看 ip 地址及接口信息?
29、查看各类环境变量用什么命令?
30、通过什么命令指定命令提示符?
31、查找命令的可执行文件是去哪查找的? 怎么对其进行设置及添加?
32、通过什么命令查找执行命令?
33、怎么对命令进行取别名?
34、du 和 df 的定义,以及区别?
35、awk 详解。
36、当你需要给命令绑定一个宏或者按键的时候,应该怎么做呢?
37、如果一个 linux 新手想要知道当前系统支持的所有命令的列表,他需要怎么做?
38、如果你的助手想要打印出当前的目录栈,你会建议他怎么做?
39、你的系统目前有许多正在运行的任务,在不重启机器的条件下,有什么方法可以把所有正在运行的进程移除呢?
40、bash shell 中的 hash 命令有什么作用?
题目标题:然后是几点 题目作者:翁恺 浙江大学
有时候人们用四位数字表示一个时间,比如 1106 表示 11 点零 6 分。现在,你的程序要根据起始时间和流逝的时间计算出终止时间。
读入两个数字,第一个数字以这样的四位数字表示当前时间,第二个数字表示分钟数,计算当前时间经过那么多分钟后是几点,结果也表示为四位数字。当小时为个位数时,没有前导的零,例如 5 点 30 分表示为 530;0 点 30 分表示为 030。注意,第二个数字表示的分钟数可能超过 60,也可能是负数。
输入格式: 输入在一行中给出 2 个整数,分别是四位数字表示的起始时间、以及流逝的分钟数,其间以空格分隔。注意:在起始时间中,当小时为个位数时,没有前导的零,即 5 点 30 分表示为 530;0 点 30 分表示为 030。流逝的分钟数可能超过 60,也可能是负数。
输出格式: 输出不多于四位数字表示的终止时间,当小时为个位数时,没有前导的零。题目保证起始时间和终止时间在同一天内。
输入样例: 1120 110 输出样例: 1310 思路1:首先我们计算出从0点0分开始,到起始时间h点m分,所经历的分钟数passM; 然后给passM加上又流逝的时间M分钟,即passM += M;这时passM就是从0点0分开始,到终止时间所经历的分钟数了,不难算出终止时刻的的小时数是(passM/60)%24,分钟数是passM%60。
注意:在计算终止时刻的小时数时,因为题目中说了终止时刻与起始时刻在同一天,所以可以不模24(模了也没有问题),如果终止时刻与起始时刻不再同一天,需要模24。
代码1:(应该是全网最简洁明了的代码了^_^)
#include <stdio.h> int main () { int N,M,h,m,passedM; scanf("%d %d", &N, &M); h = N/100; // 这里N/100赋值给h,h就是从0点开始经过的时间的小时部分 m = N%100; // 这里N%100赋值给m,m就是从0点开始经过的时间的分钟部分 passedM = h*60+m; // passedM就是从从0点开始经过的总时间,换算成分钟 passedM += M; // 又经过了M分钟, printf("
一、如何删除? 1、选中需要有数字和汉字那段文字
2、点击段落下拉
3、找到中文版式
4、将【自动调整中文与数字的间距】取消勾选(不要勾选)
5、点击确定即可删除啦
今天主要来说一下当我们拿到了烧录器和对应的demo板时,该如何去进行连线,IDE软件安装的和第一个工程建立编译和下载已经说了,那么,物理层接线该如何去接呢,相信看完这篇文章,应该就全明白啦。
首先附图32位机的demo板,分别是KF32A151系列(140、141、150、151、152):
下图为KF32A156MQV demo板。146系列和136系列demo板的形式上和156类似,外设接口略有减少,这里就不展示了,仅展示156demo板。
芯旺微的下载推荐连线模式:DPI,根据用户手册推荐此烧录方式,那么连接烧录器和PCB板的接口在手册上可以查询到如下图: 下图对应引脚接口适用于KF32A140,KF32A141,KF32A150,KF32A151,KF32A152,KF32A153系列的芯片:
下图对应引脚接口适用于KF32A136,KF32A146,KF32A156系列的芯片:
下面请看烧录器的图(目前市面上有两款烧录器,颜色区分为黄色款和蓝色款):
蓝色框图示:
黄色款:
接线说明:按照手册规定,就是将烧录器上面的四个引脚:VDD、GND、DAT、CLK 依次对应到demo板上的VDD、GND、DAT、CLK .四根线做烧录使用足够; 特别提示:注意看 烧录器上有MODE脚,这个脚在烧录的时候会触发一个低电平动作,所以此脚可以和单片机的RESET引脚进行相连,可以实现强制烧录,也建议客户将RESET引出,发生无法烧录的时侯可以和烧录器MODE连接
蓝色款接线说明:
黄色款接线说明:
好啦这样基本就完成接线了,另一端只需要将芯旺微提供的连接线连接到烧录器的插口在将USB接到电脑即可。
第一次更新时间:20230718
第二次更新时间:20231016 新增demo板展示图和说明配字。
一、动态规划(dp)
1.线性dp
2.状压dp
(1)高维前缀和
(2)动态高维前缀和
3.区间dp
4.树形dp(普通型,换根型)
5.概率dp
6.数位dp
7.背包
(1)01背包
(2)无限背包
(3)多重背包
(4)树形背包
(5)分组背包
(6)混合背包
(7)多维背包
(8)巨大背包
8.记忆化搜索
9.轮廓线dp
10.插头dp
11.计数dp
12.图上dp
13.基环树dp
14.自动机dp(状态机模型)
15.分治dp
16.填坑dp
17.动态dp
18.dp套dp
19.优化
(1)单调栈优化
(2)单调队列优化
(3)四边形不等式优化
(4)斜率优化
(5)数据结构优化
(6)决策单调性
二、数据结构
1.队列
(1)普通队列
(2)双端队列
(3)单调队列
2.栈
(1)普通栈
(2)单调栈
3.链表
(1)普通链表
(2)双向链表
(3)循环链表
(4)跳表(块状链表)
(5)邻接表、邻接矩阵(存图)
4.堆
(1)普通堆
(2)对顶堆
(3)斐波那契堆
(4)左偏堆
5.线段树
(1)基础线段树
(2)权值线段树
(3)主席树(可持久化线段树)
(4)李超线段树
(5)线段树合并
(6)zkw线段树
6.树状数组
7.平衡树
(1)Splay
(2)FHQ(范浩强)
(3)Treap
(4)可持久化平衡树
(5)AVL
这里是Redis7.0.9版本 下载地址:https://redis.io/download/
这里以linux下载:
首先判断我们的centos 7是多少位的,使用64位切记
getconf LONG_BIT 返回是多少就是几位 Linux安装Redis必须先具备gcc编译环境
gcc -v 查看版本 yum -y install gcc-c++ 安装c++库环境 下载redis7.0.9.tar.gz后放入Linux目录/opt
tar -zxvf redis7.0.9.tar.gz /opt目录下解压 cd redis7.0.9 进入目录 make && make install 在redis7.0.9目录下执行 cd /usr/local/bin 默认安装的位置查看 安装完后回到/opt/redis7.0.9/
mkdir /myredis 在根目录下创建myredis cp redis.conf /myredis/redis7.conf 将默认的复制过去 修改/myredis目录下redis7.conf配置文件做初始化设置
vim /myredis/redis7.conf redis.conf配置文件,改完后确保生效,记得重启,记得重启
默认daemonize no 改为 daemonize yes
默认protected-mode yes 改为 protected-mode no
默认bind 127.0.0.1 改为 直接注释掉(默认bind 127.0.0.1只能本机访问)或改成本机IP地址,否则影响远程IP连接
添加redis密码 改为 requirepass 你自己设置的密码
在/usr/local/bin目录下运行redis-server,启用/myredis目录下的redis7.conf
redis-server /myredis/redis7.conf 连接服务
redis-cli -a 设置的密码 -p 6379 测试 ping pong tip 登录redis 有一个warning警告
After Effects 2024是一款视频特效和动态图形设计软件,它可以帮助用户创建各种令人惊叹的视觉效果,例如粒子系统、合成特效、绿屏抠像等。该软件支持动画制作,包括关键帧动画、形状动画、运动跟踪等工具,可以创建复杂的动态动画和运动图形。
在视频合成和编辑方面,After Effects 2024支持多层视频合成,可以对多个视频片段进行剪辑、裁剪、调整颜色、添加过渡效果等操作。此外,该软件还内置了音频编辑功能,可以对音频进行剪辑、混音、添加音效等操作,并且支持与视频同步处理。
另外,After Effects 2024还加强了与Premiere Pro的协同工作,用户可以在两个软件之间轻松传输项目和合成。同时,该版本还支持新的基于文本的编辑功能,使得颜色处理更加轻松和一致。除此之外,还有数十种其他工作流程增强功能。
总的来说,After Effects 2024为视频特效和动态图形设计提供了更加强大和全面的功能,无论你是专业的设计师还是业余爱好者,都可以使用它来探索创意的无限可能性。
AE2024mac补丁(After Effects2024)
“win10怎么录音呀?朋友说win10有自带的录音机,但是我在电脑上找了很久都没找到,大家知道win录音机怎么打开吗?教教我吧。”
在数字化时代,录音机已经成为人们日常生活和工作中不可或缺的一部分。无论是记录会议、讲座、面试,还是录制音乐、音频,录音机都发挥着重要作用。随着win10操作系统的普及,许多用户发现win10内置的录音机可以用于录制音频。本文将深入介绍如何使用win10录音机,为您的声音录制需求提供全面的解决方案。
win10录音机录音方法: win 10内置录音机是一款简单易用的录音工具,用户可以通过开始菜单、搜索框找到并启动它。虽然win10录音机的功能相对简单,但基本可以满足一般用户的录音需求。以下是使用它的详细步骤:
第1步:在Windows系统中,按下“Win + S”组合键,搜索“录音机”或“声音录制器”,点击录音机打开它。
第2步:确保您的麦克风正常工作,然后点击Win10录音机中的“开始录音”按钮(通常是一个麦克风图标)。
第3步:开始说话或演奏您想要录制的声音,win10录音机将捕捉到您的声音。完成录音后,点击Win10录音机中的“停止录音”按钮,您可以在录音机内部查看录制的音频文件。
备注:使用录音机之前,确保设置中“允许应用访问你的麦克风”已经打开,并且赋予了录音机相关权限。 专业软件录音方法: 电脑录音机虽然方便,但仅限于录制一些简单的录音任务。如果您想要进行更多录音操作,可以使用数据 蛙录屏软件,它除了提供高质量的录音功能外,还具有视频录制、视频剪辑等功能,为用户提供一站式音频和视频处理解决方案。
它适用于Windows和Mac系统,拥有包括 AAC、WAV、FLAC 等多种音频格式,并允许用户自定义录音质量。下面是使用数据 蛙录屏软件进行录音的操作步骤:
操作环境: 演示机型:联想GeekPro2020 系统版本:Windows 10 软件版本:数据 蛙录屏软件1.1.8 第1步:在您的电脑上下载并安装数据 蛙录屏软件,打开软件可以看见一个简洁的应用界面,录音我们选择“音频录制”模式。
第2步:在音频录制模式里,页面可以选择开启或关闭麦克风和扬声器,上方还可以调节音量的大小,然后点击“REC”按钮开始录音。
第3步:在录音过程中可以启用自动停止和分段录制功能,安装录制时长、文件大小、停止时间进行设置,录音完成后可以自动退出程序。
第4步:录音结束后,软件提供了“高级剪辑”功能,我们可以对音频进行剪辑,删除不需要的音频文件。
结论 通过本文的介绍,相信您已经明白了win10录音机的使用方法,win10录音机是一个方便的工具,用于捕捉声音,而数据 蛙录屏软件则提供了更多高级功能和灵活性。无论您是想要简单地记录笔记,还是创建音频内容,这些工具都能满足您的需要。希望本文能帮助您更好地利用这些工具来捕捉和处理声音。
文章推荐:
qq视频录制教程,让你的视频更加精彩https://blog.csdn.net/shujuwa__net/article/details/133803690?spm=1001.2014.3001.5501
电脑怎么剪辑视频?高手分享的独家秘诀https://blog.csdn.net/shujuwa__net/article/details/133804424?spm=1001.2014.3001.5501
win10字体模糊怎么办?解决方案全解析!https://blog.csdn.net/shujuwa__net/article/details/133805043?spm=1001.2014.3001.5501
题目标题:奇偶分家 题目作者:陈越 浙江大学
给定N个正整数,请统计奇数和偶数各有多少个?
输入格式: 输入第一行给出一个正整N(≤1000);第2行给出N个非负整数,以空格分隔。
输出格式: 在一行中先后输出奇数的个数、偶数的个数。中间以1个空格分隔。
输入样例: 9 88 74 101 26 15 0 34 22 77 输出样例: 3 6 思路:用for循环把整数读入到变量num,每次判断num%2的值,若num%2 == 0,说明当前num时偶数,给偶数的计数递增1;否则num是奇数,给奇数的计数递增1。
代码1:
#include <stdio.h> int main () { int N,i,num,cnt1=0,cnt2=0; scanf("%d",&N); for (i = 0; i < N; i++) { scanf("%d", &num); if (num%2 == 0) cnt2++; else cnt1++; } printf("%d %d", cnt1, cnt2); return 0; } 更多PTA题目的的参考代码,可以在下面的小程序里找到哦
概述 纯笔记
LoRA的原理 LoRA其实是对稳定扩散模型最关键的部分进行了微小的改变。
这个关键的部分叫:cross-attention layers – 交叉注意力层。
研究人员发现,对这关键部分进行微调就足以实现良好的训练。
上面黄色部分,QKV 部分就是:交叉注意力层。
交叉注意力层里排列着权重,这些权重成矩阵的形式排列。就像Excel表格一样。
LoRA模型通过将权重添加到这些矩阵中来微调模型。
LoRA模型文件如何才能更小? LoRA 的技巧是将矩阵分解为两个较小的(低秩)矩阵。通过这样做,它可以存储更少的数字。让我们用下面的例子来说明这一点:
假设该模型有一个包含 1000 行和 2000 列的矩阵。模型文件中需要存储 2,000,000 个数字 (1000 x 2000)。 LoRA 将矩阵分解为 1000×2 矩阵和 2×2000 矩阵。这只有 6,000 个数字 (1000 x 2 + 2 x 000),少了 333 倍。这就是 LoRA 文件小得多的原因。
LoRA 将一个大矩阵分解为两个小的低秩矩阵。
在这个例子中,矩阵的秩为2。它比原始维度低得多,因此称为低秩矩阵。秩可以低至为1。
但这样的做有什么害处吗?研究人员发现,在交叉注意力层中这样做并不会影响微调的能力。所以没问题。
总结 LoRA就是对cross-attention layers – 交叉注意力层,进行了微调。微调的关键点就是调整权重,并使用低秩矩阵替换原来的高秩矩阵。 参考地址:
https://stable-diffusion-art.com/lora/#What_are_LoRA_models
netstat -tunlp|grep 5601 查看端口占用情况
[root@node1 ~]# netstat -tlnp |grep 5601 tcp 0 0 192.168.56.10:5601 0.0.0.0:* LISTEN 1536/bin/../node/bi ps aux | grep node 查看node进程
kibana是使用node.js写的,所以进程在node中
C++ 什么时候用.template 和 ::template 简单来说,就是你有一个未知类型T**(这个T本身就是模板)**
假设这个T是一个类,这个类里包含了一些模板函数或者模板结构体
你需要使用T. 或者 T:: 去调用他们,并且要显示指定模板参数
这个时候就需要用到.template 和 ::template
如果上面文字描述没懂?没关系,看示例就懂了
编译环境: gcc/g++ 13.2 -std=c++17
struct A { void Foo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } template <typename T> void Foo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } template <typename T> static void Goo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } template <typename T> struct B { using type = T; void Foo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } template <typename U> void Foo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; struct C { void Foo() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; }; template <typename T> struct Test { void Fun(T t) { t.
val dataBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main) 报错一:
Unresolved reference: ActivityMainBinding 首先你要知道一个概念,ActivityMainBinding是DataBinding中的一种视频绑定,通过activity_main.xml和此文件对应,即activity_main.xml会对应生成文件ActicityMainBinding.java文件,既然是视图绑定,就要看下你的配置文件是否写对了?
android { ... buildFeatures { //这个引用,可以引入DataBindingUtil类 //功能:数据绑定! dataBinding = true //这个引入,才能引入ActivityMainBinding (这个才是视图绑定!!!) //功能:视图绑定! //没有视图绑定,是不会自动编译生成ActivityMainBinding.java类的 viewBinding = true } } 报错二:
val dataBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
下划线位置报错:
Type argument is not within its bounds.
Expected:
ViewDataBinding!
Found:
ActivityMainBinding!
意思是:本来系统想要的是ViewDataBinding类,你给我的是ActivityMainBinding类。故,你要在activity_main.xml布局文件的根儿上添加<layout></layout>
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
目录
1、简单操作
2、按价格升序排序图书
3、按价格区间查找图书
4、根据指定书名,进行图书价格的修改
5、图书价格普调
6、查找最贵图书
7、 图书去重
8、完整代码
●此为数据结构与算法实现的练习。
该图书管理系统有以下十个功能:(链表)
0.退出
1. 图书的创建和输出
2. 新书入库(插入)
3. 旧书出库(删除)
4. 按书号查找图书
5. 按价格升序排序图书
6. 按价格区间查找图书
7. 根据指定书名,进行图书价格的修改
8. 图书价格普调
9. 查找最贵图书
10. 图书去重
【注】:功能 8.图书价格普调的含义为:计算所有图书的平均价格,将所有低于平均价格的图书价格提高20%,所有高于或等于平均价格的图书价格提高10%,最后逐行输出价格修改后的全部图书信息。
1、简单操作 对于单链表而言,以下几个操作是基本且常见的:
● 构造一个空的单链表
● 使用头插法或者尾插法创建单链表
● 元素的查找(查找第 i 个元素 / 查找值为 e 的元素 )
● 单链表的插入
● 单链表的删除
以上的操作即满足了图书管理系统里的功能1、2、3、4。
1、构造一个空的单链表:
Status InitList_L(LinkList &L) { //构造一个空的单链表L L = new LNode; //生成新结点作为头结点,用头指针L指向头结点 L->next = NULL; //头结点的指针域置空 return OK; } 2、使用头插法或者尾插法创建单链表
注意:nio4444 用户在/etc/sudoers已经配置了“nio4444 ALL=(ALL) NOPASSWD: ALL”
---
- hosts: all
remote_user: nio4444 gather_facts: No become: yes #表示是否允许用户切换
become_method: sudo #表示切换用户的方式,有su/sudo/pbrun等方式,默认是sudo方式
become_user: root #表示要切换到哪个用户,即目标用户
tasks:
- name: "create a test dir"
shell: /bin/sh -c 'mkdir -p /root/testuser'
final long count = 200_000_000; Random random = new Random(); //创建2亿条的list List<Integer> list = Stream.generate(() -> random.nextInt(20)) .limit(count) .collect(Collectors.toList()); // 顺序处理 long startTime = System.currentTimeMillis(); list.stream() .map(n -> n * 2).forEach(e -> { }); long endTime = System.currentTimeMillis(); System.out.println("顺序处理耗时:" + (endTime - startTime) + "ms"); // 并行处理 startTime = System.currentTimeMillis(); list.parallelStream() .map(n -> n * 2) .forEach(e -> { }); endTime = System.currentTimeMillis(); System.out.println("并行处理耗时:" + (endTime - startTime) + "
目录
一、环境搭建及介绍
关于STM32基础介绍
新建工程
外设案例
LED流水灯
蜂鸣器
上拉电阻和下拉电阻知识
电压比较器
c语言基础知识 类型、结构体、枚举
类型int8_t int16_t int32_t
宏替换 #define 和typedef用法 结构体两种填充方法 和 命名规则
枚举用法
常用配置
输入输出模式
GPIO常用库函数
中断函数
模块化编程 延时函数 System
LED函数 Hardwore
按键函数 Hardwore
蜂鸣器函数 Hardwore
震动模块 Hardwore
OLED IIC模块Hardwore
调试方法
中断系统
概念:
NVIC中断控制寄存器结构 NVIC分组 抢占优先级和响应优先级编辑
中断配置 --代码-----------------
两个中断时
定时器
定时器类型
定时器时基----基本定时器
通用定时器时钟输入
编辑 高级定时器编辑
代码部分-----------------------------
定时器 ---- PWM
PWM控制呼吸灯 ----------------
PWM控制SG90舵机----------------
PWM驱动电机模块
输入捕获测频率
输入捕获测占空比
输入捕获编码器计数
一、环境搭建及介绍 kile4 开发51单片机(内置芯片包)、kile_v5开发STM32手动添加芯片包、如果要开发51许把51的芯片包放在kile5中 、通过注册机key来破解
对应STM芯片包
文章目录 一、需求分析二、技术介绍2.1相关技术2.2开发环境 三、功能实现1、登录2、图书管理2.1图书列表2.2添加图书2.3修改图书信息2.4查询图书信息 3、用户管理3.1借阅图书3.2修改借阅信息3.3归还图书 4、退出系统 四、部分代码实现获取源码 文章最下方获取源码!!!
文章最下方获取源码!!!
文章最下方获取源码!!!
一、需求分析 1、实现登录基本逻辑
2、实现登录后对图书的管理,包括增、删、改、查
3、实现登录后对用户的管理,包括增、删、改、查
4、用户可以借阅处于空闲状态的图书,被借阅后的图书不能再被其他用户借阅,用户可以归还图书,归还后的图书可以再次被借阅
二、技术介绍 2.1相关技术 Spring + SpringMVC + MyBatis(SSM)Jsp + JQueryMysql 2.2开发环境 idea + tomcat
三、功能实现 1、登录 2、图书管理 2.1图书列表 2.2添加图书 2.3修改图书信息 2.4查询图书信息 3、用户管理 3.1借阅图书 3.2修改借阅信息 3.3归还图书 4、退出系统 四、部分代码实现 BookMapper.java
package com.codejams.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.codejams.domain.Book; import org.apache.ibatis.annotations.Mapper; import java.util.List; public interface BookMapper{ List<Book> findAllBook(); Book findBookById(int id); void update(Book book); void save(Book book); void delete(int id); List<Book> findBookByCondition(String condition); void changeStatusToLeisure(String name); void changeStatusToNotLeisure(String name); } BorrowerController.
参考:
文献阅读利器--HistCite安装与使用_histcite下载安装-CSDN博客
Histcite使用-CSDN博客
科研文献工具Histcite介绍-CSDN博客
邮箱发送验证码(nodemailer) 前言:后端实现1、前期准备2、安装nodemailer3、代码实现4、效果图 前言: 想要实现向邮箱发送验证码功能,先来简单地梳理一下思路:
前端用正则表达式简单验证下邮箱格式的正确性向后端发送请求,后端随机生成验证码,并根据前端传递的邮箱发送验证码 当然实际开发中当然没有那么简单,根据实际状况书写。
本文主要讲述后端实现步骤,前端挺容易的,自己研究一下吧。
后端实现 1、前期准备 以163邮箱为例(若没有邮箱,先注册一个新邮箱),先登录,然后点击设置,选择POP3/SMTP/IMAP
再开启服务
2、安装nodemailer npm i nodemailer 官方地址:https://nodemailer.com
3、代码实现 新建 utils 文件夹,该文件夹下新建mailer.js
const express = require('express'); //创建路由对象 const router = express.Router(); // 导入邮件路由处理函数对应的模块 const mail_handler = require("./mailHandler.js"); //挂载具体的路由 router.get('/mailer/sendmail', mail_handler) 同级目录下新建mailHandler.js,该文件用于存放实现发送验证码的函数
// 引入数据库 const db = require("../db/index"); // 引入bryptjs对验证码进行加密 const bcrypt = require("bcryptjs"); // 邮件发送插件 let nodemailer = require("nodemailer"); // 发送验证码函数 export.sendEmail = (req, res) => { // 创建一个SMTP客户端对象 let transporter = nodemailer.
当点击左下角的连接
遇到下面的权限提示时
mkdir: cannot create directory ‘/home/xxx(用户名)/.vscode-server’: Permission denied
可以新建文件 /etc/wsl.conf,并拷贝如下内容
# Automatically mount Windows drive when the distribution is launched [automount] # Set to true will automount fixed drives (C:/ or D:/) with DrvFs under the root directory set above. Set to false means drives won't be mounted automatically, but need to be mounted manually or with fstab. enabled = true # DrvFs-specific options can be specified. options = "
背景 我们希望实现全链路信息,但是代码中一般都会异步的线程处理。
解决思路 我们可以对以前的 Runable 和 Callable 进行增强。
可以使用 ali 已经存在的实现方式。
TransmittableThreadLocal (TTL) 解决异步执行时上下文传递的问题
核心的实现思路如下:
1)异步执行前,把当前线程的 MDC 信息放入执行对象中。
2)异步执行时,把执行对象中的信息放入 MDC 等信息。
3) 异步执行后,清空执行对象。
问题 Runable 和 Callable 只是接口,没有额外信息,所以需要进行增强。
实现方式 接口定义 package com.github.houbb.heaven.support.concurrent.context; import java.util.Map; /** * 跨线程处理类 * * @since 0.3.0 */ public interface CrossThreadProcessor { /** * 初始化上下文 * @param contextMap 上下文 */ void initContext(Map<String, Object> contextMap); /** * 执行之前 * @param contextMap 上下文 */ void beforeExecute(Map<String, Object> contextMap); /** * 执行之后 * @param contextMap 上下文 */ void afterExecute(Map<String, Object> contextMap); } 对可执行接口进行增强 package com.
一、数组赋值有两种情况: (1)声明的同时整体赋值
int a[5]={1,2,3,4,5}; (2) 声明后再通过索引单个赋值
int a[5]; a[0]=1; a[1]=2; a[2]=3; a[3]=4; a[4]=5; 注意!在c/c++中不允许将数组声明和整体赋值分开!
int a[5]; a={1,2,3,4,5}; 如果像上面这样的话则会出现以下两个错误:
第一个错误:因为C/C++中,数组名就是一个指针常量,这个指针常量里面存储的是数组第一个内存的地址,而常量是不能被赋值的
第二个错误 :因为一个指针只能被赋予一个值
百度网盘-文档在线预览
Python实现检查安卓设备是否运行monkey并杀掉进程
实测有效:
import subprocess udId = "ASDF" def run_command(cmd): process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) output, error = process.communicate() return output.decode().strip() def check_and_kill_pid(udId): #read run_command(f'adb -s {udId} shell ps > shellps.log') #check with open('shellps.log', 'r') as file: for line in file: if 'com.android.commands.monkey' in line: apk_pin = line.split()[1] break else: apk_pin = None #kill if apk_pin: print(f"Monkey process PID : {apk_pin}") run_command(f'adb -s {udId} shell kill {apk_pin}') print("Monkey test end.") else: print("
Springboot 项目的服务器部署与发布 本文旨在记录第一次将Springboot项目部署到服务器并发布的过程、遇到的问题及解决办法。
一、在IDEA中将项目打包 设置访问端口号 在application.yml这个文件中加入(端口多少由自己设置)
利用idea打包 右侧边栏中点击Maven,打开lifecycle,找到其中的package选项,双击。
当看到BUILD SUCCESS说明已经打包成功了。
jar包保存在项目target文件夹中
二、 连接服务器 租用云服务器 市面上有许许多多的服务器,可自行选择。这里以Google cloud云服务器为例介绍。
1、访问 Google cloud 官网 https://cloud.google.com/,点击右上角“Start Free”按钮。 2、登录谷歌帐号 使用谷歌帐号登录,没有的话注册一个。
2019年,国家/地区选项栏已取消了“中国”选项,这里我们可以随便选一个,比如香港或者台湾。
3、填写注册信息 填写注册信息,包括姓名、地址、联系方式等。
绑定支持双币个人信用卡,会扣除1美元作为卡验证费用,验证通过后会原路返还。
4、完成注册 填写完上述资料后,点击“START MY FREE TRIAL”按钮,便提示已成功注册。获得 300 美元的赠金,一年内有效,而且承诺如果赠金用完,不会主动扣费。
创建VM实例 1、选择 “Computer Engine” 点击菜单中的 Compute Engine,或者直接点击热门产品栏中的“Compute Engine”
2、选择创建 VM 实例 点击创建按钮,来创建一个 VM 实例。
3、填写机器配置 填写名称,选择区域,机器类型等内容,右侧会显示每月的费用,注意累计费用不要超出赠金总额。这里选择了 1 个共享 vCPU + 1.7 GB 内存。
4、启动磁盘 选择启动磁盘,可以修改磁盘大小和类型,还可以选择操作系统。
5、填写网络接口信息 填写网络接口信息,外部 IP 选择固定 IP。
6、完成创建 确认机器配置信息后,点击创建按钮完成 VM 实例的创建。
一、概述
Stream流是Java 8
API添加的一个新的抽象,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)。
Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。
二、如何使用Stream流?
1.创建Stream流。
(1)Stream创建。
Stream<String> stream1 = Stream.of("one","two"); (2) Collection集合创建。
List<String> list = Arrays.asList("one","two"); Stream<String> stream = list.stream(); // 串行Stream Stream<String> parallelStream = list.parallelStream(); // 并行Stream (3)Array数组创建
String[] strArr = {"one","two"}; Stream arrayStream = Arrays.stream(strArr); (4) 文件创建
try { Stream<String> fileStream = Files.lines(Paths.get("file.txt"), Charset.defaultCharset()); } catch (IOException e) { e.printStackTrace(); } (5) 函数创建
generator:
Stream<String> stream = Stream.generate(()->"a").limit(2); generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断。
iterator:
Stream<Integer> iterateStream = Stream.iterate(0, s -> s + 2).
方式一:直接上传(auto-upload=“true”) 一:前端样式以及效果:
前端样式代码如下:
<template> <div> <el-upload action="api/admin/file/uploadfoodpicture" list-type="picture-card" auto-upload="true" :on-success="sucuploadimg"> <i class="el-icon-plus"></i> </el-upload> <img :src="imgUrl" alt="图" /> </div> </template> <script> export default { data() { return { imgUrl: '' }; }, methods: { sucuploadimg(res) { this.imgUrl = res.data; } } } </script> 二:后端Controller层
@RestController @RequestMapping("/admin/file") @Slf4j @Api(value = "文件相关接口",tags = {"文件相关接口操作接口"}) public class filecontroller { @PostMapping("/uploadfoodpicture") @ApiOperation("上传食物图片") public Result uploadFoodPicture(@RequestBody MultipartFile file) throws Exception { //获取图片原文件名 log.info("启动图片上传功能"); String url= FileUtil.
在mac 电脑中,直接执行
java -version 显示Jdk的版本为1.8
然后打印Java环境变量
在终端中执行
echo $JAVA_HOME 1、情况一:发现环境变量是空的
我草,没配置环境变量怎么能使用Java ,和查看jdk版本
2、情况二:环境变量为:/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
进入这个路径下面,查看真的有jdk吗? 这两种情况,都说明Jdk的环境变量是配置错误的。
我们查看真实的jdk的安装路径
mac 的路径一般为:
/Library/Java/JavaVirtualMachines/
验证一下:
zhaohui@zhaohuideMacBook-Pro JavaVirtualMachines % cd /Library/Java/JavaVirtualMachines/ zhaohui@zhaohuideMacBook-Pro JavaVirtualMachines % ls jdk1.8.0_321.jdk 果然是这个路径。
然后我们再配置真实的JAVA_HOME 路径
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_321.jdk/Contents/Home
项目场景: 提示:这里简述项目相关背景:
从码云上面克隆的项目,在IDEA里面不能更新和提交,以前IDEA里面Git是可以正常用的.
问题描述 提示:这里描述项目中遇到的问题:
更新失败 Invocation failed Server returned invalid Response. java.lang.RuntimeException: Invocation failed Server returned invalid Response. at git4idea.GitAppUtil.sendXmlRequest(GitAppUtil.java:22) at git4idea.http.GitAskPassApp.main(GitAskPassApp.java:56) Caused by: java.io.IOException: Server returned invalid Response. at org.apache.xmlrpc.LiteXmlRpcTransport.sendRequest(LiteXmlRpcTransport.java:242) at org.apache.xmlrpc.LiteXmlRpcTransport.sendXmlRpc(LiteXmlRpcTransport.java:90) at org.apache.xmlrpc.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:72) at org.apache.xmlrpc.XmlRpcClient.execute(XmlRpcClient.java:194) at org.apache.xmlrpc.XmlRpcClient.execute(XmlRpcClient.java:185) at org.apache.xmlrpc.XmlRpcClient.execute(XmlRpcClient.java:178) at git4idea.GitAppUtil.sendXmlRequest(GitAppUtil.java:19) ... 1 more unable to read askpass response from 原因分析: 提示:这里填写问题的分析:
解决方案: 提示:这里填写该问题的具体解决方案:
勾选了下面的选项就好了
目录
案例六:抢红包
案例七:找素数
方法一:
方法二:
方法三:
案例八:实现双色球
关于本项目所用所有方法的解释:
案例六:抢红包 一个大V直播时发起了抢红包活动,分别有: 9、666、 188、520、 99999五个红包。请模拟粉丝来抽奖,按照先来先得,随机抽取,抽完即止,注意:一个红包只能被抽一-次,先抽或后抽哪一个红包是随机的。
方法:写个for循环控制抽奖5次,每次抽奖,都从数组中随机找出一个金额,如果该金额不是0,则代表抽中,接着用0替换该位置处的金额,然后继续下一个粉丝的抽奖;如果抽中的金额发现是0,代表该位置处的红包之前被别人抽走了,则从新从数组中随机找出一个金额,继续判断!直至抽中的金额不是0!
代码:
package com.itheima; import java.util.Random; import java.util.Scanner; public class Test6 { public static void main(String[] args) { int[] moneys={9,66,188,520,99999}; start(moneys); } public static void start(int[] moneys){ //假设money=[9,66,188,520,99999] Scanner sc=new Scanner(System.in); Random r=new Random();//随机数对象 //定义一个for循环,控制抽奖五次 for (int i = 1; i <= 5; i++) { System.out.println("请您输入任意内容进行抽奖"); sc.next();//等待用户输入内容,回车才往下走 //为当前粉丝随机一个红包出来 while (true) {//除非抽到不等于0的红包,否则一直抽 int index = r.nextInt(moneys.length);//获得0-4的随机索引 int money = moneys[index];//将对应索引的钱取出来 //判断这个红包不为0 if (money !
stuName = ['白柳', '牧四城', '刘佳仪'] # stu stuId = ['006', '004', '061'] # sid stuClass = ['A003', 'A003', 'A001'] # clas def showMune(): print("""1.添加学生信息 2.删除学生信息 3.修改学生信息 4.查询所有学生信息 0.退出系统""") print("=========") def add(): global stuName global stuId global stuClass stu = input("请输入学生姓名(输入exit退出):") while stu != 'exit': for i in range(0, len(stuName)): while stu == stuName[i]: stu = input("学生姓名重复,请重新输入") if stu == 'exit': main() stuName.append(stu) sid = input("请输入学生id:") for i in range(0, len(stuId)): while sid == stuId[i]: sid = input("
目录
题目:用折半查找在一个有序数组中查找一个具体的数字n
题目:用折半查找在一个有序数组中查找一个具体的数字n 为了方便讲解,我们假设这里的有序数组是arr[ ] = {1,2,3,4,5,6,7,8,9,10},要查找的数是 7 。
第一步,我们标出这个有序数组的下标,并找出最左边、最右边和中间的下标:
由图可见,下标left = 0,mid = 4,right = 9。
第二步,将下标为 mid 的数字与要查找的数字 7 进行比较:
此时因为 arr[mid] = 5 < 7,所以令 left = mid +1 = 5,right = 9不变,此时 mid = (5 + 9)/2 =7。这就缩小了一半的查找范围。
第三步,继续将要下标为 mid 的数与要查找的数字 7 进行比较:
此时因为 arr[mid] = 8 > 7,所以令 left = 5不变,right = mid - 1 = 6,此时mid = (5 + 6 )/2 = 5。再次缩小一半的查找范围。
目录
一、什么是DNS?
二、DNS工作原理及解析过程
三、各种资源记录
1、区域解析库详解:
2、主配置文件详解
四、试验环境
五、搭建主DNS服务器
1、配置ip地址
2、安装bind
3、修改主配置文件
4、新建正向区域配置文件
5、新建反向区域配置文件
6、重启named服务使刚才的修改生效
7、修改网卡加入dns
8、遇到的问题
六、从服务器搭建
1、安装bind
2、修改主配置文件
3、查看主域名服务器同步过来的文件
4、验证
一、什么是DNS? DNS是域名系统(Domain Name System)的缩写,它是一个用于将域名解析为IP地址的系统。在互联网上,每个设备都需要一个唯一的IP地址来进行通信,但人类更容易记住的是域名而非IP地址。DNS的作用就是将人类可读的域名解析为对应的IP地址,从而使设备能够相互通信和访问互联网上的各种服务和资源。
二、DNS工作原理及解析过程 我们以www.baidu.com 这个域名为例,来看一看当你访问 www.baidu.com 时,会发生哪些事
1.先查找本地 DNS 缓存(自己的电脑上),有则返回,没有则进入下一步
2.查看本地 hosts 文件有没有相应的映射记录,有则返回,没有则进入下一步
3.向本地 DNS 服务器(一般都是你的网络接入服务器商提供,比如中国电信,中国移动)发 送请求进行查询,本地DNS服务器收到请求后,会先查下自己的缓存记录,如果查到了直接返回就结束了,如果没有查到,本地DNS服务器就会向DNS的根域名服务器发起查询请求:请问老大, www.baidu.com 的ip地址是啥?
4.根域名服务器收到请求后,看到这是个 .com 的域名,就回信说:这个域名是由 .com 小老弟管理的,你去问他好了,这是.com老弟的联系方式(ip1)。
5.本地 DNS 服务器接收到回信后,照着老大给的联系方式(ip1),马上给 .com 这个顶级域名服务器发起请求:请问 .com 老大哥,www.baidu.com 的ip地址是啥?
6. .com 顶级域名服务器接收到请求后,看到这是 baidu.com 的域名,就回信说:这个域名是 .163.com 小老弟管理的,你就去问他就行了,这是他的联系方式(ip2)
7.本地 DNS 服务器接收到回信后,按照顶级域名老大哥的指引(ip2),又向 .163.com 这个权威域名服务器发起请求:请问 baidu.com 老大哥,请问 www.
static只会初始化1次 1.static每次初始化相同的值,代码1中的static初始化 放到for循环中,每次进行初始化相同的值,先看一下这个结果。我们发现他们的内存布局是紧按着的。结果:2.每次初始化不同的值,同样也放到for循环中去,我们这时候来看看内存布局,就会发现有意思的事情了!结果: 2.得到规律:3.测试代码14.我们修改*p = 0,之后会发生什么?结论:我们发现y的值进行了变化 1.static每次初始化相同的值,代码1中的static初始化 放到for循环中,每次进行初始化相同的值,先看一下这个结果。我们发现他们的内存布局是紧按着的。 #include <iostream> using namespace std; int main() { int n = 2; for(int i = 0; i < n; i ++) { static int a = 10; static int b = 10; static int c = 20; static int d = 10; static int e = 10; static int f = 100; int *tmp = &a; for (int i = 1; i < 20; ++i) { cout << "
计算机从给定的数据中学习规律,即从观测数据(样本)中寻找规律、建立模型,并利用学习到的规律(模型)对未知或无法观测的数据进行预测。
我们以房价预测为例,来说明机器学习的全过程。
1-数据收集和准备:
首先,我们需要收集相关的房屋数据,包括房屋的特征(如面积、卧室数量、地理位置等)以及对应的销售价格。
接着,对数据进行清洗和预处理,包括处理缺失值、异常值,进行特征选择和特征工程等。
2-数据划分:
将数据集划分为训练集和测试集。通常,我们将大部分数据用于训练模型,少部分数据用于评估模型性能。
3-模型选择和训练:
根据任务的特点和需求,选择适合的回归模型,如线性回归、决策树回归等。
使用训练集来训练模型,模型通过学习训练数据中的模式和关系来建立预测函数。
4-模型评估:
使用测试集来评估已训练的模型的性能。比较模型预测结果与真实房价标签之间的差异。
根据评估指标(如均方误差、决定系数等)来判断模型的预测能力。
5-模型调优和改进:
根据评估结果,对模型进行调优和改进。可以尝试更换不同的特征、调整模型参数等来提升预测性能。
6-预测和应用:
当模型经过调优后,可以使用该模型来进行房价预测。给定一组未知房屋特征,模型可以预测出相应的房价。
7-持续监测和更新:
一旦将模型应用到实际情况中,我们需要持续监测模型的表现,并根据新的数据进行模型更新和改进,以确保模型的持续有效性。
以上就是机器学习的典型全过程。通过数据收集和准备、数据划分、模型选择和训练、模型评估、模型调优和改进、预测和应用,以及持续监测和更新等步骤,我们可以构建一个能够预测房价的机器学习模型。当然,具体的应用场景和任务可能会有所不同,但这个过程基本上适用于大多数机器学习问题。
# 导入类库 import numpy as np from numpy import arange from matplotlib import pyplot from pandas import read_csv from pandas import set_option from pandas.plotting import scatter_matrix from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.model_selection import KFold from sklearn.model_selection import cross_val_score from sklearn.model_selection import GridSearchCV from sklearn.linear_model import LinearRegression from sklearn.
1 前言 🔥 优质竞赛项目系列,今天要分享的是
🚩 python opencv 深度学习 指纹识别算法实现
🥇学长这里给一个题目综合评分(每项满分5分)
难度系数:3分工作量:4分创新点:4分 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!
🧿 更多资料, 项目分享:
https://gitee.com/dancheng-senior/postgraduate
2 指纹识别方式 目前市面上有两种指纹识别,分别是光学式和电容式指纹识别。
2.1 电容式指纹识别 电容式指纹识别要比光学式的复杂得多,其原理是将压力感测、电容感测、热感测等感测器整合于一块芯片中,当指纹按压芯片表面时,内部电容感测器会根据指纹波峰与波谷而产生的电荷差(或是温差),形成指纹影像,再通过与算法内部的指纹库进行匹配,从而完成指纹识别。
电容式指纹识别技术较为复杂,对技术研发和积累有较高要求,并且涉及大量专利,算法得实现也相对困难,所以目前全球只有少数公司能在这方面提供领先的技术产品。
2.2 光学式指纹识别 然后是光学式指纹识别,大家常见的指纹考勤机就是光学式指纹识别。
这类光学式指纹识别主要包括4个方面功能
1.指纹模块负责采集指纹图像。2.数字信号处理器主要把采集到的指纹图像转化为数字信号。3.微控制器是整个系统的控制单元,在这里将进行指纹的验证以及输出指令。4.液晶显示器将输出指纹验证结果。 当我们把手指放在指纹考勤机上时,通过镜面反射原理,指纹模块就会采集指纹图像
接着指纹图像就会被数字信号处理器转换成数字信号
然后通过微控制器将数字信号与指纹库里的指纹进行匹配,匹配结果将通过液晶显示器显示出来。这就是光学式指纹识别的工作原理。
电容式与光学式指纹识别主要在指纹的采集方式上拥有较大差异,而在指纹的验证过程中则基本类似。然而因为电容式指纹识别拥有体积小、适用性广的优点,已经有越来越多的设备采用电容式指纹识别,未来的主流将是电容式指纹识别。
3 指纹识别算法实现 3.1 指纹识别算法流程 3.2 指纹图像预处理 1.图像归一化2.图像分割3.图像二值化4.细化 几个步骤 当然有一些算法为了追求极致,中间还利用了增强、多重滤波等方式,但最根本的目的都是为了在细化之后保留指纹信息并且方便后续处理。
预处理之后是特征点的提取,也就是找到指纹图像当中有价值,具有唯一性和不变性的信息。主要特征点有端点、分叉点几类,在提取部分除了简单的遍历检索确认之外,还有比较重要的一步是伪特征点的去除,主要是针对图像不清晰、不完整导致的断点和边缘端点等。
最后一步是特征点的匹配,将指纹库中的指纹信息与提取的目标图像特征点进行对比。主流算法是以指纹中心点为依据,根据特征点所在的角度及距离,确认其所在位置,根据匹配度计算是否是同一手指的指纹。
3.3 指纹图像目标提取 指纹图像目标提取主要是将指纹图片中提取出来,也叫做指纹图像分割。
图片的前景通过观察可知是由条状或者圆形的一些组成,而底图其他部分只是一个均匀的底色而已。
我们使用非常简单的手段,基于局部梯度就可以很容易实现我们的目标。
方差梯度法提取指纹:
指纹图像通常由前景区域(包含有指纹的脊线和谷线)和背景区域这两部分组成。一般来说,在指纹图像的前景区域中,指纹的脊线和谷线的灰度差是较大的,因而其灰度统计特性局部灰度方差很大;而在指纹图像的背景区域中,两者的方差是很小的。基于这一特性,可以利用指纹图像的局部方差来进行分割。因此,这种方法也被称为方差梯度法。
的灰度平均值 M和方差 Var计算方法:
提取前的指纹图像:
提取后的指纹图像:
可以看到,学长做的还有些噪点,但是也很好解决,做一次形态学操作即可:
这样噪点就去除了。
3.4 指纹图像增强 3.5 指纹特征提取 人体指纹的特征可以反映在给定的人类群体里来自不同手指的指纹之间相似的程度。
指纹的特征信息很多,所有的这些指纹特征信息构成了庞大的指纹特征集合。
指纹的细节特征主要指的是纹线端点(RidgeEnding)和纹线分叉点(RidgeBifurcation)。
纹线端点指的是纹线突然结束的位置,而纹线分叉点则是指纹线一分为二的位置。
大量的统计结果表明,使用这两类特征点就足以描述指纹的唯一性。
Minutia Cylinder-Code (MCC) ,该算法是非常著名的指纹特征识提取算法,
一、Shell编程☘️ 1. Shell指令快捷操作 1. echo # 系统指令 $ echo $(pwd) # 对于系统自带的pwd,此处不能写echo $pwd # 自定义变量 $ foo=$(pwd) $ echo $foo # 不同于pwd,对于自定义的foo,不能用$(foo) 2. !! # 假设你先执行了以下原本需要权限的指令 $ mkdir /mnt/xuyang # 终端立即提示你:Permission denied,这时可通过如下指令进行简化:【!!默认指代上次指令内容】 $ sudo !! # 该句等效于:sudo mkdir /mnt/xuyang 3. rm与rmdir # rmdir 仅用于删除空目录,如果要删除非空目录,需要使用 rm 命令 # rm -rf会递归地删除目录及其所有子目录和文件。选项 -r 表示递归删除,选项 -f 表示强制删除,即不提示确认。 $ rm -rf directory/ 4. &&、|| # shell脚本也可以执行逻辑指令 # &&前面为真,故会正常echo $ true && echo "hello peter parker"
一、概述
java.util.Optional<T>类是一个封装了Optional值的容器对象,Optional值可以为null,如果值存在,调用isPresent()方法返回true,调用get()方法可以获取值。
Optional是在java.util包下的一个用于代替null的一个工具类。
Optional 类的引入很好的解决空指针异常。
import java.util.Optional; 二、使用目的
1.避免使用null检查。
开发中,几乎都会遇到NullPointerException异常,大都是通过if判定null值,比如:
public void addUserRole(User user) { if (user == null) { return; } String roleId = user.getRoleId(); if (roleId == null) { return; } } 这种方式不方便出错后调试,因此,Java 8中引入了一个新的类Optional,用以避免使用null值引发的种种问题。
三、构建Optional
Optional类提供类三个方法用于实例化一个Optional对象,它们分别为empty()、of()、ofNullable()。
(1)empty()方法:empty()方法用于创建一个没有值的Optional对象:
Optional<String> emptyOpt = Optional.empty(); (2)of()方法:of()方法使用一个非空的值创建Optional对象。
String str = "my"; Optional<String> notNullOpt = Optional.of(str); (3)ofNullable()方法:ofNullable()方法接收一个可以为null的值。
Optional<String> nullableOpt = Optional.ofNullable(str); 如果str的值为null,得到的nullableOpt是一个没有值的Optional对象。
四、Optional中相关方法
1.isPresent(): 持有非空值,返回true;否则false;
Optional optional = Optional.ofNullable(null); Optional optional1 = Optional.of(""); Optional optional2 = Optional.
可以使用MATLAB中的imread函数读取彩色图像,然后使用imsplit函数将其分割成RGB三通道图像,最后使用imshow函数分别显示三个通道的图像。具体代码如下
% 读取彩色图像
img = imread('color_image.jpg');
% 分割成RGB三通道图像
[red, green, blue] = imsplit(img);
% 显示三个通道的图像
subplot(1,3,1), imshow(red), title('Red Channel');
subplot(1,3,2), imshow(green), title('Green Channel');
subplot(1,3,3), imshow(blue), title('Blue Channel');
前言 当我们在构建个人博客或网站时,为了提高资源访问的速度和稳定性,经常会使用对象存储服务,如 MinIO,来存储静态资源,例如图片。而为了让网站看起来更加专业和美观,我们通常不希望用户直接看到后端存储的地址和端口,这时就可以利用 Nginx 进行反向代理,隐藏真实的后端服务地址。
问题描述 在尝试使用 Nginx 反向代理 MinIO 时,遇到了一个问题:虽然代理配置能成功访问 MinIO,但尝试访问存储在其中的图片时会收到一个 400 Bad Request: malformed Host header 的错误。
初步的配置如下:
server { listen 80; server_name image.laodengtou.com; location / { proxy_pass http://localhost:9001; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; } } 解决方案 经过一系列的测试和尝试,以下是一个工作正常的配置:
server { listen 80; server_name your_domain.com; location / { proxy_pass http://localhost:9001/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; rewrite ^/(.*)$ /$1 break; } } 对比这两个配置,可以看到以下几点差异:
proxy_set_header Host:在原始配置中,使用了 \$host,这可能导致了“Host”头部的错误解析。而在工作的配置中,使用 $http_host 可以确保 HTTP 请求头中的 Host 信息被正确传递给 MinIO。proxy_set_header X-Forwarded-For:此头用于传递原始请求的 IP 地址,有时对于某些应用来说这是必要的。rewrite:通过使用重写规则,可以确保请求的 URL 路径不会被错误地解释或修改。 效果展示 总结 Nginx 的反向代理功能强大,但在配置时需要注意一些细节,特别是当后端服务对请求头有特定要求时。对于 MinIO 这样的对象存储服务,确保传递正确的 Host 信息是关键。如果你有其他相关的经验或知识,欢迎在评论区分享!
目录
一、背景
二、查看数据库所有表的记录条数
1、带模式名查询
2、不带模式名查询
三、查看数据库所有表的大小
1、查看单表大小
2、查看库下的所有表大小
3、查看索引大下
四、查看所有库的大小
一、背景 在实际实际生产环境中,有时候我们往往需要知道最近的数据库使用情况,已及数据库表的使用情况,那我们就需要使用sql来查询,当然有的人也会说可以使用监控来查看数据库的使用情况,这个确实是没有问题,但是使用监控有局限性,只能查看到数据库的整体使用情况,具体的表大小是看不到的,接下来我们一起来实操一下
二、查看数据库所有表的记录条数 1、带模式名查询 SELECT schemaname AS schema, relname AS table, n_live_tup AS record_count FROM pg_stat_user_tables; 效果如下:
2、不带模式名查询 SELECT relname AS table, n_live_tup AS record_count FROM pg_stat_user_tables WHERE schemaname = 'public'; 效果如下:
三、查看数据库所有表的大小 1、查看单表大小 select pg_size_pretty(pg_relation_size('表名')); 效果如下:
2、查看库下的所有表大小 select relname, pg_size_pretty(pg_total_relation_size(relid)) as size from pg_stat_user_tables; 效果如下:
3、查看索引大下 select pg_size_pretty(pg_relation_size('索引名')) as size; 四、查看所有库的大小 select datname, pg_size_pretty (pg_database_size(datname)) AS size from pg_database; 效果如下:
前言 X-Frame-Options作为HTTP头的一部分,是一种用于保护网站免受点击劫持攻击的安全措施。网站可以通过设置X-Frame-Options或csp报头来控制网站本身是否可以被嵌套到iframe中。
漏洞描述 Clickjacking(点击劫持)是一种安全漏洞,通常出现在网站未配置适当的安全标头时,该漏洞由互联网安全专家罗伯特·汉森和耶利米·格劳斯曼在2008年提出的。当网站未配置X-Frame-Options头时,它可能受到点击劫持攻击的威胁。这意味着攻击者可以在其恶意网站中将目标网站嵌套到iframe中,并引导用户执行未经授权的操作,使用户不知情地与目标网站互动。
漏洞危害 未配置X-Frame-Options头可能导致以下危害:
用户被欺骗执行不希望执行的操作。用户的敏感数据(如用户名和密码)可能被窃取。网站的访问量可能被滥用,用于刷点击量或推广不良内容。攻击者可能传播恶意软件或进行其他恶意活动。 利用场景 点击劫持的利用场景如:刷点击量、骗取关注、点击广告或链接、点击广告或链接等。以刷点击量为例,假设存在某个场景,并描述其利用方式
假设a博客网站未配置X-Frame-Options头,小明在该博客网站上发布了一篇博客,现在小明想利用点击劫持来刷该博客的访问量,那小明可能采用的点击劫持攻击来刷博客访问量的过程如下:
博客网站未配置X-Frame-Options头:博客网站的管理员未正确配置X-Frame-Options头,这意味着博客网站的内容可以被嵌套到iframe中。小明创建恶意网站:小明创建了一个恶意网站,其中包含了点击劫持攻击的代码。在恶意网站上嵌套博客:小明在他的恶意网站中创建一个透明的iframe,将博客网站的URL嵌套到iframe中。用户访问小明的恶意网站时,他们可能看到一个吸引人的内容,而不知道博客网站的内容也被加载到了iframe中。引导用户进行点击操作:小明可能在恶意网站上提供吸引人的内容,例如奖品抽奖、免费资源等,并引导用户执行点击操作。触发点击劫持:当用户在恶意网站上执行点击操作时,iframe中的博客网站也会接收到相同的点击操作。用户点击被计入博客网站访问量:尽管用户认为他们只是在小明的恶意网站上与吸引人的内容互动,但实际上他们的点击操作也被重定向到了博客网站上。这将导致博客网站的访问量增加。小明达到他的目标:小明的目标是刷博客的访问量,他可能通过引导更多用户来访问他的恶意网站,从而增加博客网站的访问量。 这个过程说明了如何利用点击劫持攻击来增加博客网站的访问量,尽管用户不清楚他们的点击操作实际上也在访问博客网站。
判断是否存在点击劫持问题 查看响应头是否存在“X-Frame-Options: ”或 “Content Security Policy”报头,如果在响应头中找不到"X-Frame-Options"报头,并且不存在Content Security Policy(CSP)报头。那么网站可能存在点击劫持漏洞。 为了判断的准确性,可以进行下一步判断
在本地新建一个html文件,src = 'url' 中填写要测试的网站 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>点击劫持示例</title> </head> <body> <h1>点击劫持示例页面</h1> <button id="clickMe">点击我</button> <iframe id="evilIframe" style="opacity: 0;"></iframe> <script> document.getElementById('clickMe').addEventListener('click', function() { // 在点击按钮时,触发iframe中的操作 document.getElementById('evilIframe').style.opacity = '1'; document.getElementById('evilIframe').src = 'https://stack.chaitin.com'; }); </script> </body> </html> 代码说明:
<iframe> 元素中,style="opacity: 0;":通过将opacity样式设置为0,使该iframe最初是不可见的。src属性:在此示例中,src属性被省略了,因此最初没有指定要加载的内容。document.getElementById('clickMe').addEventListener('click', function() {...}):这行代码为按钮添加了一个点击事件监听器。当用户点击按钮时,以下操作将被执行: document.getElementById('evilIframe').style.opacity = '1';:这行代码将第一个<iframe>元素的opacity样式设置为1,使其变得可见。document.getElementById('evilIframe').src = 'https://stack.chaitin.com';:这行代码将第一个<iframe>元素的src属性设置为"https://stack.chaitin.com",导致该iframe加载该网站的内容。 点击 点击我,如果成功加载了目标网站,说明目标网站可以被其他网站嵌入 ,存在点击劫持问题 https://stack.