0%

需要两步:

1、构建所有需要加载的jar路径,比如 new URL("file:d:/xxxx/1.jar")

2、新建 URLClassLoader 加载 jars

打包一个 1.jar,其中有一个类com.maxzhao.CatPrint

1
2
3
4
5
6
7
public class ClassLoaderTest {
public void tests(){
URL[] pluginLibsPath = new URL[]{new URL("file:d:/xxxx/1.jar")};
ClassLoader classLoader = new URLClassLoader(pluginLibsPath, Thread.currentThread().getContextClassLoader());
Class catClass = classLoader.loadClass(com.maxzhao.CatPrint);
}
}

本文地址: https://github.com/maxzhao-it/blog/post/cb80c3ad/

SPI机制的主要作用是获取接口的所有实现类,可以快速扩展和替换

创建接口及实现类

创建接口:com.maxzhao.IPrint

1
2
3
public interface IPrint {
void print();
}

创建实现类:com.maxzhao.CatPrint

1
2
3
4
5
6
public interface CatPrint implements IPrint {
@Override
public void print(){
System.out.println("cat");
}
}

resources下创建文件:META-INF/services/com.maxzhao.IPrint,写入

1
com.maxzhao.CatPrint

加载接口的实现类

1
2
3
4
5
6
7
8
9
10
public class ServiceLoaderTest {
public void tests(){
ServiceLoader<IPrint> serviceLoader = ServiceLoader.load(IPrint.class, classLoader);
Iterator<IPrint> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
NodeServerPluginStart next = iterator.next();
assert "com.maxzhao.CatPrint".equals(next.getClass().getName())
}
}
}

本文地址: https://github.com/maxzhao-it/blog/post/804090fa/

安装服务

1
apt-get install sendmail sendmail-cf mailutils  ssmtp -y

发送邮件

1
echo "`/usr/sbin/ifconfig |grep inet6`" | mail -s "Mail from shell" xxxx@qq.com

配置源邮件

1
vim /etc/ssmtp/ssmtp.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=xxx@163.com

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.163.com:465 #固定模式
AuthUser=xxx@163.com #发件人的邮箱
AuthPass=xxx # 网易邮箱的授权码
UseTLS=Yes #认证打开
# Where will the mail seem to come from?
#rewriteDomain=

# The full hostname
#hostname=pve.com

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
#FromLineOverride=YES

复杂脚本,IP发送不重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
ip=`/usr/sbin/ifconfig |grep inet6`
echo "$ip" > ipnew.txt
diff_result=$(diff ip.txt ipnew.txt)
if [ ! -f "ip.txt" ]; then
echo "1" > ip.txt
fi
if [ -n "$diff_result" ]; then
\cp ipnew.txt ip.txt
echo "发送邮件"
echo "`cat ip.txt`" | mail -s "Mail from shell" 1441439636@qq.com
else
echo “内容重复”
fi

本文地址: https://github.com/maxzhao-it/blog/post/2bee6c61/

安装图形界面命令

1
2
3
4
5
yum install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-utils -y
yum groupinstall "X Window System" -y
yum groupinstall "GNOME Desktop" "Graphical Administration Tools" -y
yum groupinstall "Chinese Support" -y
gnome-shell --version

配置默认进入 gnome

1
systemctl set-default graphical.target

本文地址: https://github.com/maxzhao-it/blog/post/ea126096/

备份账号

dump.cnf

1
2
3
[mysqldump]
user = maxzhao
password = xxx

简单脚本

1
2
3
4
5
6
7
8
9
#!/bin/bash
echo 'start 备份'
current="`date +%Y%m%d%H%M%S`"
mysqldump --defaults-file=/opt/mysql8/dump.cnf -h 192.168.3.55 -A > /opt/mysql8/dump/55-${current}.sql
echo '已备份为SQL文件:55-${current}.sql'
cd /opt/mysql8/dump/
tar -zcf 55-${current}.tar.gz 55-${current}.sql
rm -f 55-${current}.sql
echo '备份成功:55-${current}.tar.gz'

定时备份

1
crontab -e

输入

1
0 0,12 * * * /opt/mysql8/55-dump.sh

执行

1
systemctl restart crond

没有当前命令则需要安装

1
sudo yum -y install crontabs

删除 30天前的备份

编辑 vim

1
2
3
4

