目录 json的数据提取1 什么是json2 json模块中方法的学习3 jsonpath模块的学习3.1 jsonpath介绍3.2 JsonPath 对于 JSON 来说,相当于 XPath 对于 XML。3.3 JsonPath与XPath语法对比:3.4 语法使用示例3.5 代码示例: json的数据提取 1 什么是json JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。
2 json模块中方法的学习 其中类文件对象的理解:
具有read()或者write()方法的对象就是类文件对象,比如f = open(“a.txt”,”r”) f就是类文件对象
具体使用方法:
#json.dumps 实现python类型转化为json字符串 #indent实现换行和空格 #ensure_ascii=False实现让中文写入的时候保持为中文 json_str = json.dumps(mydict,indent=2,ensure_ascii=False) #json.loads 实现json字符串转化为python的数据类型 my_dict = json.loads(json_str) #json.dump 实现把python类型写入类文件对象 with open("temp.txt","w") as f: json.dump(mydict,f,ensure_ascii=False,indent=2) # json.load 实现类文件对象中的json字符串转化为python类型 with open("temp.txt","r") as f: my_dict = json.load(f) 3 jsonpath模块的学习 3.1 jsonpath介绍 用来解析多层嵌套的json数据;JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript, Python, PHP 和 Java。
参考文档:https://blog.csdn.net/killfat/article/details/94166915
参考文档里的那篇是2019的,跟现在的有点不一样,所以就写了这篇
1.对象存储oss,没有开通的开通下
2.右侧单击创建Bucket,选择与保存镜像同一地域的Bucket。切记要选择标准存储,并记住Bucket的名字。
其他选项无特殊需要时默认,点击开通。
3.工单-提交工单
4.进入默认提交工单界面,坑的地方来了,一直找不到人工,弄了好久才知道
输入在线,左边对话框会出现“联系人工”,点进去
5.产品选择 云服务器ECS
6.问题分类选择镜像咨询
7.提交
8.进入提交的页面
原因我写的是: 本地留存, 节约快照成本, 方便以后GPU配置
然后点击提交
9。在我的工单记录里可以找到这条
10。售后工程师回复后,回到ECS-实例与镜像-实例-更多-磁盘和镜像-创建自定义镜像
资源组选择默认的那个,然后创建
镜像-镜像列表-更多-导出镜像
官方文档说可以选择镜像格式的,我这里没有,所以默认是RAW格式
导出镜像文档:https://help.aliyun.com/document_detail/58181.html?spm=5176.10695662.1996646101.searchclickresult.63927cadOUcAeQ
对象存储oss-Bucket列表-文件管理-更多-下载
如图,“1”是下载的东西,层层解压得到3,所谓的raw格式镜像
下面是raw转vmdk的帖子(我用的虚拟机是VM)
https://blog.csdn.net/xuanwolanxue/article/details/105833091
最终经过多番查阅,终于发现一个好工具qemu。 它里面有一个qemu-img工具,可以将raw image转换成vmdk,也可以将vmdk转换成raw image,非常方便,如何至宝,现记录入下:
qemu-img convert usb.img -f raw -O vmdk out.vmdk
1
usb.img: qnx BSP包编译生成的raw image文件
-f raw : 指定要转换的输入文件格式为raw image类型
-O vmdk : 指定转换输出格式为VMware的虚拟硬盘格式
打开vm-文件-新建虚拟机,选择自定义,稍后安装操作系统,使用现有虚拟磁盘,吧vmdk选中就行了
出现问题:
虚拟机启动不起来,具体原因:no floppy controller found!
暂时无法解决,记录下
前提:idea卡顿有很多方面问题 ,关键是能定为到。
1.内存不足:
网上有很多内存配置优化的我这就不说了。说一个小技巧,可以在idea实时看到做大内存和当前内存。
就可以看到当前idea使用内存多少了。
2.排查真正卡顿的问题
2.1.看日志,看idea的日志,mac下idea日志是在 ~/Library/Logs/IntelliJIdea2019.3 下,可以找找,有个文件为idea.log,
2.2 看idea使用过程中cpu占用高的进程。
idea导航栏有个Help栏,下面有个监控可以看当前占用cpu最高的进程,这样你复现一下卡顿的场景,就可以在上面看到是哪一个进程导致的卡顿了,然后如果是插件就禁用掉,大部分都是插件问题,别的在百度。
一、
list转map
Map<Long, User> maps = userList.stream().collect(Collectors.toMap(User::getId,Function.identity()));
看来还是使用JDK 1.8方便一些。
二、
另外,转换成map的时候,可能出现key一样的情况,如果不指定一个覆盖规则,上面的代码是会报错的。转成map的时候,最好使用下面的方式:
Map<Long, User> maps = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(), (key1, key2) -> key2));
三、
有时候,希望得到的map的值不是对象,而是对象的某个属性,那么可以用下面的方式:
Map<Long, String> maps = userList.stream().collect(Collectors.toMap(User::getId, User::getAge, (key1, key2) -> key2));
四、
//List 以ID分组 Map<Integer,List>
Map<Integer, List> groupBy = appleList.stream().collect(Collectors.groupingBy(Apple::getId));
System.err.println(“groupBy:”+groupBy);
{1=[Apple{id=1, name=‘苹果1’, money=3.25, num=10}, Apple{id=1, name=‘苹果2’, money=1.35, num=20}], 2=[Apple{id=2, name=‘香蕉’, money=2.89, num=30}], 3=[Apple{id=3, name=‘荔枝’, money=9.99, num=40}]}
对element中Pagination 分页默认样式的的修改 pagination原本样式 pagination原本样式 ## 最后的效果图
改变pagination的中的附加功能的位置使其分散两边
原本代码
<div class="clearfix"> <el-pagination class="pagination" background :current-page="currentPage4" :page-sizes="[10, 20, 30, 40]" :page-size="10" layout="sizes,total,jumper,prev,pager,next" :total="1000"> </el-pagination> </div> 我是先使整个分页组件使用text-align让所有的附加组件向右对齐,如下图
然后再使前三个附加组件向左浮动,以及修改默认附件组件样式
<style scoped lang="scss"> .pagination { margin-top: 20px; text-align: right; } /deep/ .pagination .el-pagination__sizes { float: left; } /deep/ .pagination .el-pagination__total { float: left; } /deep/ .pagination .el-pagination__jump { float: left; } </style> 对element默认样式更改需要在css选择器前加/deep/
toString() 将数组转换成一个字符串toLocalString() 把数组转换成本地约定的字符串join() 将数组元素连接起来以构建一个字符串 toString()
数组中 toString() 方法能够把每个元素转换为字符串,然后用逗号连接输出显示。 var a = [1,2,3,4,5,6,7,8,9,0]; var s = a.toString(); //把数组转换为字符串 console.log(s); //返回字符串“1,2,3,4,5,6,7,8,9,0” console.log(typeof s); //返回字符串string,说明是字符串类型 当数组用于字符串环境中时,JS 会自动调用 toString() 方法将数组转换成字符串。在一些情况下,需要明确调用这个方法。
var a = [1,2,3,4,5,6,7,8,9,0]; var b = [1,2,3,4,5,6,7,8,9,0]; var s = a + b; //数组连接操作 console.log(s); //返回“1,2,3,4,5,6,7,8,9,01,2,3,4,5,6,7,8,9,0” console.log(typeof s); //返回字符串string,说明是字符串类型 toString() 在把数组转换成字符串时,首先要将数组的每个元素都转换为字符串。当每个元素都被转换为字符串时,才使用逗号进行分隔,以列表的形式输出这些字符串。
数组 a 是一个多维数组,JavaScript 会以迭代的方式调用 toString() 方法把所有数组都转换为字符串。
var a = [1,[2,3],[4,5]],[6,[7,[8,9],0]]]; var s = a.toString(); //把数组转换为字符串 console.log(S); //返回字符串“1,2,3,4,5,6,7,8,9,0” toLocalString()
toLocalString() 方法与 toString() 方法用法基本相同,主要区别在于 toLocalString() 方法能够让用户所在地区特定的分隔符把生成的字符串连接起来,形成一个字符串。 var a = [1,2,3,4,5]; var s = a.
《汇编语言》 王爽 基础知识机器语言汇编语言的产生汇编语言的组成存储器指令和数据储存单元CPU对存储器的读写总线地址总线控制总线数据总线 主板接口卡各类存储器芯片内存地址空间 寄存器通用寄存器字在寄存器中的存储8086CPU给出物理地址的方法CS和IP寄存器(内存访问)字单元DS和[address]栈段数据段代码段栈段 第一个程序程序执行过程的跟踪 本文只是自己看书的回忆,不如去看书,讲的更全面和透彻。 每种微处理器的汇编语言都不同,本书以8086CPU为中央处理器的PC机为例 基础知识 机器语言 电子计算机的机器指令是一列二进制数字,计算机将其转变为一列高低电平,使计算机的电子器件受到驱动,进行运算。每种微处理器由于硬件设计和内部结构不同,需要用不同的电平脉冲来控制,所以不同的微处理器其机器指令集不同。
汇编语言的产生 汇编指令是机器指令便于记忆的书写格式。每种CPU都有自己的汇编指令集。
例如:
操作:寄存器BX的内容送到AX
机器指令:1000100111011000
汇编指令:mov ax,bx
程序员用汇编语言写出源程序,汇编编译器将其编译为机器码,由计算机最终执行。
汇编语言的组成 汇编语言包括3种指令:
1.汇编指令:机器码的助记符,有对应的机器码;
2.伪指令:无对应机器码,由编译器执行(告诉编译器怎么翻译),计算机不执行;
3.其他符号:如+、-、*、/等,无对应机器码,由编译器识别;
存储器 CPU控制计算机的运作并进行运算,为其提供的指令和数据存放在存储器中。磁盘中的数据或程序必须读到内存中才可以被CPU使用。
指令和数据 指令和数据是应用上的概念。在内存或磁盘中无任何区别,都是二进制信息,区别只在于CPU在使用时赋予其意义。
例如:
1000100111011000 -> 89D8H(数据)
1000100111011000 -> mov ax,bx(指令)
储存单元 电子计算机最小信息单位是bit,是一个二进制位。8 bit = 1 Byte(字节)
微型机存储器的一个存储单元可以存储一个Byte。一个存储器被划分为若干存储单元,从0开始编号,一个存储单元的容量为一个字节。
1KB=1024B, 1MB=1024KB……
CPU对存储器的读写 存储单元的编号可看作存储单元在存储器中的地址。
CPU进行数据的读写,要和外部器件进行一下3类信息的交互:
1.存储单元的地址;(地址信息)
2.器件的选择,读或写的命令;(控制信息)
3.读或写的数据。(数据信息)
在实际信息交互层面,是通过处理、传输电信号实现的,电信号由总线(专门连接CPU和其他芯片的导线)传送。总线在逻辑上分为地址总线、控制总线和数据总线
CPU从内存中读取数据:
CPU通过地址线将地址信息3发出 → CPU通过控制线发出内存读命令,选中存储器芯片,通知它要从中读取数据 → 存储器将3号单元中的数据8通过数据线送入CPU
写操作类似。
总线 每一个CPU芯片有许多管脚,这些管脚与总线相连。
地址总线的宽度决定了CPU的寻址能力;
控制总线的宽度决定了CPU对系统中其他器件的控制能力;
数据总线的宽度决定了CPU与其他器件进行数据传送时的一次数据传送量。
地址总线 CPU有N根地址线,称该CPU的地址总线宽度为N,最多可以寻找 2 N 2^N 2N个内存单元(意味着寻址能力最小单位为B,而不是bit)。N根地址线,对应 2 N B 2^NB 2NB的寻址能力。
点击上方“图解面试算法”,选择“星标”公众号
重磅干货,第一时间送达
今天分享的题目来源于 LeetCode 上的剑指 Offer 系列 面试题09. 用两个栈实现队列。
题目链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
一、题目描述 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入: ["CQueue","appendTail","deleteHead","deleteHead"] [[],[3],[],[]] 输出:[null,null,3,-1] 示例 2:
输入: ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] [[],[],[5],[2],[],[]] 输出:[null,-1,null,null,5,2] 提示:
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
二、题目解析 实名吐槽这道题目的示例描述,我第一次真的没有看懂是啥意思。。。。
我用大白话来翻译一下 示例 2。
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] 这个表示每一次操作的集合数组 [[],[],[5],[2],[],[]] 这个表示每一次操作后对应的参数的集合数组 1、CQueue 首先初始化,没有参数,所以是 [],然后我们注意到 CQueue() 函数是没有返回值的,用 null 来表示(不要问我为什么用 null 表示。。。)
2、deleteHead 删除操作,没有参数,所以是 [],根据题意若队列中没有元素,deleteHead 操作返回 -1 ,所以输出值为 -1 。
3、appendTail 插入操作,有参数,此时是 5,并且 appendTail() 函数没有返回值的,用 null 来表示。
是因为临时文件夹太长,自己设个临时文件夹:
在发布的网站根目录中找到*.publishproj文件,在<PropertyGroup>节点内添加<IntermediateOutputPath>..\Temp</IntermediateOutputPath>,将可以设置发布过程中的临时文件存放目录,这里设置的..\Temp将会在项目目录中自动创建一个Temp文件夹,
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProductVersion>10.0.30319</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{931b5de2-a433-45c0-a8f1-b320be80eebe}</ProjectGuid> <SourceWebPhysicalPath>$(MSBuildThisFileDirectory)</SourceWebPhysicalPath> <SourceWebVirtualPath>/WebSite</SourceWebVirtualPath> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <SourceWebProject>http://localhost:4354</SourceWebProject> <SourceWebMetabasePath>/IISExpress/7.5/LM/W3SVC/9/ROOT</SourceWebMetabasePath> <!--指定发布过程中临时文件的存放目录--> <IntermediateOutputPath>..\Temp</IntermediateOutputPath> </PropertyGroup>
转载自品略图书馆 http://www.pinlue.com/article/2020/06/1500/0010739836400.html
Word表格中文字不居中,怎么办?
在工作中,相信大家都或多或少会用word来拟写通知文件,有时候文件中需要插入一些表格,表格中的文字明明在“开始”功能区的对齐方式里设置了“居中”,但是文字依然在表格中是偏上或者偏下的。有的小伙伴们就说了,是不是没有在“表格工具”或者“表格属性”中设置对齐方式为“居中”呢?的确有时候这种方式是可行的,但也有时候,即使这些地方都设置了“居中”,表格文字的位置还是偏上或者偏下的,这个时候,我们该怎么办呢?
处置方式一
选中整个表格,在“开始”功能区的对齐方式中选择“居中”对齐。
处置方式二
选中整个表格,单击“右键”,选择“表格属性”,在表格属性中“表格”功能区下方选择对齐方式中的“居中”。
处置方式三
处置方式三:选中整个表格,在上方“表格工具”功能区中选择“布局”,再选择对齐格式中的“居中”。
以上方法在一般情况下是可行的,但也存在特殊情况,比如都设置好了之后,呈现的结果如下所示:
这种结果还是不能满足我们的要求,这时候,我们就需要另想办法了。小编经过多番尝试后,发现有一种方式还是可行的,在此给大家作参考。
处置方式四
操作方式:步骤一:选中整个表格,在“开始”功能区中选择“段落”格式设置。
步骤二:在段落设置中,选择“缩进和间距”,在“行距”中选择“单倍行距”,最后点击“确定”。
在经过以上步骤之后,结果呈现符合使用需求,如下图所示:
以上方法适用于在基本的设置不能满足需求时进行使用。学会这种方式,再也不用担心word表格中文字不居中的问题了,小伙伴们,快学起来吧
报错如下:
UserWarning: Ignoring URL..................................................with link or location/anchor > 255 characters pandas读写excel是常规又常用的操作,但是实际的工作中,不知道你会不会遇到这个错误的提示,分析一下并作记录。
原因:
出现这个的原因是pandas在写入excel是会对链接形式的数据(http或者https类型)有一个检测和长度的限制,一般是255,百度时看到有说可以自己更改的,这里没有实践,不做解释,有兴趣的可以自己尝试一下。
解决办法:
writer = pd.ExcelWriter(r'图片匹配.xlsx', engine='xlsxwriter', options={'strings_to_urls': False}) df2.to_excel(writer) writer.save() 注意的是后面的参数options里面就是对我们这个报错的解决方案。这样我们在写入时就不会默认把字符串里含有http或者https的str认为是链接,也就不会出现开头所说的长度的限制。
值得一提的是最后一行代码一定要加上,否则依然会出现一个报错,会告诉你文件没有关闭。另外ExcelWriter模块还有很多更加方便用处,可以存入不同的sheet,好像还可以以追加形式写入excel文件,具体操作和注意事项,下次再做详细记录。
步骤说明
家庭版解决方案
运行的话就搜Hyper-V管理器
主机连不上的话,因为虚拟机没装 sudo apt install openssh-server
因为是裸装的系统,所以好多基本东西没装,有问题就装东西,比如没有make
sudo apt install gcc automake autoconf libtool make
parentNode.append()是还在试用期的方法,有兼容问题。
是在parendNode节点中最后一个子节点后插入新Node或者DOMString(字符串,插入后为Text节点)
与 parentNode.appendChild() 的 区别在于:
parentNode.append()可以同时传入多个节点或字符串,没有返回值;
而parentNode.appendChild()只能传一个节点,且不直接支持传字符串
(需要parentNode.appendChild(document.createTextElement('字符串'))代替),返回追加的Node节点。
https://blog.csdn.net/r4NqiAn/article/details/49929795?locationNum=16&fps=1#2-%E6%95%88%E6%9E%9C%E5%9B%BE
报错信息:
[3488:1356:0512/211222.342:ERROR:ssl_client_socket_impl.cc(1098)] handshake failed; returned -1, SSL error code 1, net_error -101
Chrome浏览器解决方案:
```handlebars from selenium import webdriver if __name__ == '__main__': options=webdriver.ChromeOptions() options.add_argument('--ignore-certificate-errors') driver=webdriver.Chrome(chrome_options=options) driver.get(u'https://python.org/') driver.close() 另外一个
chrome_options = Options() chrome_options.add_argument('--disable-extensions') chrome_options.add_experimental_option('excludeSwitches',['ignore-certificate-errors']) chrome_options.add_argument('--start-maximized') self.driver = webdriver.Chrome(chrome_options = chrome_options)
只需要把 cout<<‘解决了’中的单引号,改成双引号就可以了
文章目录 1. 背景2. 日志门面slf4j2.1 slf4j + log4j2.2 slf4j + log4j2 3. 避免冲突4. 总结 1. 背景 很多同学在进行Flink开发时,无论是使用log4j或log4j2,常常出现各种问题,如下图所示:
今天我们就要拨开云雾见天日,聊聊日志相关的知识,搞清楚这些报错的原因。
众所周知,现代框架都是用门面模式进行日志输出,例如使用Slf4j中的接口输出日志,具体实现类需要由log4j,log4j2,logback等日志框架进行实现,如Flink的类中是这样输出日志的:
// org.apache.flink.api.java.ClosureCleaner import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Internal public class ClosureCleaner { private static final Logger LOG = LoggerFactory.getLogger(ClosureCleaner.class); ... } 这种设计使得用户可以自由地进行日志框架的切换。
2. 日志门面slf4j slf4j全名Simple Logging Facade for Java,为java提供的简单日志Facade。Facade门面说白了就是接口。它允许用户以自己的喜好,在工程中通过slf4j接入不同的日志系统。slf4j入口就是众多接口的集合,它不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体有哪些接口,全部都定义在slf4j-api中。查看slf4j-api源码就可以发现,里面除了public final class LoggerFactory类之外,都是接口定义。因此slf4j-api本质就是一个接口定义。要想使用slf4j日志门面,需要引入以下依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> 这个包只有日志的接口,并没有实现,所以如果要使用就得再给它提供一个实现了些接口的日志框架包,比如:log4j,log4j2,logback等日志框架包,但是这些日志实现又不能通过接口直接调用,实现上他们根本就和slf4j-api不一致,因此slf4j和日志框架之间又增加了一层桥接器来转换各日志实现包的使用,比如slf4j-log4j12,log4j-slf4j-impl等。
2.1 slf4j + log4j Log4j + Slf4j的使用组合最为常见,依赖关系如下所示:
使用时要引入maven依赖:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.
文章目录 原理概述源码分析Job提交Stage划分Task提交Executor端运行Task Spark的调度框架分为资源调度和任务调度。Spark的资源调度是基于Yarn实现的,包含Driver和Executor资源的申请等,详细过程见博文 Spark源码分析之AM端运行流程(Driver) 和 Spark源码分析之CoarseGrainedExecutorBackend运行流程(Executor);本文主要讲述Spark任务调度框架的原理和源码分析。 原理概述 由于 Spark Scheduler内部原理剖析 中的Spark任务调度总览章节感觉已经讲解的很清楚了,因此原理部分我们直接摘抄引用如下。
当Driver起来后,Driver则会根据用户程序逻辑准备任务,并根据Executor资源情况逐步分发任务。在详细阐述任务调度前,首先说明下Spark里的几个概念。一个Spark应用程序包括Job、Stage以及Task三个概念:
Job是以Action方法为界,遇到一个Action方法则触发一个Job;Stage是Job的子集,以RDD宽依赖(即Shuffle)为界,遇到Shuffle做一次划分;Task是Stage的子集,以并行度(分区数)来衡量,分区数是多少,则有多少个task; Spark的任务调度总体来说分两路进行,一路是Stage级的调度,一路是Task级的调度,总体调度流程如下图所示。
Spark RDD通过其Transactions操作,形成了RDD血缘关系图,即DAG,最后通过Action的调用,触发Job并调度执行。DAGScheduler负责Stage级的调度,主要是将DAG切分成若干Stages,并将每个Stage打包成TaskSet交给TaskScheduler调度。TaskScheduler负责Task级的调度,将DAGScheduler给过来的TaskSet按照指定的调度策略分发到Executor上执行,调度过程中SchedulerBackend负责提供可用资源,其中SchedulerBackend有多种实现,分别对接不同的资源管理系统。有了上述感性的认识后,下面这张图描述了Spark-On-Yarn模式下在任务调度期间,ApplicationMaster、Driver以及Executor内部模块的交互过程。
Driver初始化SparkContext过程中,会分别初始化DAGScheduler、TaskScheduler、SchedulerBackend以及HeartbeatReceiver,并启动SchedulerBackend以及HeartbeatReceiver。SchedulerBackend通过ApplicationMaster申请资源,并不断从TaskScheduler中拿到合适的Task分发到Executor执行。HeartbeatReceiver负责接收Executor的心跳信息,监控Executor的存活状况,并通知到TaskScheduler。
源码分析 首先我们先看任务调度的整体流程图(Spark-On-Yarn模式),如下:
Job提交 下面我们逐步进行源码分析(源码版本为Spark2.4.3):
从上面我们知道Job的执行是由Action算子触发的。我们以rdd.count()为例看启动Job过程:
SparkContext#runJob()函数,重点分析SparkContext#runJob()函数,首先我们看其的四个参数分别为:
rdd: RDD[T]:提交Job的rdd。func: (TaskContext, Iterator[T]) => U:在rdd的每个分区上运行的函数(运行在Executor端)。partitions: Seq[Int]:rdd的分区数组(分区个数如何确定?见 Spark源码分析之分区(Partition))resultHandler: (Int, U) => Unit):在rdd的每个分区运行完成后在Driver端的回调函数(运行在Driver端)。 action算子往往是Job的最后一步,和transformation算子类似也会先在Executor的Task任务中执行分区函数func,但不同的是当分区函数func执行完成后会把执行结果返回给Driver端并在回调函数resultHandler进行最终的结果处理(此步是在DAGScheduler#handleTaskCompletion()函数中调用监听函数job.listener.taskSucceeded(rt.outputId, event.result) -> JobWaiter#taskSucceeded(),最终完成调用resultHandler回调函数)。 由此结合上面截图的源码可知count算子在Executor端的Task任务调用func函数(Utils.getIteratorSize)累加分区数据个数,然后在Task执行完成后把累加的分区数据个数值返回Driver端执行resultHandler函数((index, res) => results(index) = res)放置在数组results中,最后对results数组存储数值进行sum获得最终统计的元素个数值。
注:在计算大数据量的时,使用某些action算子(例如:collect)容易引起OOM,这是由于这些算子传入SparkContext#runJob()函数的第二个参数func的实现逻辑返回的数据量过大,最终返回Driver端的多个分区的数据全部加载在内存中导致其OOM。因此,在计算的数据量较大的时候应慎用这些大量(甚至全量)返回分区数据给Driver的action算子。
我们继续往下分析,开始进入DAGScheduler的调度…
从上面代码可以看出,DAGScheduler的任务提交流程就是封装构建JobWaiter监听对象,JobWaiter主要作用就是异步监听任务的完成情况(在任务完成后会回调该监听对象),如果成功则在JobWaiter#taskSucceeded()中处理resultHandler回调函数,失败则捕获抛出异常。然后就可以通过构建JobSubmitted消息异步提交给DAGScheduler#eventProcessLoop等待事件驱动模型的处理。
DAGScheduler的事件驱动模型也是一个典型的生产者-消费者模型,通过事件阻塞队列缓存住各种事件,然后通过事件分发器里的常驻线程不断的从事件队列里取事件并将该事件交给相应的事件处理handler进行处理。事件驱动模型是在DAGScheduler初始化时构建启动的,如下图:
根据上面事件处理函数可知DAGScheduler#submitJob()提交的JobSubmitted消息事件是由DAGScheduler#handleJobSubmitted()响应处理,如下图:
一个Job可能被划分为多个Stage,各个Stage之间存在着依赖关系,下游的Stage依赖于上游的Stage,Stage划分过程是从最后一个Stage开始往前执行的,最后一个Stage的类型是ResultStage,非最后一个Stage的类型都是ShuffleMapStage。
ResultStage可以使用指定的函数对RDD中的分区进行计算并得到最终结果。ResultStage是最后执行的Stage,此阶段主要进行作业的收尾工作(例如:对各个分区的数据收集、打印到控制台或写入HDFS)
Stage划分 下面我们首先分析finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)看如何划分Stage呢?
首先看上图右侧的DAGScheduler#getShuffleDependencies()函数是对于给定的RDD获得所有的直接父shuffle依赖,其是整个Stage划分的核心函数,在生成Stage父子依赖时候多次调用,源码分析见代码。 分析上面截图可以看出创建ResultStage过程是在DAGScheduler#createResultStage()函数中首先调用DAGScheduler#getOrCreateParentStages()函数中,其首先会调用DAGScheduler#getShuffleDependencies()获得finalRDD的所有直接父shuffle依赖,然后根据获得的依赖依次调用DAGScheduler#getOrCreateShuffleMapStage()函数生成并返回直接父Stage,在调用DAGScheduler#getOrCreateShuffleMapStage()函数也会同时生成祖先Stage,并把所有Stage添加到stageIdToStage缓存,这样我们就完成了DAG中Stage的stage的拆分。我们继续分析DAGScheduler#getOrCreateShuffleMapStage()如下图:
如上图,DAGScheduler#getOrCreateShuffleMapStage()函数首先会调用DAGScheduler#getMissingAncestorShuffleDependencies()函数获得所有祖先shuffle依赖,并依次调用DAGScheduler#createShuffleMapStage()创建所有的ShuffleMapStage,此过程会把所有创建的ShuffleMapStage放在stageIdToStage缓存中,最后把finalRDD的直接父ShuffleMapStage返回供上游DAGScheduler#createResultStage()函数创建ResultStage。最后我们看DAGScheduler#createShuffleMapStage()的代码实现,如下图:
至此,DAG中Stage的划分源码就分析完成。下面给出一个示例可以参照理解Stage划分代码:
val conf = new SparkConf().setMaster("local[*]").setAppName("test") val sc = new SparkContext(conf) val fileInt = sc.
vector的常用操作 定义和初始化 vector<T> v1;//空vector vector<T> v2(v1);//v2包含v1所有元素的副本 vector<T> v2 = v1;//同上 vector<T> v3(n,val);//n个val vector<T> v4(n);//n个重复地执行了值初始化的对象 vector<T> v5{a,b,c,...};//包含了初始值个数的元素,每个元素被赋予相应的初始值 vector<T> v6 = {a,b,c,...};//同上 vector<vector<int>> matrix(row,vector<int>(col,0));//定义并初始化二维向量 插入 vector<int> a{1,2,3}; a.push_back(4);//在末尾添加4 a.insert(a.begin()+1,5);//在a的第1个元素前插入数值5 a.insert(a.begin()+1,3,5);//在a的第1个元素前插入3个数,其值都为5 a.insert(a.begin()+1,b.begin(), b.end());//b为数组,在a的第1个元素前插入b的全部元素,可以用于合并两个向量 删除 b.pop_back();//删除末尾元素 b.erase(b.begin()); //将起始位置的元素删除 b.erase(b.begin(), b.begin()+3); //将(b.begin(), b.begin()+3)之间的元素删除 截取 vector<int> b(a.begin(), a.begin()+3);//将a中从a.begin()开始的3个元素赋值给b vector<int> b; b.assign(a.begin(),a.begin()+3);//同上 交换 a.swap(b);//b为向量,将a中的元素和b中的元素进行整体性交换 头文件algorithm中的几个重要函数 排序sort
sort(num.begin(),num.end(),sortFun);//如果shinint型,第三个参数可以使用less< int>()从小到大排序或者greater< int>()从大到小排序,是自定义类型话或者复杂类型就需自己定义比较规则函数sortFun。
vector<int> num; sort(num.begin(),num.end());//默认是升序 倒置reverse
头文件algorithm
reverse(a.begin(),a.end());//将a倒置 复制copy
copy(a.begin(),a.end(),b.begin()+1); //把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素 查找find
find(a.begin(),a.end(),10); //在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置 返回类型是迭代器,如果没有找到,则返回a.end()
返回元素 a.back();//返回a的最后一个元素 a.front();//返回a的第一个元素 a.at(ind);//返回下标为ind的元素 Set set跟vector差不多,它跟vector的唯一区别就是,set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序,而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行。要想用set先加个头文件set。
c#进行sqlite数据库连接时报异常:
无法加载 DLL“SQLite.Interop.dll”: 找不到指定的模块 一时未找到解决方案,后来经过多方尝试终于解决,在此分享下我的解决方案
关于SQLite.Interop.dll相信大家都已经有了,那么还报找不到指定模块的异常主要原因是在32位和64位系统下不兼容导致的,接下来只要做下兼容处理即可。步骤如下:
1、在项目目录下创建x86和x64文件夹 2、将SQLite.Interop.dll分别拷贝到x86和x64文件夹下 3、分别修改两个文件夹中SQLite.Interop.dll的属性 复制到输出目录:始终复制 再次运行就不会出现上述异常了,希望能帮助到遇到类似问题的小伙伴
目录 文件01. 文件的概念1.1 文件的概念和作用文件的作用 1.2 文件的存储方式文本文件和二进制文件 02. 文件的基本操作2.1 操作文件的套路2.2 操作文件的函数/方法2.3 read 方法 —— 读取文件2.4 打开文件的方式2.5 按行读取文件内容2.6 文件读写案例 —— 复制文件小文件复制大文件复制 03. 文件/目录的常用管理操作文件操作目录操作 04. 文本文件的编码格式(科普)4.1 ASCII 编码和 UNICODE 编码`ASCII` 编码`UTF-8` 编码格式 4.2 Ptyhon 2.x 中如何使用中文unicode 字符串 文件 01. 文件的概念 1.1 文件的概念和作用 计算机的 文件,就是存储在某种 长期储存设备 上的一段 数据长期存储设备包括:硬盘、U 盘、移动硬盘、光盘… 文件的作用 将数据长期保存下来,在需要的时候使用
1.2 文件的存储方式 在计算机中,文件是以 二进制 的方式保存在磁盘上的 文本文件和二进制文件 文本文件
可以使用 文本编辑软件 查看本质上还是二进制文件例如:python 的源程序 二进制文件
保存的内容 不是给人直接阅读的,而是 提供给其他软件使用的例如:图片文件、音频文件、视频文件等等二进制文件不能使用 文本编辑软件 查看 02. 文件的基本操作 2.1 操作文件的套路 在 计算机 中要操作文件的套路非常固定,一共包含三个步骤:
关于MFC模态对话框dlg.DoModal()返回-1的可能原因 CDialog::DoModal()函数的原型为:virtual INT_PTR DoModal(); DoModal的返回值:整数值,指定了传递给CDialog::EndDialog(该函数用于关闭对话框)的nResult参数值。如果函数不能创建对话框,则返回-1;如果出现其它错误,则返回IDABORT。 父窗口HWND无效 。存在无效的对话框资源ID。使用没有注册的的自定义控件,找到此控件删除能打开说明为此原因。对话框的某些控件创建失败:
RICHEDIT,要加上AfxInitRichEdit();添加AFX_MANAGE_STATE(AfxGetStaticModuleState());表示使用当前库内资源。
string的常用操作 定义和初始化 string s = "hello";//拷贝初始化 string s2("hello");//直接初始化 string s3(10,'c');//直接初始化,内容是cccccccccc 读写string对象 string s1, s2; cin >> s1;//将string对象读入s1,遇到空白停止 getline(cin,s2);//读取一行赋给s2,遇到换行符停止 可以使用while循环读取未知数量的string对象。
empty和size操作 empty函数根据string对象是否为空返回一个对应的布尔值。
size函数返回string对象的长度,即string对象中字符的个数。
比较string对象 比较运算符逐一比较string对象中的字符,并且对大小写敏感。有相等性运算符(==和!=)和关系运算符(<、<=、>、>=),如果对应位置上的字符相同,较短的小于较长的;如果在某些对应的位置上不一致,比较第一对相异字符比较的结果。
插入 string str1; str1.push_back('a');//在str1的末尾插入字符'a' str1.insert(str1.begin(),'a');//在指定位置前插入字符'a' insert用法总结
(1)在index位置插入count个字符:str.insert(0,2,‘6’);
(2)在index位置插入一个常量字符串:str.insert(0,“hello”);
(3)index位置插入常量字符串中的count个字符:str.insert(0,“hello”,3);//结果就是在str前插入了hel
(4)index位置插入常量string:str.insert(0,str);//在str前插入一个str
(5)index位置插入常量str的从index_str开始的count个字符:str.insert(0,str,1,3);
(6)使用迭代器:str.insert(str.begin(),‘a’);
(7)迭代器指向的pos位置插入count个字符ch:str.insert(str.begin(),2,‘a’);
拼接 string s1("abc"); s1.append("def"); s1 += "def"; 遍历 (1)下标法 for(int i = 0; i < s.size(); i++){ }
(2)正向迭代器 for(iter=s.begin();iter!=s.end();iter++){ }
(3)反向迭代器 for(riter=s.rbegin();riter!=s.rend();riter++){ }
删除erase (1)erase(pos,n); 删除从pos开始的n个字符
(2)erase(pos); 删除pos及以后的所有字符
(3)erase(position); 删除position处的一个字符(position是个string类型的迭代器)
(4)erase(first,last); 删除从first到last之间的字符(first和last都是迭代器)
记录一次laravel5.8开发的API接口,引入JWT的流程。
laravel+JWT的整合教程在网上有很多,根据网上的教程整合期间,还是踩了很多坑。
虽然这些坑都能在网上搜索到解决办法,但网上的其他教程都没有注明需要注意的点在哪里。
开始之前,先放两个链接。
jwt-auth for laravel的安装与使用.
JWT 完整使用详解【这篇很详细,讲得也很到位,基本看这篇就够了】.
我也是根据这几篇文章进行的整合。
laravel中使用的是jwt-auth库,那么先安装:
step1:
# 建议使用1.0以上版本 composer require tymon/jwt-auth step2:发布配置文件
# 这条命令会在 config 下增加一个 jwt.php 的配置文件 php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" step3: 生成加密密钥
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar php artisan jwt:secret step4:更新校验用户登录的模型
以User为例:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; use Tymon\JWTAuth\Contracts\JWTSubject; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable implements JWTSubject { use Notifiable; /** * 可以被批量赋值的属性. * @var array */ protected $fillable = []; protected $hidden = []; /** * 关联到模型的数据表 * * @var string */ protected $table = ''; /** * 表明模型是否应该被打上时间戳 * * @var bool */ public $timestamps = false; /** * Get the identifier that will be stored in the subject claim of the JWT.
UnicodeEncodeError: 'latin-1' codec can't encode characters or 'ascii' codec can't decode byte.
1. UnicodeEncodeError: 'latin-1' codec can't encode characters...
Solution:
data = json.loads(data.encode('utf-8').decode('latin-1')) 2. UnicodeDecodeError: 'ascii' codec can't decode byte...
Solution:
import sys reload(sys) sys.setdefaultencoding('utf-8')
GPIO简介
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片
的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。
STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103VET6 型号的
芯片有 GPIOA、GPIOB、GPIOC 至 GPIOE 共 5 组 GPIO,芯片一共 100 个引脚,其中
GPIO 就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。
最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO
引脚接入到 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可
以通过继电器或三极管控制外部大功率电路的通断。
基本结构分析
下面我们按图中的编号对 GPIO 端口的结构部件进行说明。
1. 保护二极管及上、下拉电阻
引脚的两个保护二级管可以防止引脚外部过高或过低的电压输入,当引脚电压高于
VDD 时,上方的二极管导通,当引脚电压低于 VSS 时,下方的二极管导通,防止不正常电
压引入芯片导致芯片烧毁。尽管有这样的保护,并不意味着 STM32 的引脚能直接外接大功
率驱动器件,如直接驱动电机,强制驱动要么电机不转,要么导致芯片烧坏,必须要加大
功率及隔离电路驱动。
2. P-MOS 管和 N-MOS 管
GPIO 引脚线路经过两个保护二极管后,向上流向“输入模式”结构,向下流向“输出
模式”结构。先看输出模式部分,线路经过一个由 P-MOS 和 N-MOS 管组成的单元电路。
在UITableview中放置的UICollectionView,然后设置滚动没有效果scrollToItemAtIndexPath
- (void)layoutSubviews { [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.selectedIdx inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES]; } - (void)setSelectedIdx:(NSInteger)selectedIdx { _selectedIdx = selectedIdx; // 显示到当前的位置 }
在开发的时候有时候会遇到 css、js代码文件已经推送,但是服务器却没有刷新样式,我们就可以在引入文件的时候加上?t=20200610 之类的参数即可
例如:
修改前:
<script src="/js/navBar.js"></script> 修改后:
<script src="/js/navBar.js?t=20200609"></script>
最近在研究lodop这个web打印控件,在使用的过程中因为客户需要两种纸张规格进行打印,为了简化打印过程,通过lodpo的LODOP.SET_PRINT_PAGESIZE(1, 2410, 1400, "CreateCustomPage"); LODOP.ADD_PRINT_HTM(10, 0, "100%", "100%", strFormHtml);
来设置一个自定义的纸张规格,这里面遇到一个问题,一开始我是这样设置的
LODOP.SET_PRINT_PAGESIZE(1, 2800, 1400, "CreateCustomPage"); LODOP.ADD_PRINT_HTM(10, 0, "100%", "100%", strFormHtml);
然后发现在打印服务器中有这一个纸张规格,但是打印就是不能选择这个纸张规格。一直不知道是怎么回事,后来才发现是纸张规格的宽2800超过了打印机的最大纸张规格,所以才不能选择使用。
重点:设置自定义纸张规格,宽高一定不能超过打印机支持的最大值
今天遇到一个问题,需要计算一下CAN总线上,不同的波特率下,每秒钟最多能够传输多少帧数据,或者说,每帧数据需要耗时多少时间。一开始以为这个东西是很基础的确定性数据,百度上应该随便就能查得到,可是查了半天却毫无头绪,不知道是不是我的打开方式不对(^ O ^)。一怒之下,干脆自己来。
首先我们要确认几个输入参数,包括CAN总线的波特率,要传输的帧类型和帧格式,然后根据CAN协议规范,计算出该帧的bit数。我们用数据帧来做示例,详见图1。
最后综合起来,便得到我们想要的结果,其实很简单,如图2所示:
当然,如果你要计算远程帧的,那就用下面这张图就好:
好啦,就这么简单!
为了方便起见,我将上述的过程整理成了一个excel表格,只需要手工修改几个参数便可完成所有过程的自动计算,如下图:
实际的表格,我上传到CSDN了【CAN总线负载率计算器】,有需要的可以自行下载。当然,考虑到下载需要积分(这里要澄清一下,本来我上传时是选择不需要积分下载的,但是CSDN会自动调整,这不是我能左右的),如果积分不多,也可以直接留言邮箱给我,我单独发给你。
OK,打完收工!
本文参考的博客链接
PAT排序例题A1025
经典排序算法
算法总目录
一、简介 1、头文件:#include
2、时间复杂度:sort在实现中规避了经典快速排序中可能出现的会导致实际复杂度退化到O(n^2)的极端情况。类似于快排,为nlog(2)n,效率较高。
3、参数
sort(首元素地址(必填), 尾元素地址的下一个地址(必填), 比较函数(非必填)); 二、如何使用sort排序 1、sort函数实现数的排序
#include<cstdio> #include<iostream> #include<cstdlib> #include<algorithm> using namespace std; bool cmp(int a,int b)///实现从大到小排序的比较函数 { return a>b; } int main() { int i,j; int arr[10]={1,33,56,74,34,34,555,43,43,343}; sort(arr,arr+10,cmp); for(i=0;i<10;i++) cout<<arr[i]<<" "<<endl; return 0; } 2、sort函数实现字符串的排序(根据长度,字母顺序两种情况)
(1)sort函数对string内的字符进行排序(string为字符串类型数据)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; bool cmp(char a,char b)///比较函数 { return a>b; } int main() { string s; cin>>s; sort(s.begin(),s.end());///从小到大 cout<<s<<endl; sort(s.begin(),s.end(),cmp);///从大到小 cout<<s<<endl; cout<<s.
前言
Ubuntu16.04下安装git并进行配置,利用Gitee(码云)进行项目及代码的管理
git安装与卸载 安装:apt-get install git
卸载:apt-get remove git
安装成功后可用以下命令查看git版本号:
git --version
git配置 1、配置用户名
git config --global user.name “your name”
用户名和gitee用户名一致
2、配置git邮箱
git config --global user.email “your email”
邮箱与gitee注册邮箱一致
3、查看配置信息
git config --global --list
用户名与邮箱要与gitee相关信息一致,否则会出现gitee相关统计信息无法统计的情况,如代码提交等等
4、创建公钥
ssh-keygen -C “your email” -t rsa
创建过程中,根据提示信息一直回车即可
5、gitee添加公钥
将目录下的.ssh/id_rsa.pub里的内容全部复制。登录gitee网站在设置中选择SSH公钥添加公钥内容
6、测试配置是否成功
ssh -T git@gitee.com
7、测试成功后,可以利用git下载相关代码
1.配置阿里巴巴的私服镜像:在mirros元素中增加一个mirros元素。指向阿里云的下载地址。 <mirrors> <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors> 这一步可以尝试把http改为https,哪一个好用用哪一(本人是使用https好用)。
2.删除lastupdated文件 参考方法:http://www.caiqz.cn/595.html 如果下载失败一两次,本次仓库中出现了lastupdated文件,可以自建一个TXT文本,放入下面代码,保存为bat格式,去删除里面的lastupdated文件,多次尝试下载,因为网络问题,一次两次失败是很正常的,多尝试几次,如果还不能成功,就应该考虑是别的方面的问题。 一定要删除之后多尝试几次 rem 这里写你的仓库路径 set REPOSITORY_PATH=E:\java\maven\repository rem 正在搜索... for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do ( del /s /q %%i ) rem 搜索完毕 pause
文章目录 一、诡异的NPE二、原因:三目运算符表达式类型对齐拆箱导致NPE三、继续深究四、总结 情况这段代码: 一、诡异的NPE 报错信息如下:
2020-06-03 13:02:22.193 [] [DubboServerHandler-120889-thread-8] ERROR com.alibaba.dubbo.rpc.filter.ExceptionFilter 90 - [DUBBO] Got unchecked and undeclared exception which called by . service: com.jzd.health.archive.api.si.DiseaseHistoryServiceInterface, method: getRemovedDuplicateFamilyDiseaseNames, exception: java.lang.NullPointerException: null, dubbo version: 2.8.4.1, current host: java.lang.NullPointerException: null at com.jzd.health.archive.biz.service.transform.DiseaseHistoryTransform.transform(DiseaseHistoryTransform.java:60) at com.jzd.health.archive.biz.service.transform.DiseaseHistoryTransform.transform(DiseaseHistoryTransform.java:21) at com.jzd.frw.common.transformer.AbstractContextualObjectTransformer.transform(AbstractContextualObjectTransformer.java:26) at com.jzd.frw.common.tr 定位到这里,也就是set这样代码报的NPE。这里的chronicDiseaseFlag是Boolean。
if (CollectionsUtils.isNotEmpty(diseaseInfoDTOS)) { dto.setChronicDiseaseFlag(diseaseInfoDTOS.get(0) == null ? false : diseaseInfoDTOS.get(0).getChronicDiseaseFlag()); } 二、原因:三目运算符表达式类型对齐拆箱导致NPE 这里怎么会有NPE?卡克10分钟…吃完饭才想通很久很久以前,一位同事来问过我类似的问题,也是三目运算符中出现的NPE。直觉告诉我,一定是代码问题,而不是什么“诡异”的问题。
结论:
三目运算符左右表达式在类型对齐时,产生了自动拆箱的操作,导致NPE。也就是说:
diseaseInfoDTOS.get(0)当然不为NULL,但是diseaseInfoDTOS.get(0).getChronicDiseaseFlag()为NULL,因为chronicDiseaseFlag本来就是包装类Boolean,所以赋值为NULL没任何问题。But!在三目运算符中,它自己做了“类型对齐”,这里因为表达式1是false,所以会尝试跟它对齐,把NULL转成基本类型的boolean值,自然就报错喽。
更加详细全面的解释如下文:
答案在这里:
https://mp.weixin.qq.com/s/iQ6qdNv7WTLa3drxNa9png
摘抄下最核心的2句话。
三目运算符condition ?表达式1:表达式2中,高度注意表达式1和2再类型对齐时,可能跑出因自动拆箱导致的NPE异常。 说明:以下两种场景会出发类型对齐的拆箱操作。 1)表达式1或表达式2的值只要有一个是原始类型;(俺的示例就符合这一条) 2)表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。(俺的示例也符合这一条) 2)能说明三目再背后会把boolean向上转型成Boolean。是的boolean-> Boolean没事;但是null -> Boolean就有事了。
文章目录 1.inner join2.left join3.right join4.full outer join5.自连接6.外连接与条件配合使用7.多表之间进行join时注意事项8.left semi join9.hive中各种join之间的关系一览图。 hive编程是整个数据仓库操作的核心,而各种业务之间的join是hive的核心,所以熟练明白滴掌握hive中的各种join是数据仓库开发工程师必备的技能。 hive中的join只支持等值join,也就是说join on中的on里面表之间连接条件只能是=,不能是<,>等符号。此外,on中的等值连接之间只能是and,不能是or. (如果在on 里添加非表之间的条件可以是非等号,下面有演示) 为了演示各种join之间的关系以及使用注意事项,准备如下三个表,以及表中数据。 表1:
CREATE TABLE `FDM_SOR.mytest_department`( `dept_no` int, `dept_name` string) row format delimited fields terminated by ',' 表2:
CREATE TABLE `FDM_SOR.mytest_staffinfo`( `id` int, `name` string, sex string, dept_no int )row format delimited fields terminated by ',' 表3:
CREATE TABLE `FDM_SOR.mytest_deptaddr`( `dept_no` int, `addr` string, `tel` string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' 如下对应的各表中load的数据
`FDM_SOR.mytest_staffinfo 001,'tom','man',101 002,'jerry','man',103 003,'marry','woman',101 004,'tom','man',104 005,'jebby','man',102 006,'smiiss','man',101 007,'dosos','man',102 008,'tiny','woman',102 009,'feyy','woman',104 010,'feyy','woman',103 011,'cake','man',101 012,'dogy','man',102 013,'gaayy','man',105 014,'kety','man', 015,'caggyy','man',104 016,'addy','man',106 017,'nancy','man',104 018,'tom','man',101 FDM_SOR.
Python中的sys.path.append()
当我们导入一个模块时:import xxx,默认情况下python解析器会搜索当前目录、已安装的内置模块和第三方模块
当运行脚本文件和导入模块不再同一目录下
import sys sys.path.append(' .....路径。。。 ') 就能继续import进去该模块了
永久添加路径到sys.path中,方式有三,如下:
1)将写好的py文件放到 已经添加到系统环境变量的 目录下 ;
2) 在 /usr/lib/python2.6/site-packages 下面新建一个.pth 文件(以pth作为后缀名)
将模块的路径写进去,一行一个路径,如: vim pythonmodule.pth
/home/liu/shell/config
/home/liu/shell/base
3) 使用PYTHONPATH环境变量
export PYTHONPATH=$PYTHONPATH:/home/liu/shell/config
菜鸟的springboot项目图片上传及图片路径分析 说明一、图片路径分析二、实现图片上传(1)单文件上传(非异步)(2)单文件上传(异步) 三、总结四、更新配置文件 说明 更新时间:2020/6/15 16:15,更新了部署环境的判断
本文记录一下springboot项目的图片上传的相关知识,主要解决项目打成jar包部署时的图片路径问题,本文会持续更新,不断地扩充
本文仅为记录学习轨迹,如有侵权,联系删除
一、图片路径分析 springboot项目在还没打包时,很多人喜欢把图片上传后,保存在项目的静态资源下,就像下面的图片那样
这样好像看来没问题,在还没打成jar包时,在idea启动运行正常,图片也确实存储到了静态资源下的images文件夹中,但是一旦打包成jar包后,运行jar包时,发现图片存储路径出错了,图片并不会存储到静态资源下的images文件夹中,而是到服务器的用户下的路径那里去,如果把打包后的jar包在自己电脑运行,按照上面的代码,图片就会存储到C盘下对应的电脑用户那里
这是因为打包后会生成一个jar包,这个jar可以解压,发现里面的classes就打包有静态资源,而且上面的代码String path = System.getProperty("user.dir");在idea启动运行时会获取到项目的根路径,所以在idea启动运行时可以将图片保存在项目下的images文件夹里面,而打包成一个jar后,用java -jar jar包名称启动时,获取的确是C盘下的用户路径,而不会获取到jar所在的目录,跟不会获取到jar里面的classes文件的路径
这种获取图片路径并存储的方式肯定不是我想要的,我想要的效果是,即使打成jar包后,可以在jar包所在目录下自动生成一个文件夹,上传的图片就保存在这个文件夹中,这样就不论你jar包部署在哪里,都可以获取到jar所在根目录,并自动生成一个文件夹保存上传的图片
这样的话部署就方便多了
二、实现图片上传 (1)单文件上传(非异步) 我们知道项目打包后的jar包都在target文件夹里面,也就是说target所在文件夹才是jar包所在的路径,所以,图片存储的位置就应该在target里面,这样在打成jar包后,就可以获取jar包所在目录,实现上面分析的功能
前端代码
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>单文件上传</title> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <script th:src="@{/js/jquery.min.js}"></script> </head> <body> <div th:if="${uploadStatus}" style="color: red" th:text="${uploadStatus}">上传成功</div> <form th:action="@{/singleUploadFile}" method="post" enctype="multipart/form-data"> <div class="form-group"> <label>单文件上传</label> <input class="form-control-file" type="file" name="file" required="required" /> </div> <input id="submit" type="submit" value="上传" /> </form> </body> </html> 后端对应控制器的代码
SELECT * FROM ‘表名’ WHERE LOCATE(‘包含的字符串’,‘字段’) > 0
在实现接口自动测试的时候,会经常遇到接口参数依赖的问题,例如调取登录接口的时候,需要先获取登录的key值,而每次请求返回的key值又是不一样的,那么这种情况下,要实现接口的自动化,就要用到postman中设置环境变量这个功能了;
在postman中,可以利用tests将接口返回的response设置为环境变量,供后续接口使用(类似参数化的概念)
获取环境变量需要具体方法:
var jsonData =JSON.parse(responseBody);//获取body中返回的所有参数
postman.setEnvironmentVariable(“appKey”,jsonData.data.appKey);//把返回参数中的keys设置为环境变量
获取headers值更新环境变量的方法:
Var data=postman.getResponse.Header(“Access-token”) //获取返回header中token的值
postman.setEnvironmentVariable(“AccessToken”,data) //将token值设置为环境变量
1.设置环境变量
postman.setEnvirionmentVariable(“key”,“value”) //postman 5.0一下置环境变量的方法
//postman 5.0以上版本设置环境变量的方法
pm.environment.get(“key”,“value”);
2.设置全局变量
postman.setGlobalVariable(“key”, “value”);//postman 5.0以下版本设置全局变量方法
//postman 5.0以上版本设置全局变量方法
pm.globals.set(“variable_key”, “variable_value”);
3.检查response body中是否包含某个string
tests[“Body matches string”] = responseBody.has(“string_you_want_to_search”);
//5.0以上版本方法
pm.test(“Body is correct”, function () {
pm.response.to.have.body(“response_body_string”);});
4.检测JSON中的某个值是否等于预期的值
var data = JSON.parse(responseBody);
tests[“Your test name”] = data.value === 100;
JSON.parse()方法,把json字符串转化为对象。parse()会进行json格式的检查是一个安全的函数。
检查json中某个数组元素的个数(这里检测programs的长度)
var data = JSON.parse(responseBody);
tests[“program’s lenght”] = data.programs.length === 5;
5.转换XML body为JSON对象
一、问题描述 提高道路通行能力是现代城市交通面临的重要课题。
道路通行能力衡量参数:
交通流:在单方向道路上行驶的一辆辆汽车组成的连续流体。用流量、速度、密度3个参数描述其基本特征。
流量q:某时刻单位时间内通过道路某断面的车辆数(单位辆/h);
速度v:某时刻通过道路某断面的车辆速度(km/h);
密度k:某时刻通过道路某断面单位长度内的车辆数(辆/km)。
3个参数之间的数学关系:
(1)
要提高道路通行能力,即增大q值,有两个途径:提高速度v,或者增加密度k。但是,现实存在这样的矛盾(问题点):
车辆速度越快,刹车距离变大,车间距需要增大,车辆密度由此下降。车辆密度越大,刹车距离变小,车辆速度需要降低。 所以,需要综合这些因素进行综合分析。
刹车距离:从司机决定刹车到车完全停止行驶的距离。
道路通行能力:在安全条件下,当具有标准长度和技术指标的车辆,以前后两车最小车头间隔连续行驶时,单位时间内通过道路某断面的最大车辆数(辆/h)。
二、问题分析 要解决的问题是找到最佳的车速,以达到最大的道路通行能力。问题求解的步骤
①求解刹车距离与车速之间的关系;
②求解道路通行能力与刹车距离之间的关系;
③求解道路通行能力与车速之间的关系。
三、符号说明 :车辆密度为0时的车速,即理论上的最高车速,称畅行车速;
:速度为0时的密度,称阻塞密度;
:最大车流量;
:最大车流密度;
:最大车流速度。
:刹车距离(m);
:反应距离(m);
:制动距离(m);
F:刹车制动力(N);
:反应时间(s);
N:道路通行能力(辆/h)。
D:最小车头间隔(m)。
四、模型假设 假设1:经验表明,道路上车辆密度增加时,司机就会降低车速。速度和密度之间存在如下关系
假设2:刹车距离d为反应距离d1 与制动距离d2 之和。
假设3:反应距离d1 与车速v成正比,比例系数为反应时间。
五、模型建立 5.1车流量与速度的模型 将假设1代入到数学模型(1)中,得到流量与密度的关
同时,也可以得到流量与速度的关系
5.2刹车距离与速度的模型 制动力F做功为 ;车速从v到0,动能变化为 ,于是得到
其中, ,a为减速度,反应了道路的减速状况,雨天路滑,道路减速效果差, 值大。
根据刹车距离d为反应距离 与制动距离 之和,得到
5.3道路通行能力与速度的模型 道路通行能力
最小车头间隔D主要由刹车距离d决定:
:车身标准长度与两车间安全距离之和,取固定值。
于是,得到
可以看出,道路通行能力与汽车速度、司机反应时间、道路情况、车辆大小等因素大小有关。
六、模型求解
6.1求解系数c1、c2 利用机器学习中的一元线性回归方法,根据测试数据对模型作拟合;
或者,调查交通工程学的相关资料,得到司机反应时间c1 为0.7~1s,系数c2 约为 ( )
6.2 道路通行能力最大时对应的车速 根据正数的不等式性质,有
// 1构建为base64图片字符串 let base64 = ... // 从服务器获取的base64字符串 iVBORw0KGgoAAAANSUhEUg... let imageStr = 'data:image/png;base64,' + base64 // 保存到本地 let bitmap = new plus.nativeObj.Bitmap("test"); bitmap.loadBase64Data(imageStr, function(){ console.log("加载Base64图片数据成功"); bitmap.save("_doc/test.png",{},function(i){ console.log('保存图片成功:'+JSON.stringify(i)); bitmap.clear(); },function(e){ onsole.log('保存图片失败:'+JSON.stringify(e)); bitmap.clear(); }); }, function(){ console.log('加载Base64图片数据失败:'+JSON.stringify(e)); bitmap.clear(); });
开发中我们常需要将电脑作为临时服务器,如何允许其他设备访问自己的电脑是开发前需要准备好的。本篇介绍的是如何开放自己电脑的内网ip和端口,让同一wifi网络下的其他设备能访问到我们的电脑。
电脑端将wifi添加至专用网络
找到wifi设置窗口(设置 -> 网络和Internet -> WLAN -> 点击连接的wifi) 或(电脑右下角 -> wifi属性) 设置为专用网络
开放端口
找到 “高级安全 Windows Defender 防火墙” 窗口(设置 -> 更新和安全 -> Windows安全中心 -> 防火墙和网络保护 -> 高级设置)
开放端口:新建入站规则(端口、下一步、填写要开放的端口、下一步… )
服务器host设置0.0.0.0,端口设置为开放的端口(以python flask为例)
cmd -> ipconfig 查看WLAN的电脑的内网ip地址
最后就可以在手机浏览器使用该内网ip加端口号即可进行电脑访问测试啦!!
2020 年 5 月 15 日,Dubbo 发布 2.7.7 release 版本。其中有这么一个 Features
新增一个负载均衡策略。
先看一下提交记录:
https://github.com/chickenlj/incubator-dubbo/commit/6d2ba7ec7b5a1cb7971143d4262d0a1bfc826d45
负载均衡是基于 SPI 实现的,我们看到对应的文件中多了一个名为 shortestresponse 的 key。
这个,就是新增的负载均衡策略了。看名字,你也知道了这个策略的名称就叫:最短响应。
所以截止 2.7.7 版本,官方提供了五种负载均衡算法了,他们分别是:
ConsistentHashLoadBalance 一致性哈希负载均衡LeastActiveLoadBalance 最小活跃数负载均衡RandomLoadBalance 加权随机负载均衡RoundRobinLoadBalance 加权轮询负载均衡ShortestResponseLoadBalance 最短响应时间负载均衡 最短响应时间负载均衡
首先,我们看一下这个类上的注解,先有个整体的认知。
org.apache.dubbo.rpc.cluster.loadbalance.ShortestResponseLoadBalance
我来翻译一下是什么意思:
从多个服务提供者中选择出调用成功的且响应时间最短的服务提供者,由于满足这样条件的服务提供者有可能有多个。所以当选择出多个服务提供者后要根据他们的权重做分析。但是如果只选择出来了一个,直接用选出来这个。如果真的有多个,看它们的权重是否一样,如果不一样,则走加权随机算法的逻辑。如果它们的权重是一样的,则随机调用一个。 再配个图,就好理解了,可以先不管图片中的标号:
有了上面的整体概念的铺垫了,接下来分析源码的时候就简单了。
源码一共就 66 行,我把它分为 5 个片段去一一分析。
这里一到五的标号,对应上面流程图中的标号。我们一个个的说。
标号为①的部分 这一部分是定义并初始化一些参数,为接下来的代码服务的,翻译一下每个参数对应的注释:
length 参数:服务提供者的数量。
shortestResponse 参数:所有服务提供者的估计最短响应时间。(这个地方我觉得注释描述的不太准确,看后面的代码可以知道这只是一个零时变量,在循环中存储当前最短响应时间是多少。)
shortCount 参数:具有相同最短响应时间的服务提供者个数,初始化为 0。
shortestIndexes 参数:数组里面放的是具有相同最短响应时间的服务提供者的下标。
weights 参数:每一个服务提供者的权重。
totalWeight 参数:多个具有相同最短响应时间的服务提供者对应的预热(预热这个点还是挺重要的,在下面讲最小活跃数负载均衡的时候有详细说明)权重之和。
firstWeight 参数:第一个具有最短响应时间的服务提供者的权重。
sameWeight 参数:多个满足条件的提供者的权重是否一致。
标号为②的部分 这一部分代码的关键,就在上面框起来的部分。而框起来的部分,最关键的地方,就在于第一行。
获取调用成功的平均时间。
成功调用的平均时间怎么算的?
调用成功的请求数总数对应的总耗时 / 调用成功的请求数总数 = 成功调用的平均时间。
IDEA当在一个分支上修改了内容没有提交,然后切换到其他分支时,可能会发生冲突。
这时IDEA会弹出提示,问你要选择Smart Checkout还是Force Checkout:
如果想保留你在原分支上的修改内容,那么选择Smart Checkout,
Force Checkout不会保留你的修改,切到另一个分支内容就消失了,且切回来原来分支也找不回,白写了。
原理:
选择Smart Checkout,IDEA会先执行stash命令,贮存这些未提交的修改,然后checkout 到分支B,在切换到分支B后,unstash 这些修改,所以A分支本地的这些修改会带到B分支上。
使用循环完成剪刀石头布游戏,提示用户输入要出的拳 :石头(1)/剪刀(2)/布(3)/退出(4) 电脑随机出拳比较胜负,显示用户胜、负还是平局。运行如下图所示: 提示:电脑随机出拳 使用随机数,首先需要导入随机数的模块 —— “工具包” import random 导入模块后,可以直接在 模块名称 后面敲一个"."然后按 Tab键,会提示该模块中包含的所有函数 random.randint(a, b),返回[a, b]之间的整数,包含a和b ''' print('第一题*********************************************************************************************') import random # 导入随机数的模块 while True: player = int(input('请你输入你要的选项 石头(1)/剪刀(2)/布(3)/退出(4):')) # 输入选择的选项 if player == 4: # 如果玩家输入4 ,满足条件,执行这个 print('游戏退出') break # 退出当前循环 computer = random.randint(1,3) # 电脑随机在1-3之间选择 if ((player == 3 and computer == 1) # 如果玩家出布(3)和电脑出石头(1) 玩家胜利 or (player == 1 and computer == 2) # 如果玩家出石头(1)和电脑出剪刀(2) 玩家胜利 or (player == 2 and computer == 3)): # 如果玩家出剪刀(2)和电脑出布(3) 玩家胜利 print('玩家选择的出拳是{},电脑出拳的{},玩家胜利!'.
关于某查猫查询参数的加密逻辑分析 先上链接:
aHR0cHM6Ly93d3cucWljaGFtYW8uY29tLw==
抓包分析 找到要分析的参数,通过首页的检索栏,输入企业名称关键字点击查询就可以抓到类似下面的两个包。
上图标记出来的mfccode就是需要分析的加密参数
同样的我留意到在上图选中的上一个请求,看着像加密的请求,接下来通过断点来分析是否为加密位置
加密定位 在这个请求上打上XHR 断点
重新发起请求之后就能看到成功断上
通过分析堆栈找到下图这个位置找到了加密的位置
通过断点可以找到加密生成的地方
或者在堆栈的位置找到下图这个位置也可以快速定位到加密的位置
加密分析 找到加密的位置后接下来就要分析加密的逻辑了,打上断点可以看到这里进入了一个VM中
我们复制到美化网站中格式化后分析
在编辑器中可以大致看到这段代码对cookie中的qznewsite.uid字段进行了操作
最后将 dc方法的结果返回给了window.__qzmcf,这个和我在网页上断点的结果相对应
只要能跑通这段 js 就可以完成这段加密了。
经过修改可以正常运行了,主要解决的就是关于 node 中调用 window、document 的问题
但是将结果带入到 Python 代码里并没有如预期一样返回搜索结果,而是返回了登陆的界面,这个结果让我十分费解。
刚刚开始一直以为是我爬虫代码的问题,之后突然想到,这个 js 代码是由对方返回给客户端的,所以这个 js 代码应该动态的。
经过对比,发现mov以及sk的长数组都是动态的,或许还有其他的代码是动态的不过这间接的验证了我的猜测。
所以 js 加密的代码写死调用是没有办法完成破解的,想通这一点,我只要将返回回来的 js 代码动态的调用就可以了。
第一步、将 cookie 传入,替换为第一次访问首页返回的 cookie 即可,不过之后测试这一步貌似没有验证,直接写死也是可以的。
第二步、将服务端返回的动态调用,只要在静态的代码基础上小修一下就行了,例如声明window,document这些操作,之前的文章均有提及
第三步、调用window.__qzmcf这个方法,完成加密参数的生成
完成上面的步骤之后重新调用就可以正确拿到网页的结果了。
总结 有两周没有练习 js 逆向了所以分析的逻辑有点乱,不过大致的分析流程都是类似的,这里一定要说的就是学习 js 真的不吃亏,很多地方都有用到。
这个网站就这个加密参数有点意思,改写 js 的思路值得学习。
今天就到这里,下次再会~
抽奖赠书 今日赠书:《Python 入门到人工智能实战》 《Web 前端性能优化》
赞助商:北京大学出版社
书籍介绍:
《Python 入门到人工智能实战》是针对零基础编程学习者编写的教程。从初学者角度出发,每章以问题为导向,辅以大量的实例,详细地介绍了Python 基础、机器学习,以及最好也最易学习的两个平台PyTorch 和Keras。
环境 MacBook Pro
Java:1.8
前言 想弄懂抽象语法树,还得明白相关方法;
想要利用自定义注解写方法,就是在重载方法visitClassDef中,
利用jcClassDecl.defs.prepend(),把新方法加入到源代码中。
如何写新方法呢?
① 生成表达式 — 其实就是方法体的内容
例如:
public void getPerson(String name) { this.name = name; } 例子中,this.name = name;就是表达式;
② 生成方法体;
就是在生成表达式的基础上,把{ }补上;
{ this.name = name; } ③ 生成入参
上例中就是String name
④ 生成返回值
上例中就是void;
⑤ 生成最终方法
利用方法treeMaker.MethodDef就可以生成了;
treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), //访问标志 names.fromString("<init>"), //名字 treeMaker.TypeIdent(TypeTag.VOID), //返回类型 List.nil(), //泛型形参列表 List.nil(), //参数列表 List.nil(), //异常列表 jcBlock, //方法体 null //默认方法(可能是interface中的那个default) ); 其实通过MethodDef方法的参数,就知道我们需要干什么了;
TreeMaker treeMaker.VarDef() 用于创建字段/变量定义语法树节点(JCVariableDecl);
源码:
public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init) { JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, (VarSymbol)null); tree.
问题:tomcat部署javaweb项目(war包)后,启动正常,但是项目页面不能访问。 环境:Linux下tomcat9.0.27。 排查: 1.首先排除启动异常,无论是tomcat日志还是应用自身的日志,都没有异常记录;
2.然后排除端口和路径异常,tomcat首页(猫图)可以正常访问,且webapps文件夹下新建静态资源页面可以访问到。但是项目页面不能访问,也不返回404,就是一片空白。若输入错误的项目名,则会返回404;(网上多数都是这里的问题,但我不是-_-||)
3.然后排除项目本身的异常,在其他服务器上,同版本的tomcat部署同样的war包,启动后可以正常访问,且在这个访问不了的服务器上,项目后台一直在运行,每天设置的定时任务都可以正常执行并产生日志。
推测: 如此看来,很可能就是目标机器的环境问题了,上过linux的都知道,这个环境要素是相当重要的,无论在上面干点啥都敏感得一批,稍有不慎就会丢给你一堆莫名其妙、刷新你三观的问题。既然tomcat可以正常启动,且webapps下的纯静态资源可以访问到,那么,就很可能是tomcat对java资源的编译问题了。
项目用的是jsp页面(甲方要求的),小众所知,tomcat上的jsp页面,会随着浏览器请求逐一编译成java和class文件,并存放在work目录下。查看了一下work目录,下面有Catalina/localhost/项目名/org/apache/jsp/项目jsp资源路径,里面对应于所有被访问到的jsp页面生成了一对.java、.class文件,如index.jsp被访问过就会有index_jsp.java和index_jsp.class这对cp,一次访问,终身保留,以后每次光顾的都是这里,直到资源被修改或服务重启。没有被请求过的资源则不会出现在这里。
我看到的这个work目录下,只有index.jsp被编译进来,因为其他的根本没机会被请求。问题是这个index既然进来了,就表明请求已经收到,那为何不返回给客户端呢?洗都洗好了,不让人碰,什么情况?是编译后的index异常,拿不出手?还是中间被什么拦截了?放一个在别的地方被正常使用过的index_jsp.java和index_jsp.class来替换一下试试吧。(实际并没有试,因为发现问题可能不在这里。)
(未完待续……) 书接上回…… 昨天突然想到一个问题,项目的首页jsp并没有什么实际内容,在被访问到的时候,它会判断一下当前用户状态,然后跳转到其他页面。所以,在linux上直接用curl http://....访问,会被瞬间弹走。那么就要加上另一个option了,这样:curl -L http://.....,哎,这回有内容了,果然是跳转后的页面!那么本地访问是没问题的,不信的话进到work里面再去看看,果然又多了一对刚才访问到的页面编译后的java类文件。这时又想到另一个文件(之前就是没关注这个,后来经人提醒才想到。。。),tomcat/logs下面的访问日志“localhost_access_log.2020-06-08.txt”,这里面存有所有成功抵达的来访记录,包括来访者IP、时间、请求方式、请求资源以及返回的http状态码。看了下,刚才的访问果然有记录:
127.0.0.1 - - [08/Jun/2020:09:37:18 +0800] "GET /xmm HTTP/1.1" 302 - 127.0.0.1 - - [08/Jun/2020:09:38:10 +0800] "GET /xmm/index.jsp HTTP/1.1" 302 - 127.0.0.1 - - [08/Jun/2020:09:47:12 +0800] "GET /xmm/ HTTP/1.1" 302 - 127.0.0.1 - - [08/Jun/2020:09:47:12 +0800] "GET /xmm/login/main.do?reqId=10010 HTTP/1.1" 302 - 127.0.0.1 - - [08/Jun/2020:09:47:12 +0800] "GET /xmm/login/login.do;jsessionid=D570B19A7F2B44C05FD425E2DD659011 HTTP/1.1" 200 10086 127.0.0.1 - - [08/Jun/2020:09:55:43 +0800] "
本文翻译自:How to check if element exists in the visible DOM?
How do you test an element for existence without the use of the getElementById method? 如何在不使用getElementById方法的情况下测试元素是否存在? I have setup a live demo for reference. 我已经设置了一个现场演示供参考。 I will also print the code on here as well: 我也会在这里打印代码: <!DOCTYPE html> <html> <head> <script> var getRandomID = function (size) { var str = "", i = 0, chars = "0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ"; while (i < size) { str += chars.
科技不断进步的今天,传统的电销模式己经满足企业公司的需求了,ai人工智能电销机器人的诞生,可以所以所有电销企业BOSS的福音,以往的招聘难,人员流失严重,工作效低,人工成本高等等问题都能得到缓解,而电销人员也能解放双手,更好更轻?的去完成业绩,跟进客户,AI技术发展到现在这个阶段,电销行业成为了第一批的受益者!ai人工智能电销机器人将改变未来企业和公司的营销方式。
早在几年前,国家曾发布《机器人产业发展规划(2016-2020)》的通知,对人工智能将对生活做出的改变进行初步规划,更预言人工智能将会使目前的商业模式发生质变。许多人觉得人工智能离自己的生活还十分遥远,替代目前的人工劳作更是遥不可及。但是,事实上,近几年 人工智能在飞速发展,并且改变着每个人的生活。举个简单的例子,我们手机里的siri,它可以帮助你打电话、发邮件、打开聊天设备、社交工具,我们感叹着“siri好智能”的同时,已经成为了人工智能的受益者。更别说现在的智能家电,回到家自动开灯、晚上睡觉自动拉窗帘……这些智能的操作已经渗透了我们生活的方方面面。
更加令人欣喜的是,随着智能语音技术的不断发展、技术层面的不断升级,学习算法的不断完善,如易网行一般的智能电销机器人不断涌现,成为电销行业不可或缺的重要“成员”,企业的电话外呼系统从此开始由人工向智能转型。“人工智能的出现将会大规模淘汰低技术工作人群,并且还会保持一定的速度逐步淘汰高技术工作人群”。智能电销机器人——使企业成本不升反降许多企业主担心,智能电销机器人会使企业运营成本上升,其实这种担心是完全没有必要的,因为实际上恰恰相反,智能电销机器人让企业成本不升反降。有了电销机器人,企业减少了不稳定的人员支出,以云蝠电销机器人为例,每台机器人每天的外呼能达到5000次,节省了人工电话的时间和精力,又能帮助企业减少招聘成本和管理成本等开销。没有人员雇佣费、培训费、五险一金……各种费用的消耗,可以说是从根本上解决企业的人力支出!
人工智能的发展有目共睹,选择合适的智能电销机器人,才能在激流勇进的电销行业不输在起跑线上。深圳市易网行科技有限公司就是研发AI电话营销系统的商家:易网行AI电话机器人具有话术配置,真人语音,识别准确,支持打断,多轮会话,智能拨打,智能沟通,自动记录,自动分类分析,数据挖掘等功能,可满足个性化消费需求,截至目前为止已服务个人与企业超过1000家;帮助企业极大降低企业运营成本,快速提升企业运营效率,打造财富,信息,资源平台,共享智能时代新红利。实现运营成本减少50%以上显著效果!
产品特色:100%真人式语音------高能力语音理解------自主开展工作-------阿里云识别技术--------自由选择时段拨打
能帮你解决的问题:销售业绩提高300%-----工作效率提升13倍-----人工成本降低80%-----客户满意度提升50%-----管理成本降低50%-----转化率提高30%-----拓客量提升8倍
现在来说,电销机器人是一个机遇,就看这几年,电话机器人如雨后春笋般大量出现,就知道未来电销行业的趋势,每个公司的电话营销部门必将会配备电话机器人,如果再不抓紧机会,就会跟不上时代的变化,终将会被淘汰!