echo '#!/bin/bash' > /opt/mysql8/dump_limit.sh
echo 'find /opt/mysql8/dump/ -type f ! -newermt "30 days ago" -delete' >> /opt/mysql8/dump_limit.sh
chmod u+x /opt/mysql8/dump_limit.sh

同样的

1
crontab -e

输入

1
2
0 0,12 * * * /opt/mysql8/55-dump.sh
0 0,12 * * * /opt/mysql8/dump_limit.sh

本文地址: https://github.com/maxzhao-it/blog/post/3ed70945/

基于JVM的开源数据处理语言主要有Kotlin、Scala、SPL,下面对三者进行多方面的横向比较,从中找出开发效率最高的数据处理语言。本文的适用场景设定为项目开发中常见的数据处理和业务逻辑,以结构化数据为主,大数据和高性能不作为重点,也不涉及消息流、科学计算等特殊场景。

基本特征

适应面

Kotlin的设计初衷是开发效率更高的Java,可以适用于任何Java涉及的应用场景,除了常见的信息管理系统,还能用于WebServer、Android项目、游戏开发,通用性比较好。Scala的设计初衷是整合现代编程范式的通用开发语言,实践中主要用于后端大数据处理,其他类型的项目中很少出现,通用性不如Kotlin。SPL的设计初衷是专业的数据处理语言,实践与初衷一致,前后端的数据处理、大小数据处理都很适合,应用场景相对聚焦,通用性不如Kotlin。

编程范式

Kotlin以面向对象编程为主,也支持函数式编程。Scala两种范式都支持,面向对象编程比Koltin更彻底,函数式编程也比Koltin方便些。SPL可以说不算支持面向对象编程,有对象概念,但没有继承重载这些内容,函数式编程比Kotlin更方便。

运行模式

Kotlin和Scala是编译型语言,SPL是解释型语言。解释型语言更灵活,但相同代码性能会差一点。不过SPL有丰富且高效的库函数,总体性能并不弱,面对大数据时常常会更有优势。

外部类库

Kotlin可以使用所有的Java类库,但缺乏专业的数据处理类库。Scala也可以使用所有的Java类库,且内置专业的大数据处理类库(Spark)。SPL内置专业的数据处理函数,提供了大量时间复杂度更低的基本运算,通常不需要外部Java类库,特殊情况可在自定义函数中调用。

IDE和调试

三者都有图形化IDE和完整的调试功能。SPL的IDE专为数据处理而设计,结构化数据对象呈现为表格形式,观察更加方便,Kotlin和Scala的IDE是通用的,没有为数据处理做优化,无法方便地观察结构化数据对象。

学习难度

Kotlin的学习难度稍高于Java,精通Java者可轻易学会。Scala的目标是超越Java,学习难度远大于Java。SPL的目标就是简化Java甚至SQL的编码,刻意简化了许多概念,学习难度很低。

代码量

Kotlin的初衷是提高Java的开发效率,官方宣称综合代码量只有Java的20%,可能是数据处理类库不专业的缘故,这方面的实际代码量降低不多。Scala的语法糖不少,大数据处理类库比较专业,代码量反而比Kotlin低得多。SPL只用于数据处理,专业性最强,再加上解释型语言表达能力强的特点,完成同样任务的代码量远远低于前两者(后面会有对比例子),从另一个侧面也能说明其学习难度更低。

语法

数据类型

原子数据类型:三者都支持,比如Short、Int、Long、Float、Double、Boolean

日期时间类型:Kotlin缺乏易用的日期时间类型,一般用Java的。Scala和SPL都有专业且方便的日期时间类型。

有特色的数据类型:Kotlin支持非数值的字符Char、可空类型Any?。Scala支持元组(固定长度的泛型集合)、内置BigDecimal。SPL支持高性能多层序号键,内置BigDecimal。

集合类型:Kotlin和Scala支持Set、List、Map。SPL支持序列(有序泛型集合,类似List)。

结构化数据类型:Kotlin有记录集合List,但缺乏元数据,不够专业。Scala有专业的结构化数类型,包括Row、RDD、DataSet、DataFrame(本文以此为例进行说明)等。SPL有专业的结构化数据类型,包括record、序表(本文以此为例进行说明)、内表压缩表、外存Lazy游标等。

Scala独有隐式转换能力,理论上可以在任意数据类型之间进行转换(包括参数、变量、函数、类),可以方便地改变或增强原有功能。

流程处理

三者都支持基础的顺序执行、判断分支、循环,理论上可进行任意复杂的流程处理,这方面不多讨论,下面重点比较针对集合数据的循环结构是否方便。以计算比上期为例,Kotlin代码:

img

Kotlin的forEachIndexed函数自带序号变量和成员变量,进行集合循环时比较方便,支持下标取记录,可以方便地进行跨行计算。Kotlin的缺点在于要额外处理数组越界。

Scala代码:

img

Scala跨行计算不必处理数组越界,这一点比Kotlin方便。但Scala的结构化数据对象不支持下标取记录,只能用lag函数整体移行,这对结构化数据不够方便。lag函数不能用于通用性强的forEach,而要用withColumn之类功能单一的循环函数。为了保持函数式编程风格和SQL风格的底层统一,lag函数还必须配合窗口函数(Python的移行函数就没这种要求),整体代码看上去反而比Kotlin复杂。

SPL代码:

img

SPL对结构化数据对象的流程控制进行了多项优化,类似forEach这种最通用最常用的循环函数,SPL可以直接用括号表达,简化到极致。SPL也有移行函数,但这里用的是更符合直觉的“[相对位置]”语法,进行跨行计算时比Kotlin的绝对定位强大,比Scala的移行函数方便。上述代码之外,SPL还有更多针对结构化数据的流程处理功能,比如:每轮循环取一批而不是一条记录;某字段值变化时循环一轮。

Lambda表达式

Lambda表达式是匿名函数的简单实现,目的是简化函数的定义,尤其是变化多样的集合计算类函数。Kotlin支持Lambda表达式,但因为编译型语言的关系,难以将参数表达式方便地指定为值参数或函数参数,只能设计复杂的接口规则进行区分,甚至有所谓高阶函数专用接口,这就导致Kotin的Lambda表达式编写困难,在数据处理方面专业性不足。几个例子:

img

Koltin的Lambda表达式专业性不足,还表现在使用字段时必须带上结构化数据对象的变量名(it),而不能像SQL那样单表计算时可以省略表名。

同为编译型语言,Scala的Lambda表达式和Kotlin区别不大,同样需要设计复杂的接口规则,同样编写困难,这里就不举例了。计算比上期时,字段前也要带上结构化数据对象变量名或用col函数,形如mData (“Amount”)或col(“Amount”),虽然可以用语法糖弥补,写成$”Amount”或’Amount,但很多函数不支持这种写法,硬要弥补反而使风格不统一。

SPL的Lambda表达式简单易用,比前两者更专业,这与其解释型语言的特性有关。解释型语言可以方便地推断出值参数和函数参数,没有所谓复杂的高阶函数专用接口,所有的函数接口都一样简单。几个例子:

img

SPL可直接使用字段名,无须结构化数据对象变量名,比如:

img

SPL的大多数循环函数都有默认的成员变量~和序号变量#,可以显著提升代码编写的便利性,特别适合结构化数据计算。比如,取出偶数位置的记录:

img

求各组的前3名:

img

SPL函数选项和层次参数

值得一提的是,为了进一步提高开发效率,SPL还提供了独特的函数语法。

有大量功能类似的函数时,大部分程序语言只能用不同的名字或者参数进行区分,使用不太方便。而SPL提供了非常独特的函数选项,使功能相似的函数可以共用一个函数名,只用函数选项区分差别。比如,select函数的基本功能是过滤,如果只过滤出符合条件的第1条记录,可使用选项@1:

img

对有序数据用二分法进行快速过滤,使用@b:

img

函数选项还可以组合搭配,比如:

img

有些函数的参数很复杂,可能会分成多层。常规程序语言对此并没有特别的语法方案,只能生成多层结构数据对象再传入,非常麻烦。SQL使用了关键字把参数分隔成多个组,更直观简单,但这会动用很多关键字,使语句结构不统一。而SPL创造性地发明了层次参数简化了复杂参数的表达,通过分号、逗号、冒号自高而低将参数分为三层:

img

数据源

数据源种类

Kotlin原则上可以支持所有的Java数据源,但代码很繁琐,类型转换麻烦,稳定性也差,这是因为Kotlin没有内置的数据源访问接口,更没有针对结构化数据处理做优化(JDBC接口除外)。从这个意义讲,也可以说它不直接支持任何数据源,只能使用Java第三方类库,好在第三方类库的数量足够庞大。

Scala支持的数据源种类比较多,且有六种数据源接口是内置的,并针对结构化数据处理做了优化,包括:JDBC、CSV、TXT、JSON、Parquet列存格式、ORC列式存储,其他的数据源接口虽然没有内置,但可以用社区小组开发的第三方类库。Scala提供了数据源接口规范,要求第三方类库输出为结构化数据对象,常见的第三方接口有XML、Cassandra、HBase、MongoDB等。

SPL内置了最多的数据源接口,并针对结构化数据处理做了优化,包括:

JDBC(即所有的RDB)

CSV、TXT、JSON、XML、Excel

HBase、HDFS、Hive、Spark

Salesforce、阿里云

Resful、WebService、Webcrawl

Elasticsearch、MongoDB、Kafka、R2dbc、FTP

Cassandra、DynamoDB、influxDB、Redis、SAP

这些数据源都可以直接使用,非常方便。对于其他未列入的数据源,SPL也提供了接口规范,只要按规范输出为SPL的结构化数据对象,就可以进行后续计算。

代码比较

以规范的CSV文件为例,比较三种语言的解析代码。Kotlin:

img

Koltin专业性不足,通常要硬写代码读取CSV,包括事先定义数据结构,在循环函数中手工解析数据类型,整体代码相当繁琐。也可以用OpenCSV等类库读取,数据类型虽然不用在代码中解析,但要在配置文件中定义,实现过程不见得简单。

Scala专业性强,内置解析CSV的接口,代码比Koltin简短得多:

img

具体的代码在下面的原链接里

Scala在解析数据类型时麻烦些,其他方面没有明显缺点。

SPL更加专业,连解析带计算只要一行:

img

跨源计算

JVM数据处理语言的开放性强,有足够的能力对不同的数据源进行关联、归并、集合运算,但数据处理专业性的差异,导致不同语言的方便程度区别较大。

Kotlin不够专业,不仅缺乏内置数据源接口,也缺乏跨源计算函数,只能硬写代码实现。假设已经从不同数据源获得了员工表和订单表,现在把两者关联起来:

img

很容易看出Kotlin的缺点,代码只要一长,Lambda表达式就变得难以阅读,还不如普通代码好理解;关联后的数据结构需要事先定义,灵活性差,影响解题流畅性。

Scala比Kotlin专业,不仅内置了多种数据源接口,而且提供了跨源计算的函数。同样的计算,Scala代码简单多了:

img

可以看到,Scala不仅具备专用于结构化数据计算的对象和函数,而且可以很好地配合Lambda语言,代码更易理解,也不用事先定义数据结构。

SPL更加专业,结构化数据对象更专业,跨源计算函数更方便,代码更简短:

img

自有存储格式

反复使用的中间数据,通常会以某种格式存为本地文件,以此提高取数性能。Kotlin支持多种格式的文件,理论上能够进行中间数据的存储和再计算,但因为在数据处理方面不专业,基本的读写操作都要写大段代码,相当于并没有自有的存储格式。

Scala支持多种存储格式,其中parquet文件常用且易用。parquet是开源存储格式,支持列存,可存储大量数据,中间计算结果(DataFrame)可以和parquet文件方便地互转。遗憾的是,parquet的索引尚不成熟。

img

SPL支持btx和ctx两种私有二进制存储格式,btx是简单行存,ctx支持行存、列存、索引,可存储大量数据并进行高性能计算,中间计算结果(序表/游标)可以和这两种文件方便地互转。

img

结构化数据计算

结构化数据对象

数据处理的核心是计算,尤其是结构化数据的计算。结构化数据对象的专业程度,深刻地决定了数据处理的方便程度。

Kotlin没有专业的结构化数据对象,常用于结构化数据计算的是List,其中EntityBean可以用data class简化定义过程。

List是有序集合(可重复),凡涉及成员序号和集合的功能,Kotlin支持得都不错。比如按序号访问成员:

img

还可以按倒数序号取成员:

img

涉及顺序的计算难度都比较大,Kotlin支持有序计集合,进行相关的计算会比较方便。作为集合的一种,List擅长的功能还有集合成员的增删改、交差合、拆分等。但List不是专业的结构化数据对象,一旦涉及字段结构相关的功能,Kotlin就很难实现了。比如,取Orders中的两个字段组成新的结构化数据对象。

img

上面的功能很常用,相当于简单SQL语句select Client,Amount from Orders,但Kotlin写起来就很繁琐,不仅要事先定义新结构,还要硬编码完成字段的赋值。简单的取字段功能都这么繁琐,高级些的功能就更麻烦了,比如:按字段序号取、按参数取、获得字段名列表、修改字段结构、在字段上定义键和索引、按字段查询计算。

Scala也有List,与Kotlin区别不大,但Scala为结构化数据处理设计了更加专业的数据对象DataFrame(以及RDD、DataSet)。 DataFrame是有结构的数据流,与数据库结果集有些相似,都是无序集合,因此不支持按下标取数,只能变相实现。比如,第10条记录:

img

可以想象,凡与顺序相关的计算,DataFrame实现起来都比较麻烦,比如区间、移动平均、倒排序等。 除了数据无序,DataFrame也不支持修改(immutable特性),如果想改变数据或结构,必须生成新的DataFrame。比如修改字段名,实际上要通过复制记录来实现:

img

DataFrame支持常见的集合计算,比如拆分、合并、交差合并,其中并集可通过合集去重实现,但因为要通过复制记录来实现,集合计算的性能普遍不高。 虽然有不少缺点,但DataFrame是专业的结构化数据对象,字段访问方面的能力是Kotlin无法企及的。比如,获得元数据/字段名列表:

img

还可以方便地用字段取数,比如,取两个字段形成新dataframe:

img

或用计算列形成新DataFrame:

img

遗憾的是,DataFrame只支持用字符串形式的名字来引用字段,不支持用字段序号或默认名字,导致很多场景下不够方便。此外,DataFrame也不支持定义索引,无法进行高性能随机查询,专业性还有缺陷。

SPL的结构化数据对象是序表,优点是足够专业,简单易用,表达能力强。 按序号访问成员:

img

按倒数序号取记录,独特之处在于支持负号表示倒数,比Kotlin专业且方便:

img

作为集合的一种,序表也支持集合成员的增删改、交并差合、拆分等功能。由于序表和List一样都是可变集合(mutable),集合计算时尽可能使用游离记录,而不是复制记录,性能比Scala好得多,内存占用也少。 序表是专业的结构化数据对象,除了集合相关功能外,更重要的是可以方便地访问字段。比如,获得字段名列表:

img

取两个字段形成新序表:

img

用计算列形成新序表:

img

修改字段名:

img

有些场景需要用字段序号或默认名字访问字段,SPL都提供了相应的访问方法:

img

作为专业的结构化数据对象,序表还支持在字段上定义键和索引:

img

计算函数

Kotlin支持部分基本计算函数,包括:过滤、排序、去重、集合的交叉合并、各类聚合、分组汇总。但这些函数都是针对普通集合的,如果计算目标改成结构化数据对象,计算函数库就显得非常不足,通常就要辅以硬编码才能实现计算。还有很多基本的集合运算是Kotlin不支持的,只能自行编码实现,包括:关联、窗口函数、排名、行转列、归并、二分查找等。其中,归并和二分查找等属于次序相关的运算,由于Kotlin List是有序集合,自行编码实现这类运算不算太难。总体来讲,面对结构化数据计算,Kotlin的函数库可以说较弱。

Scala的计算函数比较丰富,且都是针对结构化数据对象设计的,包括Kotlin不支持的函数:排名、关联、窗口函数、行转列,但基本上还没有超出SQL的框架。也有一些基本的集合运算是Scala不支持的,尤其是与次序相关的,比如归并、二分查找,由于Scala DataFrame沿用了SQL中数据无序的概念,即使自行编码实现此类运算,难度也是非常大的。总的来说,Scala的函数库比Kotlin丰富,但基本运算仍有缺失。

SPL的计算函数最丰富,且都是针对结构化数据对象设计的,SPL极大地丰富了结构化数据运算内容,设计了很多超出SQL的内容,当然也是Scala/Kotlin不支持的函数,比如有序计算:归并、二分查找、按区间取记录、符合条件的记录序号;除了常规等值分组,还支持枚举分组、对齐分组、有序分组;将关联类型分成外键和主子;支持主键以约束数据,支持索引以快速查询;对多层结构的数据(多表关联或Json\XML)进行递归查询等。

以分组为例,除了常规的等值分组外,SPL还提供了更多的分组方案:

枚举分组:分组依据是若干条件表达式,符合相同条件的记录分为一组。

对齐分组:分组依据是外部集合,记录的字段值与该集合的成员相等的分为一组,组的顺序与该集合成员的顺序保持一致,允许有空组,可单独分出一组“不属于该集合的记录”。

有序分组:分组依据是已经有序的字段,比如字段发生变化或者某个条件成立时分出一个新组,SPL直接提供了这类有序分组,在常规分组函数上加个选项就可以完成,非常简单而且运算性能也更好。其他语言(包括SQL)都没有这种分组,只能费劲地转换为传统的等值分组或者自己硬编码实现。

下面我们通过几个常规例子来感受一下这三种语言在计算函数方式的差异。

排序

按Client顺序,Amount逆序排序。Kotlin:

img

Kotlin代码不长,但仍有不便之处,包括:逆序正序是两个不同的函数,字段名必须带表名,代码写出的字段顺序与实际的排序顺序相反。

Scala:

img

Scala简单多了,负号代表逆序,代码写出的字段顺序与排序的顺序相同。遗憾之处在于:字段仍要带表名;编译型语言只能用字符串实现表达式的动态解析,导致代码风格不统一。

SPL:

img

SPL代码更简单,字段不必带表名,解释型语言代码风格容易统一。

分组汇总

Kotlin:

img

Kotlin代码比较繁琐,不仅要用groupingBy和fold函数,还要辅以硬编码才能实现分组汇总。当出现新的数据结构时,必须事先定义才能用,比如分组的双字段结构、汇总的双字段结构,这样不仅灵活性差,而且影响解题流畅性。最后的排序是为了和其他语言的结果顺序保持一致,不是必须的。

Scala:

img

Scala代码简单多了,不仅易于理解,而且不用事先定义数据结构。

SPL:

img

SPL代码最简单,表达能力不低于SQL。

关联计算

两个表有同名字段,对其关联并分组汇总。Kotlin代码:

img

Kotlin代码很繁琐,很多地方都要定义新数据结构,包括关联结果、分组的双字段结构、汇总的双字段结构。

Scala

img

Scala比Kolin简单多了,不用繁琐地定义数据结构,也不必硬编码。

SPL更简单:

img

综合数据处理对比

CSV内容不规范,每三行对应一条记录,其中第二行含三个字段(即集合的集合),将该文件整理成规范的结构化数据对象,并按第3和第4个字段排序.

Kotlin:

img

Koltin在数据处理方面专业性不足,大部分功能要硬写代码,包括按位置取字段、从集合的集合取字段。

Scala:

img

Scala在数据处理方面更加专业,大量使用结构化计算函数,而不是硬写循环代码。但Scala缺乏有序计算能力,相关的功能通常要添加序号列再处理,导致整体代码冗长。 SPL:

img

SPL在数据处理方面最专业,只用结构化计算函数就可以实现目标。SPL支持有序计算,可以直接按位置分组,按位置取字段,从集合中的集合取字段,虽然实现思路和Scala类似,但代码简短得多。

应用结构

Java应用集成

Kotlin编译后是字节码,和普通的class文件一样,可以方便地被Java调用。比如KotlinFile.kt里的静态方法fun multiLines(): List,会被Java正确识别,直接调用即可:

img

Scala编译后也是字节码,同样可以方便地被Java调用。比如ScalaObject对象的静态方法def multiLines():DataFrame,会被Java识别为Dataset类型,稍做修改即可调用:

img

SPL提供了通用的JDBC接口,简单的SPL代码可以像SQL一样,直接嵌入Java:

img

复杂的SPL代码可以先存为脚本文件,再以存储过程的形式被Java调用,可有效降低计算代码和前端应用的耦合性。

img

SPL是解释型语言,修改后不用编译即可直接执行,支持代码热切换,可降低维护工作量,提高系统稳定性。Kotlin和Scala是编译型语言,编译后必须择时重启应用。

交互式命令行

Kotlin的交互式命令行需要额外下载,使用Kotlinc命令启动。Kotlin命令行理论上可以进行任意复杂的数据处理,但因为代码普遍较长,难以在命令行修改,还是更适合简单的数字计算:

img

Scala的交互式命令行是内置的,使用同名命令启动。Scala命令行理论上可以进行数据处理,但因为代码比较长,更适合简单的数字计算:

img

SPL内置了交互式命令行,使用“esprocx -r -c”命令启动。SPL代码普遍较短,可在命令行进行简单的数据处理。

img

通过多方面的比较可知:对于应用开发中常见的数据处理任务,Kotlin因为不够专业,开发效率很低;Scala有一定的专业性,开发效率比Kotlin高,但还比不上SPL;SPL语法更简练,表达效率更高,数据源种类更多,接口更易用,结构化数据对象更专业,函数更丰富且计算能力更强,开发效率远高于Kotlin和Scala。

本文地址: https://github.com/maxzhao-it/blog/post/204d2ea9/

操作JSON

json.dumps() 将 Python 对象编码成 JSON 字符串
json.loads() 将已编码的 JSON 字符串解码为 Python 对象
json.dump() 将Python内置类型序列化为json对象后写入文件
json.load() 读取文件中json形式的字符串元素转化为Python类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import json
data = {'name':'nanbei','age':18}
# 将Python对象编码成json字符串
print(json.dumps(data)) # {"name": "nanbei", "age": 18}
data = {'name':'nanbei','age':18}
# 将Python对象编码成json字符串
# print(json.dumps(data))
# 将json字符串解码成Python对象
a = json.dumps(data)
print(json.loads(a)) #{'name': 'nanbei', 'age': 18}

data = (1,2,3,4)
data_json = [1,2,3,4]
#将Python对象编码成json字符串
print(json.dumps(data)) # [1, 2, 3, 4]
print(json.dumps(data_json))# [1, 2, 3, 4]

#将Python对象编码成json字符串
a = json.dumps(data)
b = json.dumps(data_json)
#将json字符串编码成Python对象
print(json.loads(a))# [1, 2, 3, 4]
print(json.loads(b))# [1, 2, 3, 4]

将Python内置类型序列化为json对象后写入文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

import json

data = {
'nanbei':'haha',
'a':[1,2,3,4],
'b':(1,2,3)
}
with open('json_test.txt','w+') as f:
json.dump(data,f)


with open('json_test.txt','r+') as f:
print(json.load(f))

6.5 更多实例
json.dumps():将一个Python数据类型列表编码成json格式的字符串

#python的列表转换为json的数组

import json
json.dumps([1,2,3])
‘[1, 2, 3]’
#python的字符串转换为json的字符串
json.dumps(‘abdcs’)
‘“abdcs”‘
#python的元祖转换为json的数组
json.dumps((1,2,3,’a’))
‘[1, 2, 3, “a”]’#注意此时显示的是方括号
#python的字典转换为json的对象
json.dumps({1:’a’,2:’b’})
‘{“1”: “a”, “2”: “b”}’#注意此时1和2转换后是加了引号的,因为json的名称是必须要加引号的
#python的整数转换为json的数字
json.dumps(13)
‘13’
#python的浮点数转换为json的数字
json.dumps(3.1415)
‘3.1415’
#python的unicode字符串转换为json的字符串
json.dumps(u’a’)
‘“a”‘
#python的True转换为json的数组true
json.dumps(True)
‘true’
#python的False转换为json的数组false
json.dumps(False)
‘false’
#python的None转换为json的null
json.dumps(None)
‘null’
#json本质上是一个字符串
type(json.dumps(‘abc’))
<class ‘str’>
dump和dumps:

import json

dumps可以格式化所有的基本数据类型为字符串

data1 = json.dumps([]) # 列表
print(data1, type(data1))
data2 = json.dumps(2) # 数字
print(data2, type(data2))
data3 = json.dumps(‘3’) # 字符串
print(data3, type(data3))
dict = {“name”: “Tom”, “age”: 23} # 字典
data4 = json.dumps(dict)
print(data4, type(data4))

with open(“test.json”, “w”, encoding=’utf-8’) as f:
# indent 超级好用,格式化保存字典,默认为None,小于0为零个空格
f.write(json.dumps(dict, indent=4))
json.dump(dict, f, indent=4) # 传入文件描述符,和dumps一样的结果
得到的输出结果如下:格式化所有的数据类型为str类型:

[] <class ‘str’>
2 <class ‘str’>
“3” <class ‘str’>
{“name”: “Tom”, “age”: 23} <class ‘str’>
test.json中的内容:

{
“name”: “Tom”,
“age”: 23
}
load和loads

import json

dict = ‘{“name”: “Tom”, “age”: 23}’ # 将字符串还原为dict
data1 = json.loads(dict)
print(data1, type(data1))

with open(“test.json”, “r”, encoding=’utf-8’) as f:
data2 = json.loads(f.read()) # load的传入参数为字符串类型
print(data2, type(data2))
f.seek(0) # 将文件游标移动到文件开头位置
data3 = json.load(f)
print(data3, type(data3))
运行结果如下:

{‘name’: ‘Tom’, ‘age’: 23} <class ‘dict’>
{‘name’: ‘Tom’, ‘age’: 23} <class ‘dict’>
{‘name’: ‘Tom’, ‘age’: 23} <class ‘dict’>

  1. 参数详解

dumps(obj,skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
函数作用: 将Python对象转变成JSON对象,便于序列化内存/文件中。

参数:

skipkeys: 如果为True的话,则只能是字典对象,否则会TypeError错误, 默认False
ensure_ascii: 确定是否为ASCII编码
check_circular: 循环类型检查,如果为True的话
allow_nan: 确定是否为允许的值
indent: 会以美观的方式来打印,呈现,实现缩进
separators: 对象分隔符,默认为,
encoding: 编码方式,默认为utf-8
sort_keys: 如果是字典对象,选择True的话,会按照键的ASCII码来排序
对于dump来说,只是多了一个fp参数:

简单说就是dump需要一个类似文件指针的参数(并不是真正的指针,可以称之为文件对象),与文件操作相结合,即先将Python文件对象转化为json字符串再保存在文件中。

dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw)

Serialize obj as a JSON formatted stream to fp (a.write()-supporting file-like object).
类似Java中的class implements java.io.Serializable

Java提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。

  1. JSON反序列化为对象
    JSON反序列化为类对象或者类的实例,使用的是loads()方法中的object_hook参数:

代码示例:

import json

定义一个员工类

class Employee(object):
def init(self,name,age,sex,tel):
self.name=name
self.age=age
self.sex=sex
self.tel=tel

实例化一个对象

emp = Employee(‘kongsh’,18,’female’,13123456789)

定义JSON转换Python实例的函数

def jsonToClass(emp):
return Employee(emp[‘name’], emp[‘age’], emp[‘sex’], emp[‘tel’])

定义一个json字符串(字典)

json_str = ‘{“name”: “kongsh”, “age”: 18, “sex”: “female”, “tel”: 13123456789}’

emp = json.loads(json_str, object_hook=jsonToClass)
print (emp)
print(emp.name)
结果展示:

在这里插入图片描述

  1. 常见的错误

9.1 读取多行的JSON文件
假如要读取一个多行的JSON文件:

{“坂”: [“坂5742”]}
{“构”: [“构6784”]}
{“共”: [“共5171”]}
{“钩”: [“钩94a9”]}
{“肮”: [“肮80ae”]}
{“孤”: [“孤5b64”]}
如果直接使用:

with open(json_path, ‘r’) as f:
json_data = json.load(f)
就会报错:抛出异常JSONDecodeError

json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 17)

表示数据错误,数据太多,第2行第一列

因为json只能读取一个文档对象,有两个解决办法

单行读取文件,一次读取一行文件。
保存数据源的时候,格式写为一个对象(dump)。

  1. 单行读取文件:

with open(json_path, ‘r’) as f:
for line in f.readlines():
json_data = json.loads(line)
但是这种做法还有个问题,如果JSON文件中包含空行,还是会抛出JSONDecodeError异常。

json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

可以先处理空行,再进行文件读取操作:

for line in f.readlines():
line = line.strip() # 使用strip函数去除空行
if len(line) != 0:
json_data = json.loads(line)
2. 合并为一个对象:

将json文件处理成一个对象文件(序列化):

{“dict”: [
{“坂”: [“坂5742”]},
{“构”: [“构6784”]},
{“共”: [“共5171”]},
{“钩”: [“钩94a9”]},
{“肮”: [“肮80ae”]},
{“孤”: [“孤5b64”]}
]}
然后再用:

with open(json_path, ‘r’) as f:
json_data = json.loads(f.read())

9.2 控制台乱码

ensure_ascii=False 表示在控制台能够显示中文

json_str = json.dumps(center_data_list, ensure_ascii=False)

  1. 总结
    json.dumps 将 Python 对象编码成 JSON 字符串
    json.loads 将已编码的 JSON 字符串解码为 Python 对象
    json.dump和json.load,需要传入文件描述符,加上文件操作。
    json内部的格式要注意,一个好的格式能够方便读取,可以用indent格式化。
    个人总结:

dump:存入的实例对象object(序列化)
dumps:存入的JSON的字符串数据
load:读取的实例对象object(反序列化)
loads:读取的JSON的字符串数据,转化为Python字典对象

本文地址: https://github.com/maxzhao-it/blog/post/93daf94d/