Git Product home page Git Product logo

blog's Introduction

Hi there 👋

Techial's GitHub stats

广告位出租

Billboard rental

看板レンタル

Powered by Google translate

blog's People

Contributors

techiall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

goworldchina

blog's Issues

使用 Git 同步两台电脑之间的代码

  • 使用 ssh 连接到远程的 Linux 服务器时,写代码觉得有时候很麻烦,没本地舒服。
  • 测试环境
    • 远程 Linux 服务器
    • 本地 Window 系统
  • 最终效果
    • 在本地 window 系统写完代码,commit 之后,在 Linux 端 fetch/pull 即可同步代码,而且附带了版本控制,方便回滚。

远程 Linux 服务器配置

有关的配置如下

  • 将 git 默认的 nano 编辑器修改为 vim

    vim ~/.bashrc ,在该文件的最后一行添加 export EDITOR=/usr/bin/vim

  • Git 全局设置

    git config --global user.name "name"
    git config --global user.email "email"

如果当期仓库为空的,参考一下命令创建一个 git 仓库

mkdir demo
cd demo
git init
touch README.md
git add README.md
git commit -m "first commit"

若项目已经存在

cd demo
git init
git add --all
git commit -m "first commit"

如果还绑定了远程,使用 git remote rm origin 命令可以取消和远程库的连接。

# Linux 服务器创建好 git 仓库后,切换到 demo 父目录
cd ..

# 从刚才的库复制一个新的仓库,不含源码
git clone --bare ./demo

# ls 命令查看目录结构,会发现多了一个 demo.git
ls

# 回到刚才的 demo 目录下,我们要绑定远程库了
cd demo
git remote add origin ../demo.git

# 推送并绑定
git push -u origin maser

到此,在 Linux 服务器配置完成。

Window 环境配置

强烈推荐 cmder 这款软件

# git clone ssh://<登录的用户名>@<ip/域名><git仓库所在的目录> <存放本地的目录>
git clone ssh://<username>@<ip>~/demo.git ./demo

clone 下来后,在 Windows 写完代码,add commit,然后 git push origin master 即可,和远程操作一样, 如 push / pull / fetch 等等。

在 Linux 的文件夹里面使用 Git 合并从 Windows 提交的 commit 即可实现同步。

MongoTemplate 使用总结

本文的示例如下,集合名称为 Message。

{
  "_id" : ObjectId("5ccaff6ff86fa11cd44b68e1"),
  "title" : "title",
  "content" : "content",
  "editorId" : "1",
  "list" : [{
      "id" : "2",
      "isRead" : "READ",
      "readTime" : ISODate("2019-05-02T14:33:58.558Z")
    },
    {
      "id" : "3",
      "isRead" : "READ",
      "readTime" : ISODate("2019-05-02T14:35:51.658Z")
    }],
  "createTime" : ISODate("2019-05-02T14:32:15.284Z")
}

自动注入该变量。

@Autowired
private MongoTemplate mongoTemplate;

根据 ID 进行查找

可能你第一反应写出以下代码,但是结果不如你所愿,他返回的集合是空。

Query query = Query.query(Criteria.where("_id").in("5ccaff6ff86fa11cd44b68e1");
mongoTemplate.find(query, Message.class);

我们改成一下这种方式即可查询得到。引入 ObjectId 即可。

import org.bson.types.ObjectId; // 引入 ObjectId;

Query query = Query.query(Criteria.where("_id")
                          .in(new ObjectId("5ccaff6ff86fa11cd44b68e1"));
mongoTemplate.find(query, Message.class);

查询嵌入文档里的字段

我们可以用 . 点表示法来查询嵌入文档里的字段。格式为 <嵌入的文档名>.<字段>

假设我们需要根据 list 中 id 进行查询。使用 . 点表示法进行查询,方法如下

Query query = Query.query(Criteria.where("list.id").in("2"));
mongoTemplate.find(query, Message.class);

更新嵌入文档里面的字段

list 文档里面是一个数组,如果我们对 list 中的 id 字段为 "2" 进行更新,那我们得先查到这条数据,接着在对他进行更新。若只想更新一条,可以使用 mongoTemplate.updateFirst,更新多条,可以使用 mongoTemplate.findAndModify 或者 mongoTmplate.updateMulti

下面以 mongoTmplate.updateMulti 为例。

mongoTemplate.updateMulti(
	Query.query(Criteria.where("_id").in(new ObjectId("5ccaff6ff86fa11cd44b68e1"))),
	Update.update("list.$.isRead", "READ")
		.set("list.$.readTime", new Date()),
	Message.class
);

update 用到了 $ 运算符, $ 运算符充当占位符,表示访问嵌入文档中的字段。你可以把 $ 当成一个下标。查询的时候不需要用 $,更新的时候就需要。

unwind 拆分字段

个人觉得这方法超级好用!

我们想把 list 中的每一个元素输出成一份新的文档,也就是变成

{
  "_id" : ObjectId("5ccaff6ff86fa11cd44b68e1"),
  "title" : "title",
  "content" : "content",
  "editorId" : "1",
  "list" : {
      "id" : "2",
      "isRead" : "READ",
      "readTime" : ISODate("2019-05-02T14:33:58.558Z")
    },
  "createTime" : ISODate("2019-05-02T14:32:15.284Z")
},
{
  "_id" : ObjectId("5ccaff6ff86fa11cd44b68e1"),
  "title" : "title",
  "content" : "content",
  "editorId" : "1",
  "list" : {
      "id" : "3",
      "isRead" : "READ",
      "readTime" : ISODate("2019-05-02T14:35:51.658Z")
    },
  "createTime" : ISODate("2019-05-02T14:32:15.284Z")
}

我们就需要 unwind 这个操作符了。他是属于 mongodb 中的聚合查询。

我们需要在被操作的字段名称前加上 $ 符号,在这个示例之中需要被操作的字段也就是 list

mongoTemplate 操作如下。我们需要定义一个新的类来接受返回的数据,如果懒得定义,使用 java.lang.Object 来接收也是可以的。

Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.unwind("$list"),
);
mongoTemplate
	.aggregate(aggregation, Message.class, NotifyMessage.class)
    // 以上那句也可以改成
    // 	.aggregate(aggregation, Message.class, Object.class)
    .getMappedResults()
    .stream()
    .collect(Collectors.toList());

参考文档

Java 函数式编程

函数式编程**

写多了 for 循环,都感觉 for 写的太丑了,能不用 for 就不用 for。后来发现 函数式编程就可以很好的代替 for。

要理解函数式编程,你需要特别明确的知道你要输入的东西,和你要输出的东西。不用考虑中间的过程。这和写的语句有一些不一样,要深入理解函数式编程,首先先要转化一下思考方式。

image

Java 函数式接口

函数接口 形式 作用
Function Function<A, B> toA = (B a) -> {...}; 将 B 转换成 A
Predicate Predicate<A> aIsTrue = (A a) -> {...}; 判断 A 是 true 还是 false
Consumer Consumer<A> aConsumer = (A a) -> {...} 执行某些操作,没有返回值。
Supplier Supplier<A> aSupplier = () -> {return A;} 返回一个对象

日常使用中,FunctionPredicate 用的比较多,把某些方法用函数接口封装起来后,利用 Java 的 stream,可以让你的代码看起来更加简洁,在也不用写 for 循环了。

这些函数接口其实还扩展出来很多其他的接口,如接收两个参数返回一个对象的 BiFunction<A, B, C>,接收 B 和 C,并返回 A;接受一个对象 A 返回一个 int 类型的 IntFunction<A>。其实想想,这个接口也就是相当于 Function<int, A> 接受一个 A 对象,返回一个 int 类型。

我们可以通过猜方法名来知道他的意思,如 ToIntBiFunction<A,B> ,意思也就是 接收 A 和 B 两个对象,返回一个 int 类型。

自定义函数接口

如果你觉得自带的这些满足不了你的需求,那你可以选择自定义函数接口。

使用 @FunctionalInterface 注解。

比如你想传进 3 个参数,然后返回 1 个值,那你可以选择写两个 Function,或者就自定义函数接口。形式如下。

    @FunctionalInterface
    public interface toA<A, B, C, D> {
        A apply(B b, C c, D d);
    }

这段代码的意思是,输入 B,C,D 然后输出 A。当然了,你也可以调换顺序写,改成输入 A,B,C 然后输出 D。

    @FunctionalInterface
    public interface toD<A, B, C, D> {
        D apply(A a, B b, C c);
    }

调用的时候,我们可以这样子写

toA<A, B, C, D> toA = (B b, C c, D d) -> {...};
A a = toA(b, c, d);

Makefile

基础知识

编译和链接

1

把过程了解了,为什么要执行这条命令也就轻松多了。

绘图参考链接 http://www.ruanyifeng.com/blog/2014/11/compiler.html

静态库和动态库

  • ldd 查看依赖库。
  • 静态库和动态库同名时,优先使用动态库。
  • export LD_LIBRARY_PATH= 只是临时设置,关闭 bash 重新启动会消失。
静态库 动态库
特点 编译的时候将所有数据整合进目标代码中 编译时没有被编译进目标代码,执行到相关函数才调用
后缀 *.a *.so
命名规范 lib*.a lib*.so.major.minor
共同点 得存在 *.o 得存在 *.o
编译 ar crv out.a src_1.o src_2.o ... gcc -shared -fPIC -o libout.so src.o
链接 gcc main.c out.a -o out gcc -o out main.c libout.so
注意事项 / 将 *.so 放在 /usr/lib 或者 设置 export LD_LIBRARY_PATH="./"

Makefile 介绍

跟我一起写Makefile 感觉一篇文章就够了。

Makefile 注释方法

  • 单行注释

     # this is comment
  • 多行注释(方式一

     # this is comment \
     	this is comment \
     	... \
  • 多行注释(方式二

     # this is comment
     # this is comment
     # ...

Makefile 的规则

写 Makefile 文件之前,首先得先明白编译和链接过程,知道要生成什么中间文件,过程懂了,再配合 Makefile 的书写规则,就没有什么太多问题。

在讲述这个makefile之前,还是让我们先来粗略地看一看makefile的规则。

target ... : prerequisites ...
   command
   ...
   ...
  • target

    可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对 于标签这种特性,在后续的“伪目标”章节中会有叙述。

  • prerequisites

    生成该target所依赖的文件和/或target

  • command

    该target要执行的命令(任意的shell命令)

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command中。说白一点就是说:

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

这就是makefile的规则,也就是makefile中最核心的内容。

原文链接 https://seisman.github.io/how-to-write-makefile/introduction.html#id1

Leetcode | 行程和用户

Trips 表中存所有出租车的行程信息。每段行程有唯一健 Id,Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。

+----+-----------+-----------+---------+--------------------+----------+
| Id | Client_Id | Driver_Id | City_Id |        Status      |Request_at|
+----+-----------+-----------+---------+--------------------+----------+
| 1  |     1     |    10     |    1    |     completed      |2013-10-01|
| 2  |     2     |    11     |    1    | cancelled_by_driver|2013-10-01|
| 3  |     3     |    12     |    6    |     completed      |2013-10-01|
| 4  |     4     |    13     |    6    | cancelled_by_client|2013-10-01|
| 5  |     1     |    10     |    1    |     completed      |2013-10-02|
| 6  |     2     |    11     |    6    |     completed      |2013-10-02|
| 7  |     3     |    12     |    6    |     completed      |2013-10-02|
| 8  |     2     |    12     |    12   |     completed      |2013-10-03|
| 9  |     3     |    10     |    12   |     completed      |2013-10-03| 
| 10 |     4     |    13     |    12   | cancelled_by_driver|2013-10-03|
+----+-----------+-----------+---------+--------------------+----------+

Users 表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。

+----------+--------+--------+
| Users_Id | Banned |  Role  |
+----------+--------+--------+
|    1     |   No   | client |
|    2     |   Yes  | client |
|    3     |   No   | client |
|    4     |   No   | client |
|    10    |   No   | driver |
|    11    |   No   | driver |
|    12    |   No   | driver |
|    13    |   No   | driver |
+----------+--------+--------+

写一段 SQL 语句查出 2013年10月1日2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

+------------+-------------------+
|     Day    | Cancellation Rate |
+------------+-------------------+
| 2013-10-01 |       0.33        |
| 2013-10-02 |       0.00        |
| 2013-10-03 |       0.50        |
+------------+-------------------+

mysql

解决这题得知道以下几个。

  • 非禁止用户的取消率 = cancelled开头的 status / 总的个数。
  • if 的用法
  • sum() 的用法
    • 就是字面意思,求和。
  • round() 的用法
    • ROUND(X,D) -- 表示将值 X 四舍五入为小数点后 D 位的数值,D 为小数点后小数位数。

t1.Banned = 'No' //该用户未被禁止

Request_at BETWEEN '2013-10-01' AND '2013-10-03' // 题目要求的时间范围

round(sum(if(Status like 'ca%', 1, 0)) / count(*), 2) // 对得到的值保留小数点后两位,利用 if sum 进行计数,就可以算得取消率

select Request_at DAY, 

        round(sum(if(Status like 'ca%', 1, 0)) / count(*), 2) 'Cancellation Rate'
        
from Trips t inner join Users t1 on t.Client_Id = t1.Users_Id

where t1.Banned = 'No' AND (Request_at BETWEEN '2013-10-01' AND '2013-10-03')

GROUP BY t.Request_at;

CTF | 常见的符号类解密

摩斯电码

密文格式:

-... -.- -.-. - ..-. -- .. ... -.-.

在线解密链接: http://atool.org/morse.php

brainfuck

密文格式:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.

在线解密链接: https://www.splitbrain.org/services/ook

Ook

密文格式:

Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook.

在线解密链接: https://www.splitbrain.org/services/ook

JsFuck

密文格式:

[][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]][([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[!+[]+!+[]+!+[]]]]+([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]]]+([][[]]+[])[+[[+!+[]]]]+(![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[+!+[]]]]+([][[]]+[])[+[[+[]]]]+([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]]]+(!![]+[])+[[+!+[]]]][+[[+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]+(!![]+[])[+[[+[]]]]+([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[+!+[]]]+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+[+!+[]]+([][(![]+[])[+[[+[]]]]+([][[]]+[])[+[[!+[]+!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[[!+[]+!+[]]]]+(!![]+[])[+[[+[]]]]+(!![]+[])[+[[!+[]+!+[]+!+[]]]]+(!![]+[])[+[[+!+[]]]]]+[])[+[[+!+[]]]+[[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]]])()

在线解密链接: http://www.jsfuck.com/

UTC, UT, GMT, 时区 和 时间戳

UTC, UT 和 GMT

  • 格林尼治平时(Greenwich Mean Time),简称 GMT

  • 世界时(Universal Time),简称 UT

  • 协调世界时(Coordinated Universal Time),简称 UTC

UTC, UT, GMT 三者出现的先后顺序: GMT -> UT -> UTC,我们可以把 GMT 和 UTC 看作一样的。

GMT 和 UTC 都是用 数来计算的。

时区

  • 全球划分为 24 个时区

  • 时区可以用 UTC 表示,范围为 UTC-12 --- UTC-11 --- UTC-1 --- UTC --- UTC-1 --- UTC+11 --- UTC+12,依次增加

  • 相差多少个时区,就是相差多少个小时

  • 时区可以按 区域/位置 的格式表示,就可以得到一个独有的名称(时区有很多种表示方式)

以 **时区 为例。

  • China Standard Time (CST), UTC +8、

  • 无夏令时,全年 UTC 偏移量不变

  • **的 IANA 时区标识符为 Asia/Shanghai。

时间戳

  • 时间戳(Timestamp)是指字符串或编码信息用于辨识记录下来的时间日期。国际标准为 ISO 8601

  • 时间戳是从 协调世界时(UTC) 1970 年 1 月 1 日 0 时 0 分 0 秒 起至现在的总秒数,不考虑闰秒

  • 时间戳是用无符号整数表示

  • 时间戳 和 UTC 进行转换,UTC 和 时区 进行转换,因此 时间戳 也可以和 UTC 进行转换

  • 有 秒级(10 位), 毫秒级(13 位) 的时间戳,也有纳米级(19 位) 的时间戳,如 Java 就可以处理到纳米级

本地时间是指当前操作系统设定的时区。例如北京时区是东8区,则本地时间:

2015-04-19 12:20:00

实际上就是UTC+8:00时区的时间:

2015-04-19 12:20:00 UTC+8:00

而此刻的格林威治标准时间与北京时间差了8小时,也就是UTC+0:00时区的时间应该是:

2015-04-19 04:20:00 UTC+0:00

原文链接:https://blog.csdn.net/zheng_lan_fang/article/details/79448965

  • 因为时区的关系,由于没设置使用者的时区,所以就会看到在一些系统上会出现时间慢了 8 个小时的现象(以北京时区为例)

  • 不同的编程语言可以通过设置的方式来调整时区

  • 不同的编程语言 格式化 时间的方式也不同(如:yyyy-mm-dd hh:mm:ss),为确保统一,我们可以使用时间戳(mysql,Java,JavaScript 存放时间都用 时间戳,就不会产生写入和读出不一致了。

参考链接

Leetcode | 位1的个数

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3

进阶:
如果多次调用这个函数,你将如何优化你的算法?

java

二进制中 1 的个数,首先会联想到 位运算。

  • & 运算:x & y 当且仅当 xy 都为 1 的时候,结果才为 1,其余结果为 0
  • | 运算: x | y 当且仅当 xy 都为 0 的时候,结果才为 0,其他结果都为 1

这道题目我们可以用 与运算 &,和这个数的每一位作比较,有 1 的位累加起来即可。

Java 要看作无符号,所以在进行位运算的时候,要使用无符号的左移(<<<)或者右移(>>>)。

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count = 0;
        while (n != 0) {
            count += (n & 1);
            n >>>= 1;
        }
        return count;
    }
}

SIM800C

在某宝上买了 SIM800C 开发板(店家记得给我广告费),这样子就不需要自己焊板子了,233

里面店铺还给了相关的资料,动手难度不大。https://pan.baidu.com/s/1b01Wrc

还需要一张卡,买那种 物联卡 就行了,卡放在模块里面前,先要插入手机,激活后才可以使用。注意模块 SIM 插槽的方向,有坑。

USB-TTL 调试

需要 USB-TTL,连线如下

SIM800C - USB-TTL

GND - GND

VCC - 5.0V (3.3V 也是可以的,没什么问题)

R - RXD

T - TXD

SSCOM 软件波特率选择 9600,记得勾选回车换行。

虽然说 SIM800C 自动识别波特率,但是发现选择其他波特率的时候不够稳定。若通过 Arduino Uno 连接,使用 115200 波特率不够稳定,发送 AT 指令后,模块返回数值时会出现乱码,因此才选择使用 9600。

在提供的资料里面,算是很全面了。包括 AT 指令。使用 SIM800C 模块进行 TCP/IP 连接服务器。相关指令。具体参考资料里面的文档 SIM800系列_TCPIP_应用文档_V1.01.pdf

AT+CPIN?
AT+CSQ
AT+CREG?
AT+CGATT?
AT+CSTT="CMNET"
AT+CIICR
AT+CIFSR
AT+CIPSTART="TCP","test.com","8500"
AT+CIPSEND

连接 Arduino Uno

连接方式,参考链接:http://www.ayomaonline.com/programming/quickstart-sim800-sim800l-with-arduino/

  • SIM800 5v/4v ↔ Arduino 5v
  • SIM800 GND (either one) ↔ Arduino GND
  • SIM800 SIM_TXD ↔ Arduino D8 (read through for the reason)
  • SIM800 SIM_RXD ↔ Arduino D7 (read through for the reason)

代码

#include <SoftwareSerial.h>
 
//SIM800 TX is connected to Arduino D8
#define SIM800_TX_PIN 8
 
//SIM800 RX is connected to Arduino D7
#define SIM800_RX_PIN 7
 
//Create software serial object to communicate with SIM800
SoftwareSerial serialSIM800(SIM800_TX_PIN,SIM800_RX_PIN);
 
void setup() {
  //Begin serial comunication with Arduino and Arduino IDE (Serial Monitor)
  Serial.begin(9600);
  while(!Serial);
   
  //Being serial communication witj Arduino and SIM800
  serialSIM800.begin(9600);
  delay(1000);
   
  Serial.println("Setup Complete!");
}
 
void loop() {
  //Read SIM800 output (if available) and print it in Arduino IDE Serial Monitor
  if(serialSIM800.available()){
    Serial.write(serialSIM800.read());
  }
  //Read Arduino IDE Serial Monitor inputs (if available) and send them to SIM800
  if(Serial.available()){    
    serialSIM800.write(Serial.read());
  }
}

SIM800C AT 指令集成库

在网上找了这个这个硬件的封装库,发现只找到和他类似的,然后就下载来用,后来出了一些问题,看了下实现的源码,发现有些和我使用不符,而且实现起来不是很难,因此自己就写了一个,只写了 HTTP 发送数据。

附上链接 https://github.com/techial1042/SIM800C_Arduino

Leetcode | 连续出现的数字

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

+----+-----+
| Id | Num |
+----+-----+
| 1  |  1  |
| 2  |  1  |
| 3  |  1  |
| 4  |  2  |
| 5  |  1  |
| 6  |  2  |
| 7  |  2  |
+----+-----+

例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。

+-----------------+
| ConsecutiveNums |
+-----------------+
| 1               |
+-----------------+

使用自连接,利用别名,将同一张表使用三次

连续至少出现三次,那也是就 id 号得相差 1,并且 num 的值得相等。

再利用 distinct 去重即可。

mysql

select distinct a.Num ConsecutiveNums from

Logs a 

inner join Logs b on a.id = b.id - 1

inner join Logs c on b.id = c.id - 1

where a.Num  = b.Num and b.Num  = c.Num;

selenium python 环境搭建

环境

  • Windows 环境

Python 安装

  • 官网直接下即可,建议安装 anaconda ,里面带了 selenium

selenium 安装

  1. pip 安装 selenium: pip install selenium (如果用了 anaconda,会提示已经存在

  2. 根据自己的使用的浏览器选择对应的驱动(我用的是 Chrome

  3. 打开 Chrome — 设置 — 关于Chrome,找到 Chrome 的版本号

    1. 版本 69.0.3497.23(正式版本) (64 位) ,之前用的是 70.+ 版本,驱动没支持 70.0 版本以上只能回退回来。
  4. http://chromedriver.chromium.org/downloads Chrome 版本对应的驱动号

    ChromeDriver 2.40
    
    Supports Chrome v66-68
  5. https://chromedriver.storage.googleapis.com/index.html 找到自己电脑对应 Chrome 版本驱动和平台下载到本地,解压。

  6. 将 Chromedriver 的目录添加到 环境变量 。如果不想配置环境变量的话写代码就得添加 Chromedriver 的路径。

Chrome 安装 Katalon Recorder 插件

测试

from selenium.webdriver import Chrome,ChromeOptions

driver_path = r"D://chromedriver.exe"  # Chromedriver 路径
Options = ChromeOptions()

# 浏览器路径,如果有 Chrome 安装的话, 会自动查找,绿色版会有问题
# 如果有多个版本的 Chrome,可以指定对应的版本
Options.binary_location = r"Chrome.exe" # 浏览器路径
driver = Chrome(chrome_options=Options, executable_path = driver_path, port=9515) 

driver.get( "https://www.baidu.com")   #打开百度

GreenChrome

  • 我用的是 GreenChrome,绿色版的 Chrome 方便携带,如果知道的是 MyChrome.exe 的路径的话,虽然可以调用起 Chrome,但还是有问题

    $ ls
     Chrome/   MyChrome.exe*   MyChrome.ini   MyChrome.vbs  'User Data'/
    • 列表如上,进入 Chrome 文件夹中,找到 Chrome.exe,将浏览器的路径换成这个即可正常运行,虽然有 error 错误提示,但是不影响使用。

参考链接

spring boot 配置 redis

redis maven 依赖配置

如果只是单纯的使用 redis 来当缓存,用缓存注解的话,就不需要导入以下这个依赖。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>RELEASE</version>
        </dependency>

application.yml 配置

根据自己的需要进行配置即可,spring boot 里面都有默认配置。

不全部列举。

spring:
  redis:
    database: 0     # 选择 redis 数据库
    host: localhost # ip
    port: 6379      # 端口
    timeout: 0      # 超时时间(毫秒)
    jedis:
      pool:
        min-idle: 0 # 最小空闲连接
        max-idle: 8 # 最大空闲连接

之后在 main 方法的那个类上,加入 @EnableCaching 注解。

每个要想写入 redis 的类都需要 实现(implements) Serializable 这个接口。 不然会报错。

spring boot 缓存注解

  • @CacheConfig 一般放在类上。
  • @Cacheable 放在方法上,只缓存一次,之后只返回 缓存 里面的数据,不会刷新。
  • @CachePut 放在方法上,都会调用函数。也是会刷新,将刷新的值放入 缓存。
  • @CacheEvict 放在方法上,从缓存中移除数据。

cache 中 key属性的 root 对象。

属性 描述 示例
methodName 当前方法名 #root.methodName
method 当前方法 #root.method.name
target 当前被调用的对象 #root.target
targetClass 当前被调用的对象的class #root.targetClass
args 当前方法参数数组 #root.args[0]
caches 当前被调用的方法使用的Cache #root.caches[0].name

参考链接

CTF | 告诉你一个秘密

挺奇葩的一道题目。

flag格式:zsctf{}

636A56355279427363446C4A49454A7154534230526D6843 56445A31614342354E326C4B4946467A5769426961453067

先对这一段进行了单词出现频率统计,发现范围在 0 - 9,A - E,发现刚好符合 16 进制,而且长度为 48。

分析代码如下

word = {}
string = '636A56355279427363446C4A49454A7154534230526D684356445A31614342354E326C4B4946467A5769426961453067'
for i in string:
        if i not in word:
                word[i] = 1
        else:
                word[i] = word[i] + 1

print(word)
print(sorted(word.keys()))
print(word.keys())
print(word.values())

输出结果

{'6': 16, '3': 12, 'A': 5, '5': 12, '2': 7, '7': 6, '9': 5, '4': 21, 'C': 2, '1': 4, '0': 2, 'D': 1, '8': 1, 'E': 1, 'B': 1}
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E']
dict_keys(['6', '3', 'A', '5', '2', '7', '9', '4', 'C', '1', '0', 'D', '8', 'E', 'B'])
dict_values([16, 12, 5, 12, 7, 6, 5, 21, 2, 4, 2, 1, 1, 1, 1])

那么把这个字符串按每两位,拆成 16 进制。(直接用正则

h = b'\x63\x6A\x56\x35\x52\x79\x42\x73\x63\x44\x6C\x4A\x49\x45\x4A\x71\x54\x53\x42\x30\x52\x6D\x68\x43\x56\x44\x5A\x31\x61\x43\x42\x35\x4E\x32\x6C\x4B\x49\x46\x46\x7A\x57\x69\x42\x69\x61\x45\x30\x67'
print(h.decode('utf-8'))
print(h.decode('utf-8').__len__())
print(base64.b64decode(h.decode('utf-8')))

输出结果

cjV5RyBscDlJIEJqTSB0RmhCVDZ1aCB5N2lKIFFzWiBiaE0g
48
b'r5yG lp9I BjM tFhBT6uh y7iJ QsZ bhM '

先把十六进制变成 utf-8 格式,再用 base64 进行解密,得到了 b'r5yG lp9I BjM tFhBT6uh y7iJ QsZ bhM ' ,后来以为是 qwe 加密,发现不是。

我们直接观察键盘,发现每一小段字符串,标记在键盘上,会发现围成一圈,中间刚好有一个字母。将所有的字母合并起来。

不知道大小写,都提交一遍,小写成功。

正确答案:zsctf{tongyuan}

Leetcode | 换座位

小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。

其中纵列的 id 是连续递增的

小美想改变相邻俩学生的座位。

你能不能帮她写一个 SQL query 来输出小美想要的结果呢?

示例:

+---------+---------+
|    id   | student |
+---------+---------+
|    1    | Abbot   |
|    2    | Doris   |
|    3    | Emerson |
|    4    | Green   |
|    5    | Jeames  |
+---------+---------+

假如数据输入的是上表,则输出结果如下:

+---------+---------+
|    id   | student |
+---------+---------+
|    1    | Doris   |
|    2    | Abbot   |
|    3    | Green   |
|    4    | Emerson |
|    5    | Jeames  |
+---------+---------+

注意:

如果学生人数是奇数,则不需要改变最后一个同学的座位。

mysql

sql 里面有 if 函数,if 的使用方法如下,和 编程语言的 if...else... 差不多。

if(expr1, expr2, expr3),当 exp1 的值为 true 时,运行的 exp2,否则运行 exp3。

这题就逻辑题,用伪代码表达如下。

id = function(id) {
	if (id % 2 == 1) { // 如果 id 不是 2 的倍数,也就是奇数
		if (id == count(*)) { // 如果是最后一行, 就还是自己
			return id;
		} else { // 第一个条件满足,第二个不满足时和下一个交换
			return id + 1;
		}
	} else { // id 为偶数,和上一个交换
		return id - 1;
	}
	
};

以下为提交代码

select if (id % 2 = 1, if (id = (select count(*) from seat), id, id + 1), id - 1) id, student 

from seat order by id;

Leetcode | 两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

c++

利用哈希,先判断相减后的值是否存在,不存在的话将该数组中的值作为主键,存入哈希中,之后匹配。

查找成功则返回。

若找不到索引,find() 的返回值为 .end()。

class Solution {
	public:
		vector<int> twoSum(vector<int>& nums, int target) {
			map<int, int> hash_map;
			vector<int> re;
			for (size_t i = 0; i < nums.size(); i++) {
				int tmp = target - nums[i];
				if (hash_map.find(tmp) != hash_map.end()) {
					re.push_back(hash_map[target - nums[i]]);
					re.push_back(i);
					break;
				}
				hash_map[nums[i]] = i;
			}
			return re;
		}
};

Java

public class Solution {
	public int[] twoSum(int []nums, int target) {
		Map<Integer, Integer> map = new HashMap<>();
		for (int i = 0; i < nums.length; i++) {
			Integer tmp = target - nums[i];
			if (map.containsKey(tmp)) {
				return new int[]{map.get(tmp), i};
			}
			map.put(nums[i], i);
		}
		return new int[]{0};
	}
}

SQL | 约束条件

主键约束

  • primary key

检查约束

  • check
  • MySQL 里面的 检测约束 好像有问题,最好用触发器替代。
  • (触发器也别用得太频繁,会对性能产生影响

外键约束

  • foreig key
  • 又称完整性约束
  • 两张表,一张是父表,另一张是子表
  • 得先创建父表
  • 父表得先有这个数据,子表才能有,父表没有的数据,子表不能存在
  • 在子表中创建外键,指向父表
  • 外键可以指向自己本身

若在表 A 中的某一列设置了外键,在表 A 的这一列中的数据必须来自你设置的外键的来源(这个来源可以是本表,或者是其他表),不然无法设置成功。

MySQL 外键约束类型

类型 作用
RESTRICT 默认类型,不指定时默认使用这种类型,等同于 NO ACTION
CASCADE 跟随外键改动
SET NULL 设为空值
NO ACTION 无动作

唯一约束

  • 这一列的值必须唯一
  • 允许有空值,但是一旦有值必须唯一

Java 元注解和自定义注解

Java 自定义注解

使用 @interface 就可以自定义注解,Java 也给我们提供了一些元注解。

在 spring boot 中可以利用 aop (面向侧面的程序设计 aspect-oriented programming)简化处理自定义注解。spring boot 使用 aop 需要导入如下依赖。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

自定义注解使用如下

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {}

Java 元注解

  • @Target
  • @Retention
  • @Documented
  • @Inherited

元注解作用在其他注解上时,不需要每个都使用。

@target

标识注解的使用范围,多个范围用花括号括起来即可。

使用:@Target({ElementType.PARAMETER, ElementType.METHOD })

类型 范围
ElementType.TYPE 接口、类、枚举、注解
ElementType.FIELD 字段、枚举的常量
ElementType.METHOD 方法
ElementType.PARAMETER 方法参数
ElementType.CONSTRUCTOR 构造函数
ElementType.LOCAL_VARIABLE 局部变量
ElementType.ANNOTATION_TYPE 注解
ElementType.PACKAGE

@document

使用了该注解,就会包含在 Javadoc 中。

@inherited

使用了该注解,子类可以继承父类的注解。

一般指的是 extents,接口不算继承,只能说是实现(implement)。

@retention

注解的保留策略,只能选一。

使用: @Retention(RetentionPolicy.SOURCE)

类型 范围
RetentionPolicy.SOURCE 只作用在 代码 中,字节码(Class)中不存在
RetentionPolicy.CLASS default,@retention 默认策略,只在字节码中存在
RetentionPolicy.RUNTIME 字节码中存在,运行的时候可以用 反射 得到

参考链接

Leetcode | 上升的温度

给定一个 Weather 表,编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 Id。

+---------+------------------+------------------+
| Id(INT) | RecordDate(DATE) | Temperature(INT) |
+---------+------------------+------------------+
|       1 |       2015-01-01 |               10 |
|       2 |       2015-01-02 |               25 |
|       3 |       2015-01-03 |               20 |
|       4 |       2015-01-04 |               30 |
+---------+------------------+------------------+

例如,根据上述给定的 Weather 表格,返回如下 Id:

+----+
| Id |
+----+
|  2 |
|  4 |
+----+

mysql

题目不是很难,就是有一个函数需要了解一下,TO_DAYS 给定一个日期 date,返回一个天数(年份从0开始的天数)。第一次直接拿两个值进行比较,挂了一次。和日期有关的值,记得使用 TO_DAYS。

select b.Id from
Weather a inner join Weather b
on TO_DAYS(a.RecordDate) = TO_DAYS(b.RecordDate) - 1
where b.Temperature > a.Temperature

RESTful API 学习心得

最近将之前看的 RESTful API 设计指南翻出来重新看了一下,根据这阵子做的项目,总结一下。

路径

  • 每一个网址代表一种 资源
    • https://demo.com/api/users 代表所有用户这个群体,你可以对 /users 进行一系列操作。
  • 网址都用 名词,不用动词。
  • 一种 资源 一般是一个集合,所以最好用 复数。

操作

数据库和 HTTP 请求对应如下

数据库 HTTP 动词
增 (insert) POST
删 (delete) DELETE
改 (update) 修改分为 全量 更新,和 局部 更新。
如果全部更新,用 PUT,如果是局部更新,用 PATHCH
查 (select) GET

https://demo.com/api/users 为例。

link HTTP 动词 效果
/api/users GET 列出 user 的信息
/api/users POST 添加 user 信息
/api/users/id DELETE 删除某个 user
/api/users/id PUT 更新该 user 的全部信息
/api/users/id PATCH 更新该 user 的部分信息

状态码

状态码 信息
200 成功
401 权限不足,无权限,可用在登录时密码错误
403 没有权限访问,和 401 有点区别
管理员权限可观看所有用户信息,当普通用户权限请求这个接口,就可以返回 403 状态码
404 资源未找到,没登录状态时,应该显示 404,而不使用 401 或 403
500 服务器内部错误

C# DateTime

最近从 redis 数据库中获得一些数据,数据非主要部分已做处理。数据如下:

[{
	"Path": "MTBA",
	"Timestamp": "636948697749377910",
	"Current": "27.384489640391596"
}, {
	"Path": "temperature",
	"Timestamp": "636948697738137200",
	"Current": "27.899760796418597"
}]

有点让我好奇的是 Timestamp 字段的数据值,咋一看,不是时间戳,也有点不太确定是不是,之前写过一篇关于时间戳的文章 #52

难道是纳米级的时间戳么?把当前时间转换成纳米级时间戳,发现对应不上;利用 JS 计算一下长度,排除了是时间戳,压根没对应。

> "636948697749377910".length
< 18

上谷歌搜了一下关键词 十八位 时间戳,2333,找到了线索,原来是和 C# 的 DateTime 有关。

DateTime值类型表示日期和时间的值范围从 00:00:00 (午夜),1 月 1 日 0001 午夜 11:59:59 止 (基督纪元) 通过 11:59:59 PM,到公元 9999 年 12 月 31 日 (C.E.)在公历。

那怎么把 DateTime.Ticks 转成 时间戳 或者 当前时区的时间呢?后来发现 JS 就可以做到了。一句就可以搞定。

> Date(636948697749377910)
< "Sat Jun 01 2019 00:11:38 GMT+0800 (China Standard Time)"

在找这个问题的过程中还发现了一个有趣的问题,日本立法年号改变了(平成变成了令和)。那岂不是会影响日历的程序。23333

参考链接

Leetcode | 超过经理收入的员工

Employee 表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

+----+-------+--------+-----------+
| Id | Name  | Salary | ManagerId |
+----+-------+--------+-----------+
| 1  | Joe   | 70000  | 3         |
| 2  | Henry | 80000  | 4         |
| 3  | Sam   | 60000  | NULL      |
| 4  | Max   | 90000  | NULL      |
+----+-------+--------+-----------+

给定 Employee 表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

+----------+
| Employee |
+----------+
| Joe      |
+----------+

利用内连接,若该员工为经理,则他的 ManagerId 为 null,

当 员工的 ManagerId = 经理的 Id 时,说明为员工,

Managerld 为 null,内连接不会成立。

连接成功后,剩下的都是有经理的员工,只需要判断他的薪水高于经理的薪水即可。

使用 > 或者 >= 都可以通过。

mysql

select a.Name Employee

from Employee a inner join Employee b

on a.ManagerId = b.Id

and a.Salary > b.Salary

Python | str bytes 进制区别和互转

题外话

pycharm 重构常用快捷键

快捷键 用途
CTRL + ALT + M 将选取的代码提取成函数
CTRL + ALT + V 将选取的代码段赋值给一个变量

字符串和 bytes 区别

bytes str
序列 字节序列,字节没有编码这一说法,和进制更没有任何关系 文本序列,因此存在编码
编码 无编码 py3 默认是 utf-8,其他编码如 gbk,ascii ...
标记 b'' 包含 '' 或者 "" 包含
转换 bytes.decode() 转换成 str str.encode() 转换成 bytes
修改 不可以被修改,会抛异常 可修改,有很多方法,如 replace() ...

注意事项

  • bytes.decode() 可能会出错,因为 utf-8 等编码可能无法解码一些字节
  • 无法解码的时候,可以选择直接 print(bytes) 可以显示正常解码的部分
  • 十六进制,十进制,二进制等等进制和 strbytes 没有任何关系,不过你可以利用已有的方法将他们进行转换

str 转 十六进制

利用 binascii 库,可以将 str 转为 十六进制,不过是保存在 bytes 中,我们如果要处理需要将 bytes 转为 str。

测试代码如下

if __name__ == '__main__':
	test = 'admin'
	print(type(test))
	x = binascii.b2a_hex(test.encode('ascii'))
	print(type(x))
	print(x)
	string = x.decode()
	print(type(string))
	print(string)
	result = re.sub("(..)", r"0x\1 ", string, 0, re.MULTILINE)
	print(result)

运行结果

<class 'str'>
<class 'bytes'>
b'61646d696e'
<class 'str'>
61646d696e
0x61 0x64 0x6d 0x69 0x6e 

十六进制 转 str

将形如 0x00\x00 的字符串编码转换成 str

python 中的 十六进制 是 \x00, 使用之前得想转成 \x00 格式。

def to_bytes(data: str) -> str:
	data = data.replace('0x', '\\x')
	return str.encode(data, encoding='utf-8').decode()


if __name__ == '__main__':
	data = '\x4D\x51\x54\x54'
	print(to_bytes(data=data))

str 二进制 互转

def encode(s):
    return ' '.join([bin(ord(c)).replace('0b', '') for c in s])

def decode(s):
    return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])
    
>>>encode('hello')
'1101000 1100101 1101100 1101100 1101111'
>>>decode('1101000 1100101 1101100 1101100 1101111')
'hello'

代码出处 https://segmentfault.com/q/1010000007305401

进制互转

python 中 十六进制 是以 0x \x 开头

八进制是以 0o 开头

二进制是以 0b 开头

十进制转十六进制,二进制,八进制

>>> hex(1024)
'0x400'
>>> bin(1024)
'0b10000000000'
>>> oct(1024)
'0o2000'

十六进制转十进制,八进制,二进制

>>> int('0x1024', 16)
4132
>>> bin(int('0x1024', 16))
'0b1000000100100'
>>> oct(0x1024)
'0o10044'

八进制转十进制,十六进制,二进制

>>> int('1024', 8)
532
>>> hex(int('1024', 8))
'0x214'
>>> bin(int('1024', 8))
'0b1000010100'

二进制转十进制,八进制,十六进制

>>> int('1010', 2)
10
>>> oct(0b1010)
'0o12'
>>> hex(int('1010', 2))
'0xa'

我觉得看一下知乎上关于 Python 编码的讨论。https://www.zhihu.com/question/31833164

输入输出练习

题库链接

http://acm.two.moe:808/JudgeOnline/

Linux 内核代码风格(供参考

https://www.kernel.org/doc/html/v4.13/translations/zh_CN/coding-style.html

1092: 输入输出练习之A+B(1)

1092: 输入输出练习之A+B(1)
题目描述
计算a+b

输入
若干组测试数据,每组测试数据占一行,包含2个整数a和b,空格隔开。

输出
对于每组测试数据,输出占一行,结果为a+b的和。

样例输入
1 5
10 20
样例输出
6
30

参考代码

#include <stdio.h>
int main(void)
{
	int a, b;
	while (~scanf("%d %d", &a, &b)) {
		printf("%d\n", a + b);
	}
	return 0;
}

//#include <stdio.h>
//int main(void)
//{
//	int a, b;
//	while (scanf("%d %d", &a, &b) != EOF) {
//		printf("%d\n", a + b);
//	}
//	return 0;
//}

1093: 输入输出练习之A+B(2)

1093: 输入输出练习之A+B(2)
题目描述
计算a+b

输入
第一行为一个整数n,接下来有n行。每行包含2个整数a和b,空格隔开。

输出
对于每对a和b,计算它们的和,并占一行输出。

样例输入
2
1 5
10 20
样例输出
6
30

参考代码

#include <stdio.h>
int main(void)
{
  	int t;
  	scanf("%d", &t);
  	while (t--) {
		int a, b;
		scanf("%d %d", &a, &b);
		printf("%d\n", a + b);
    }
	return 0;
}

1094: 输入输出练习之A+B(3)

1094: 输入输出练习之A+B(3)
题目描述
计算a+b

输入
输入包含多组测试用例。每组用例占一行,包含2个整数a和b。最后一组用例为0 0,表示输入结束,该用例不需要处理。

输出
对于每组输入用例,输入a+b的和,占一行。

样例输入
1 5
10 20
0 0
样例输出
6
30

参考代码

#include <stdio.h>
int main(void)
{
	int a, b;
	while (~scanf("%d %d", &a, &b) ) {
      	if (a == 0 && b == 0) {
        	break;
      	}
		printf("%d\n", a + b);
	}
	return 0;
}

//#include <stdio.h>
//int main(void)
//{
//#ifdef _DEBUG
//	freopen("in.txt", "r", stdin);
//	//freopen("out.txt", "w", stdout);
//#endif // _DEBUG
//	int a, b;
//	while (~scanf("%d %d", &a, &b) && (a || b)) {
//		printf("%d\n", a + b);
//	}
//	return 0;
//}

1095: 输入输出练习之A+B(4)

1095: 输入输出练习之A+B(4)
题目描述
计算整数的和。

输入
输入包含多组测试用例。每组用例占一行,每行第一个整数为n,接下来有n个整数。以0开头的用例表示输入结束,该组用例不需要处理。

输出
对于每组测试用例,计算该用例中n个整数的和,占一行输出。

样例输入
4 1 2 3 4
5 1 2 3 4 5
0 
样例输出
10
15

参考代码

#include <stdio.h>
int main(void)
{
    int t;
    while (scanf("%d", &t) && t) { 
        int n;
        int sum = 0;
        for (int i = 0; i < t; i++) {
            scanf("%d", &n);
            sum += n;
            if (sum == 0) {
                break;
            }
        }
        printf("%d\n", sum);
    }
    return 0;
}

1096: 输入输出练习之A+B(5)

1096: 输入输出练习之A+B(5)
题目描述
计算整数的和。

输入
第一行为一个整数n,接下来有n行。每一行第一个整数为m,该行接下来有m个整数。

输出
对于每组测试用例,计算m个整数的和,输出占一行。

样例输入
2
4 1 2 3 4
5 1 2 3 4 5
样例输出
10
15

参考代码

#include <stdio.h>
int main(void)
{
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		scanf("%d", &n);
		int x;
		int num = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d", &x);
			num += x;
		}
		printf("%d\n", num);
	}
	return 0;
}

1097: 输入输出练习之A+B(6)

1097: 输入输出练习之A+B(6)
题目描述
计算整数的和。

输入
输入包含多组测试用例,每组用例占一行。每组用例第一个整数为n,接下来该行有n个整数。

输出
对于每组用例,计算n个整数的和,占一行输出。

样例输入
4 1 2 3 4
5 1 2 3 4 5
样例输出
10
15

参考代码

#include <stdio.h>
int main(void)
{
	int n;
	while (~scanf("%d", &n)) {
		int x;
		int num = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d", &x);
			num += x;
		}
		printf("%d\n", num);
	}
	return 0;
}

1098: 输入输出练习之A+B(7)

1098: 输入输出练习之A+B(7)
题目描述
计算a+b

输入
输入包含多组数据,每组占一行。每一行有2个整数a和b,空格隔开。

输出
对于每组数据,计算a+b的和,然后输出一个空行。

样例输入
1 5
10 20
样例输出
6

30

参考代码

#include <stdio.h>
int main(void)
{
	int a, b;
	while (~scanf("%d %d", &a, &b)) {
		printf("%d\n\n", a + b);
	}
	return 0;
}

1099: 输入输出练习之A+B(8)

1099: 输入输出练习之A+B(8)
题目描述
计算整数的和。

输入
输入第一行为一个整数n,接下来有n行。每一行开头有一个整数m,接下来有m个整数。

输出
对于每组数据,输出m个整数的和。同时每组输出之间有一个空行。

样例输入
3
4 1 2 3 4
5 1 2 3 4 5
3 1 2 3
样例输出
10

15

6

参考代码

#include <stdio.h>
int main(void)
{
#ifdef _DEBUG
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif // _DEBUG

	int n;
	scanf("%d", &n);
	int falg = 0;
	while (n--) {
		int t;
		scanf("%d", &t);
		int x;
		int sum = 0;
		for (int i = 0; i < t; i++) {
			scanf("%d", &x);
			sum += x;
		}
		printf("%d%s", sum, n == 0 ? "" : "\n\n");
	}
	return 0;
}

hibernate 命名策略

骄傲的使用 谷歌翻译

原文链接 https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/domain/naming.html

不想看原文的话,最后我会说下的理解。

hibernate 命名策略

对象模型到关系数据库的映射的一部分是将对象模型中的名称映射到相应的数据库名称。 Hibernate 将此视为两阶段过程:

  • 第一阶段是从域模型映射中确定正确的逻辑名称。逻辑名可以由用户显式指定(使用 @Column@Table),也可以由 Hibernate 通过 ImplicitNamingStrategy 隐式确定。
  • 其次是将此逻辑名称解析为 PhysicalNamingStrategy 定义的物理名称。

过去的命名策略

之前 Hibernate 只定义了一个 org.hibernate.cfg.NamingStrategy 。这个单一的 NamingStrategy 现在已经拆成ImplicitNamingStrategyPhysicalNamingStrategy

此外,NamingStrategy 通常不够灵活,无法正确应用给定的命名“规则”,因为 API 缺乏可供决定的信息,或者因为 API 实际上没有得到很好的定义。

由于这些限制,org.hibernate.cfg.NamingStrategy 已被弃用,然后被删除,转而使用 ImplicitNamingStrategyPhysicalNamingStrategy

每种命名策略背后的想法是最小化数量,开发人员必须提供的重复信息,用于映射域模型。

JPA 兼容性

JPA 定义了关于隐式逻辑名称确定的固有规则。如果 JPA 提供程序可移植性是一个主要问题,或者您真的喜欢 JPA 定义的隐式命名规则,请务必坚持使用 ImplicitNamingStrategyJpaCompliantImpl(默认值)

此外,JPA 定义逻辑名称和物理名称之间没有分离。遵循 JPA 规范,逻辑名称是物理名称。如果 JPA 提供程序可移植性很重要,应用程序应该不要指定 PhysicalNamingStrategy

ImplicitNamingStrategy

当实体没有显式命名它映射到的数据库表时,我们需要隐式确定该表名。或者,当某个特定属性没有显式命名它映射到的数据库列时,我们需要隐式确定该列名。当映射未提供显式名称时, org.hibernate.boot.model.naming.ImplicitNamingStrategy 用于确定逻辑名称。

Hibernate定义了多个 ImplicitNamingStrategy 实现。应用程序也可以免费插入自定义实现。

有多种方法可以指定要使用的 ImplicitNamingStrategy 。首先,应用程序可以使用 hibernate.implicit_naming_strategy 配置设置来指定实现,该设置接受:

  • 开箱即用预先定义的 “短名称”
    • 默认
      • org.hibernate.boot.model.name.ImplicitNamingStrategyJpaCompliantImpl jpa 的别名
    • jpa
      • org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl 符合 JPA 2.0 标准的命名策略
    • 旧式 HBM
      • org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl 符合原始的 Hibernate 命名策略
    • 旧式 JPA
      • org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl 符合为 JPA 1.0开发的旧版命名策略,遗憾的是,在很多方面,隐式命名规则都不清楚
    • 组件的路径
      • org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl 主要遵循ImplicitNamingStrategyJpaCompliantImpl 规则,除了它使用完整的复合路径,而不仅仅是结束属性部分
  • 实现 org.hibernate.boot.model.naming.ImplicitNamingStrategy 类的引用
  • 实现 org.hibernate.boot.model.naming.ImplicitNamingStrategy 的类的 FQN

其次,应用程序和集成可以利用 org.hibernate.boot.MetadataBuilder#applyImplicitNamingStrategy 来指定要使用的 ImplicitNamingStrategy。有关引导的其他详细信息,请参阅 Bootstrap

PhysicalNamingStrategy

许多组织围绕数据库对象(表,列,外键等)的命名定义规则。 PhysicalNamingStrategy 的想法是帮助实现这样的命名规则,而不必通过显式名称将它们硬编码到映射中。

虽然 ImplicitNamingStrategy 的目的是确定名称为 accountNumber 的属性在未明确指定时映射到 accountNumber 的逻辑列名称,PhysicalNamingStrategy 的目的是,例如,假设物理列名称应缩写为 acct_num

确实,在这种情况下,acct_num 的解析可以在 ImplicitNamingStrategy 中处理。但重点是分离关注点。无论属性是否明确指定列名,或者我们是否隐式确定,都将应用 PhysicalNamingStrategy 。仅当未给出显式名称时,才会应用ImplicitNamingStrategy 。所以它取决于需求和意图。

默认实现是简单地使用逻辑名称作为物理名称。但是,应用程序和集成可以定义此 PhysicalNamingStrategy 合同的自定义实现。以下是一个名为 Acme Corp 的虚构公司的 PhysicalNamingStrategy 示例,其命名标准是:

  • 喜欢用下划线分隔的单词而不是驼峰法
  • 用标准缩写替换某些单词

示例1. PhysicalNamingStrategy 实现示例

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.userguide.naming;

import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import org.apache.commons.lang3.StringUtils;

/**
 * An example PhysicalNamingStrategy that implements database object naming standards
 * for our fictitious company Acme Corp.
 * <p/>
 * In general Acme Corp prefers underscore-delimited words rather than camel casing.
 * <p/>
 * Additionally standards call for the replacement of certain words with abbreviations.
 *
 * @author Steve Ebersole
 */
public class AcmeCorpPhysicalNamingStrategy implements PhysicalNamingStrategy {
	private static final Map<String,String> ABBREVIATIONS = buildAbbreviationMap();

	@Override
	public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		// Acme naming standards do not apply to catalog names
		return name;
	}

	@Override
	public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		// Acme naming standards do not apply to schema names
		return name;
	}

	@Override
	public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		final List<String> parts = splitAndReplace( name.getText() );
		return jdbcEnvironment.getIdentifierHelper().toIdentifier(
				join( parts ),
				name.isQuoted()
		);
	}

	@Override
	public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		final LinkedList<String> parts = splitAndReplace( name.getText() );
		// Acme Corp says all sequences should end with _seq
		if ( !"seq".equalsIgnoreCase( parts.getLast() ) ) {
			parts.add( "seq" );
		}
		return jdbcEnvironment.getIdentifierHelper().toIdentifier(
				join( parts ),
				name.isQuoted()
		);
	}

	@Override
	public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		final List<String> parts = splitAndReplace( name.getText() );
		return jdbcEnvironment.getIdentifierHelper().toIdentifier(
				join( parts ),
				name.isQuoted()
		);
	}

	private static Map<String, String> buildAbbreviationMap() {
		TreeMap<String,String> abbreviationMap = new TreeMap<> ( String.CASE_INSENSITIVE_ORDER );
		abbreviationMap.put( "account", "acct" );
		abbreviationMap.put( "number", "num" );
		return abbreviationMap;
	}

	private LinkedList<String> splitAndReplace(String name) {
		LinkedList<String> result = new LinkedList<>();
		for ( String part : StringUtils.splitByCharacterTypeCamelCase( name ) ) {
			if ( part == null || part.trim().isEmpty() ) {
				// skip null and space
				continue;
			}
			part = applyAbbreviationReplacement( part );
			result.add( part.toLowerCase( Locale.ROOT ) );
		}
		return result;
	}

	private String applyAbbreviationReplacement(String word) {
		if ( ABBREVIATIONS.containsKey( word ) ) {
			return ABBREVIATIONS.get( word );
		}

		return word;
	}

	private String join(List<String> parts) {
		boolean firstPass = true;
		String separator = "";
		StringBuilder joined = new StringBuilder();
		for ( String part : parts ) {
			joined.append( separator ).append( part );
			if ( firstPass ) {
				firstPass = false;
				separator = "_";
			}
		}
		return joined.toString();
	}
}

有多种方法可以指定要使用的 PhysicalNamingStrategy。首先,应用程序可以使用 hibernate.physical_naming_strategy 配置设置指定实现,该设置接受:

  • 实现 org.hibernate.boot.model.naming.PhysicalNamingStrategy 类的引用
  • 实现 org.hibernate.boot.model.naming.PhysicalNamingStrategy 类的FQN

其次,应用程序和集成可以利用 org.hibernate.boot.MetadataBuilder#applyPhysicalNamingStrategy。有关引导的其他详细信息,请参阅Bootstrap。

个人总结

首先也就是 Hibernate 中的 NamingStrategy 已经被 ImplicitNamingStrategyPhysicalNamingStrategy取代。

PhysicalNamingStrategy 有两个属性

  • PhysicalNamingStrategyStandardImpl不做修改,直接映射
  • SpringPhysicalNamingStrategy 首字母小写,大写字母变为下划线加小写

ImplicitNamingStrategy 有四个属性

  • jpa (默认)
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl 符合 JPA 2.0 标准的命名策略
  • 旧式 HBM
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl 符合原始的 Hibernate 命名策略
  • 旧式 JPA
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl 符合为 JPA 1.0开发的旧版命名策略,遗憾的是,在很多方面,隐式命名规则都不清楚
  • 组件的路径
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl 主要遵循ImplicitNamingStrategyJpaCompliantImpl 规则,除了它使用完整的复合路径,而不仅仅是结束属性部分

几个属性的例子可以参考这个链接 https://stackoverflow.com/questions/41267416/hibernate-5-naming-strategy-examples

配置

在 Spring Boot 中的 application.yml 建议做如下配置。

spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl

CTF | as-fast-as-you-can

http://118.89.138.44:10003

  • F12 打开开发者工具,刷新网页,发现返回头 Get-flag
  • base64 进行解码
  • 再进行提交即可得到 flag。
import base64
import requests
if __name__ == '__main__':
	url = 'http://118.89.138.44:10003/index.php'
	r = requests.post(url=url)
	source = base64.b64decode(r.headers['Get-flag'])
	print(source)
	print(r.cookies.get_dict())
	r = requests.post(url=url, data={'zsctf': source},
					  cookies=r.cookies.get_dict())
	print(r.text)

cookies

  • cookies 要是存在了,也就是你设置了,再去请求同一个网站,是不会返回 cookie,因为 cookies 已经存在
  • 如果是第一次访问的话,服务器是会返回一个 cookie。
  • requests.cookies.get_dict() 将请求后得到的 cookies 格式化成一个 dict

Leetcode | 分数排名

编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+

例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):

+-------+------+
| Score | Rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+

原先的想法使用 SQL 里面的变量来做的,然后点执行代码的时候发现过不去,就放弃了这种想法。

使用 count 计数, distinct 去重,比较两个分数,在利用 order by 进行排序即可。

mysql

select s.Score, (select count(distinct s1.Score)

	from Scores s1 where s1.Score >= s.Score) Rank

from Scores s order by s.Score desc;

测试成功后,发现用的时间有点长……

我看了一下其他人的提交,找了执行用时较少的代码看,居然发现他们是使用变量,而且思路更简单。

(其实执行代码那里使用变量测试不通过,但是直接提交一发,就知道是不是正确了。

思路如下

预选设置一个分数,实际生活中是不存在负的分数的,因此选一个负数的值作为预置的分数即可。

rank 的起始为 0,递增即可。

rank 递增的条件:等于上一次的 rank,加上判断分数是否等于上一次的分数。

比较的两个值不相等就是 1,就递增,如果相等,就不递增。

@cur_rank := @cur_rank + (@pre_score != (@pre_score := s.Score))

相当于

@tmp1 = (@pre_score := s.Score);

@tmp2 = @pre_score != tmp; // 值只会是 0 或者 1

@cur_rank = @cur_rank + tmp2;
set @pre_score = -324234;

set @cur_rank = 0;

select s.Score, (@cur_rank := @cur_rank + (@pre_score != (@pre_score := s.Score))) Rank

	from Scores s order by 1 desc;

利用 vscode / vim 写 avr 代码

------------------------- 2018.05.09 update -------------------------------------

强烈安利用 Vim ,再加上配置 Makefile ,写起来更方便,保存直接编译成 hex 文件。

------------------------- 2018.05.09 update -------------------------------------

前言废话

AVR 开发环境很简单,老师让我们使用的是 ICC AVR 这个软件,基于 Mega16

AVR 开发软件链接:AVR开发软件下载

5ab120b7c29f7.png

界面如上

个人是觉得这个界面很简单,对于一个强迫症患者来说,颜值第一,再考虑实用性,可惜这个软件,这两点都达不到。

我觉得写代码也要写的舒适一点,为革命保护视力,给 Visual Studio 换颜色 ,这个是之前看的文章。我觉得一个好的编译器配色方案,可以让人写代码更加舒服。

下面介绍如何用 vscode 写 avr 程序并编译成 hex 文件。

一、配置 ICC AVR 环境变量

先安装好 ICC AVR ,接着找到 ICC AVR 安装路径。打开 bin 文件夹,复制该文件夹的路径。

假设路径为 D:\iccv7avr\bin

其实和配置 Java 和 Python 环境变量差不多。

电脑(右键) - 属性 - 高级系统设置 - 环境变量 - 系统变量 - Path - 新建 - 粘贴 bin 路径 - 保存 - 退出。

打开 cmd ,输入 imakew ,若出现以下信息则说明配置成功。

D:\iccv7avr\bin\imakew.exe: Can't open makefile

二、打开 vscode 写代码并编译

安装 vscode 的过程就省了,官网就可以下载安装即可。

新建一个文件夹,名字任意。

vscode 打开该文件夹。

接着创建三个文档。

  • FIRST.lk
  • FIRST.mak
  • main.c
  1. FIRST.lk 写入如下内容
main.o 
  1. FIRST.mak 写入如下内容,这些参数根据自己的板子自己调整。(其实有一个方法就是在 ICC AVR 软件中创建好一个工程,然后参数什么的调好之后,复制那个后缀名为 .mak 的文件即可)
CC = iccavr
LIB = ilibw
CFLAGS =  -e -D__ICC_VERSION=722 -DATMega16  -l -g -MLongJump -MHasMul -MEnhanced -Wf-const_is_flash -DCONST="" 
ASFLAGS = $(CFLAGS) 
LFLAGS =  -g -e:0x4000 -ucrtatmega.o -bfunc_lit:0x54.0x4000 -dram_end:0x45f -bdata:0x60.0x45f -dhwstk_size:16 -beeprom:0.512 -fihx_coff -S2
FILES = main.o 

FIRST:	$(FILES)
	$(CC) -o FIRST $(LFLAGS) @FIRST.lk   -lcatm128
main.o: D:\iccv7avr\include\iom16v.h D:\iccv7avr\include\macros.h D:\iccv7avr\include\AVRdef.h
main.o:	main.c
	$(CC) -c $(CFLAGS) main.c

D:\iccv7avr\include\iom16v.h D:\iccv7avr\include\macros.h D:\iccv7avr\include\AVRdef.h 这个 include 的路径要修改成安装 avr 的目录。
该文件为什么要这样子配置,前半部分是单片机的一些配置,后半部分类似 Makefile ,有兴趣可以去了解一下。

  1. main.c 写入如下内容
/*
imakew -f FIRST.mak
*/
#include <iom16v.h>
#include <macros.h>

int main(void)
{
	return 0;
}

按下 ctrl + ` 快捷键,打开终端,或者从 vscode 界面下方拉起。

在该文件夹的目录下,输入以下命令 imakew -f FIRST.mak ,即可编译成功。成功编译的截图如下

5ab12704e7720.png

回到项目工程的文件夹,会发现生成了一下其他的文件,其中就包括了后缀名为 hex 的文件。

修改 main.c 文件的内容,保存后使用刚才的编译命令即可编译生成新的文件。其实这个过程和用 Java 写代码,然后用命令行编译 Java 过程类似。

三、链接到 avr 头文件库

为啥用链接头文件库?因为强迫症,头文件下方有波浪线,受不了。

把鼠标放到头文件处,如下图。

edit

点击 Edit "includePath" setting ,弹出配置文件,,找到 name: "Win32" ,如下图。
includepath 的最后面添加 ivvavr 的 include 文件夹,记得配置好了之后记得保存。

假设路径为 E:/7/dpj/include

include

最后一个建议,开启 vscode 的自动保存功能。这样子就不需要每一次写完就 Ctrl + S 。那只要写完之后,在下方的 shell 编译成 hex 即可。(很容易没保存,然后生成的 hex 文件和上一次一样。

开启方法: 文件 - 自动保存

这样子就链接到了 avr 的头文件库了。这样子就可以在 vscode 愉快的写 avr 代码。

WeChat Image_20180321093501.png

vscode and vcc 对比图

Chrome 浏览器重启后就同步暂停

个人猜想:这个问题和 cookie 有关,谷歌账号的 cookie 被清。

方案一

Hello,

I have found this work-around

In the affected Chrome account, open a blank tab and type the following:
chrome://flags/#account-consistency
In the drop-down box on the right, choose "disable."
Now, relaunch the browser.
NOTE:
Chrome Flags are Experimental Features
Please check out Chrome's Flag disclaimer before continuing.
Kindly type the following in Chrome omnibox to view the disclaimer: chrome://flags/
Best,
Ms. Drasby

谷歌论坛看到的,以上为原话

https://productforums.google.com/d/msg/chrome/s3v4yG1uAcY/J7UPRWHfAgAJ

意思也就是打开 chrome://flags/#account-consistency,把高亮的设置改为 disable,重启 Chrome 即可。

方案二

设置里面,把自己的谷歌账号关闭,并 将设置还原为原始模式设置

重启 Chrome,再次登录自己的谷歌账号即可。

方案三

打开 chrome://settings/content/cookies仅将本地数据保留到您退出浏览器为止 取消勾选。重启 Chrome 即可。

Leetcode | 组合两个表

表1: Person

+-------------+---------+
| 列名         | 类型     |
+-------------+---------+
| PersonId    | int     |
| FirstName   | varchar |
| LastName    | varchar |
+-------------+---------+
PersonId 是上表主键

表2: Address

+-------------+---------+
| 列名         | 类型    |
+-------------+---------+
| AddressId   | int     |
| PersonId    | int     |
| City        | varchar |
| State       | varchar |
+-------------+---------+
AddressId 是上表主键

编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:

FirstName, LastName, City, State

这题目主要的一个点为 person 不管是否有地址信息,都要显示出来,其实这句话就有嗅到一点左右连接的味道。一个左连接搞定。

mysql

select FirstName, LastName, City, State

from Person p left join Address a

on p.PersonId = a.PersonId;

使用 Git 踩过的坑

记录一下最近使用 Git 踩过的坑。

将 commit 提交到错误分支

将项目 clone 下来之后就直接在 master 分支上操作,忘记切换到 dev 分支去了,gg,还提交了几个分支。

拯救方法如下。

  1. 记录错误的分支的 hash。
  2. 回滚该错误分支。 git cherry-pick af2279b
  3. 切换到正确的分支。 git checkout dev
  4. 提交分支 git cherry-pick af2279b 6a498ec
  5. git cherry-pick 后面可以带多个 commit,这样子就可以把提交到 master 的错误 commit,移到 dev 分支。

参考链接

git merge --no-ff 和 git merge --squash 区别

--no-ff指的是强行关闭fast-forward方式。

fast-forward方式就是当条件允许的时候,git直接把HEAD指针指向合并分支的头,完成合并。属于“快进方式”,不过这种情况如果删除分支,则会丢失分支信息。因为在这个过程中没有创建commit

git merge --squash 是用来把一些不必要commit进行压缩,比如说,你的feature在开发的时候写的commit很乱,那么我们合并的时候不希望把这些历史commit带过来,于是使用--squash进行合并,此时文件已经同合并后一样了,但不移动HEAD,不提交。需要进行一次额外的commit来“总结”一下,然后完成最终的合并。

总结:
--no-ff:不使用fast-forward方式合并,保留分支的commit历史
--squash:使用squash方式合并,把多次分支commit历史压缩为一次

https://segmentfault.com/q/1010000002477106

我感觉别人已经说的很好了,就不重复造轮子了。

1

Leetcode | 部门工资最高的员工

Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。

+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 70000  | 1            |
| 2  | Henry | 80000  | 2            |
| 3  | Sam   | 60000  | 2            |
| 4  | Max   | 90000  | 1            |
+----+-------+--------+--------------+

Department 表包含公司所有部门的信息。

+----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+

编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。

+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| Sales      | Henry    | 80000  |
+------------+----------+--------+

mysql

先用 group by 对 部门id 进行聚合,然后返回 最高的工资 和 对应的部门id。

select max(Salary) s, DepartmentId from Employee group by DepartmentId

给这张表起个别名,如 tmp,然后使用表连接即可。

连接条件为 tmp 表中的最高工资等于 Employee 中的 salary,剩下的列项按要求列出即可。

select b.Name Department, e.Name Employee, e.Salary Salary

from Employee e, Department b, (select max(Salary) s, DepartmentId from Employee group by DepartmentId) c

where e.DepartmentId = b.Id and e.Salary = c.s and e.DepartmentId = c.DepartmentId;

Regex | windows文件名不能包含的字符

  • windows 文件名不能包含的字符 \ / : * ? " < > |
  • 使用该正则表达式进行替换 \/\\\:\*\?\"\<\>\|
  • 将不能使用的字符替换成 - 。

Python 版本

import re

def new_name(file_name):
       return re.sub(r"[\/\\\:\*\?\"\<\>\|]", "-", file_name)

JavaScript 版本

function new_name(file_name) {
	return file_name.replace(/[\|\/\\\:\?\"\<\>]/mg, "-");
}

Spring Data Mongo 不保存 _class 字段

由于项目之前是使用 Nodejs 写的,数据存放在 Mongodb 中,为了保证切换到 Spring Boot 这边,数据保持一致性。而 Spring Data Mongo 在保存数据的过程中会加入 _class 字段。数据库中虽然没有这个字段还是可以正常读取的,但写入或者更新的时候,就默认给你添加这个字段。

去掉 Mongodb 写入时自动写入类名,写一个配置类即可。直接复制粘贴到项目中即可。已经过测试。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

/**
 * https://stackoverflow.com/questions/23517977/spring-boot-mongodb-how-to-remove-the-class-column
 */
@Configuration
public class MongoClassConverter {
    private final MongoDbFactory mongoDbFactory;
    private final MongoMappingContext mongoMappingContext;

    public MongoClassConverter(MongoDbFactory mongoDbFactory, MongoMappingContext mongoMappingContext) {
        this.mongoDbFactory = mongoDbFactory;
        this.mongoMappingContext = mongoMappingContext;
    }

    @Bean
    public MappingMongoConverter mappingMongoConverter() {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));
        return converter;
    }

}

参考链接

Linux | 进程和线程

个人理解

进程

  • 分配独立的资源空间
  • 父进程结束,子进程不结束,子进程会变成孤儿进程。
  • 进程不相互影响。
  • 进程资源不共享,需要 IPC 通讯手段 (.exe 和 .exe
  • 进程开销大
  • 子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。(类比 file 文件打开,但是没关闭,文件还是出于被占用状态。
  • 线程是操作系统直接支持的执行单元

线程

  • 线程是由进程创建的
  • 线程可以创建线程
  • 一个进程看成由一个或多个线程组成,里面必然包含一个主线程
    • 多个线程里面包含一个主线程和其他子线程,主线程可以称为其他子线程的父线程
  • 有同步和互斥
  • 主线程崩溃,子线程也会崩溃(main 和 main 调用的该程序中的其他函数

编程语言

  • C/C++ 多线程/多进程
  • Java 多线程
  • Pyhon 多线程/多进程

GitHub | 将 issues 保存到本地

利用 GitHub 的 api 接口,我们可以调用这个接口,访问所有人的 issue,返回的格式为 JSON。

https://api.github.com/repos/<your github name>/<your repositories>/issues
  • <your github name> github 的用户名
  • your repositories 要获取 issues 的仓库名

爬取代码

  • python 3.6
  • 将每个 issues 保存为后缀名为 md 的文件
  • 利用正则,将不能作为文件名的字符替换成 -

方式1

import requests
import json
import re


def new_name(file_name: str) -> str:
	"""
	将 [ \ / : * ? " < > | ] 替换成 [ - ]

	:param file_name: str
	:return: str
	"""
	return re.sub(r"[\/\\\:\*\?\"\<\>\|]", "-", file_name)


if __name__ == '__main__':
	# https://api.github.com/repos/<your github name>/<your repositories>/issues
	r = requests.get(
		url='https://api.github.com/repos/techial1042/blog/issues',
	)
	data = json.loads(r.text)
	for i in data:
		print(i['title'])
		with open(
			'%s.md' % new_name(file_name=i['title']),
			'w',
			encoding='utf-8'
		) as f:
			f.write(i['body'])

方式二

import requests
import json
import re

if __name__ == '__main__':
	# https://api.github.com/repos/<your github name>/<your repositories>/issues
	r = requests.get(url='https://api.github.com/repos/techial1042/blog/issues')
	data = json.loads(r.text)
	title = map(
		lambda file_name: re.sub(r"[\/\\\:\*\?\"\<\>\|]", "-", file_name),
		map(lambda this: this['title'], data)
	)
	body = map(lambda this: this['body'], data)
	for (t, d) in zip(title, body):
		with open('{}.md'.format(t), 'w', encoding='utf-8') as f:
			f.write(d)
		

github 备份 idea/pyCharm 配置

多台电脑同步 IDEA/PyCharm 配置。使用 ToolBox,然后登录 JetBrains 账号也是可以同步的。

Github 配置

  1. GitHub 上创建一下新的仓库
  2. 在自己的 GitHubsetting 创建 token
    1. setting -> Developer settings -> Personal access tokens -> Generate new token
    2. token 的配置可以如下图

token

把生成的值保存下来,等一下有用。

token_num

IDEA/PyCharm 设置

idea version 2018.3.2

  1. File -> Settings -> Tools -> Settings Repository,点击 + 号。把刚才 Github 创建的仓库地址添加进去。
    1. 格式如:https://github.com/techial1042/ideaConfig.git 。点击 OK 退出 settings。
  2. 再打开 File -> Settings Repository,输入刚才的 仓库地址
  3. 点击 Overwrite Remote
  4. 输入刚才创建的 token 的值
  5. 即可同步。

另一台电脑只需把仓库地址,和 token 填写好,点击 Overwrite Local/Merge 即可同步。

add

settings

参考链接

Git 换行符

Git 换行符

操作系统 换行符
Unix 和 OS X \n ,0x0A(LF)
Windows \r\n,0x0D0A(CRLF)
早期 Mac OS \r,0x0D(CR)

在 Git 中可以使用 AutoCRLFSafeCRLF 对换行符进行设置。

以下是我个人使用配置。

# 拒绝提交包含混合换行符的文件
git config --global core.safecrlf true

# 提交时转换为LF,检出时不转换
git config --global core.autocrlf input

在多人团队协作中,我们可以使用添加 .gitattributes 文件来限定换行符。这个文件通常放在项目的根目录,也就是有 .git 文件夹的那个目录。

.gitattributes 每一行格式如下

要匹配的文件模式 属性1 属性2 ...

因此我们用如下格式来限定换行符

# 对于任意文件,当该文件的格式为 text,即文本时,在提交的时候,行尾自动转换成 lf。
* text eol=lf

参考文档

7z 常用命令行总结

压缩文件

7z a <archive_name> [<file_name>...]

# fila_name 支持文件夹
# 将 后缀为 php 的文件压缩,压缩包的名称为 main.zip
7z a main.zip *.php

解压文件

7z x <archive_name>

# 解压文件,逐层加压,目录结构不变
# 7z e 会改变目录结构。
7z x main.zip

7z x main.zip -o"test"
# 将文件解压出来,解压到 test 目录下
# -o 指定文件夹,和文件夹之间不能存在空格

测试压缩包

7z t <archive_name>

# 测试 main.zip 压缩包
7z t main.zip

删除压缩包的文件

7z d <archive_name> [<file_name>]

# 从 main.zip 中删除后缀名为 .php 的文件
7z t main.zip *.php

查看压缩包的文件

7z l <archive_name>

# 查看 main.zip 包含的文件
7z l main.zip

大端和小端(Big endian and Little endian)

1537943476417

大端模式,数据的高字节保存在内存的低地址中;

小端模式,数据的高字节保存在内存的高地址中。

首先,为什么会有小端字节序?

答案是,计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。(也就是从低位开始处理数据的称之为小端模式

但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。

http://www.ruanyifeng.com/blog/2016/11/byte-order.html

判断大小端

/*
 * 返回 1 / 0
 * 1 代表 小端
 * 0 代表 大端
 */
bool check() 
{
	int value = 0x12345678;  
	return (*((char *)&value) == 0x78); 
}

一道笔试题目

char *sz = "0123456789"; 
int *p = (int*)sz; 
printf("%x\n",*++p); 

// 字符'0'对应的十六进制是0x30,请问在x86环境下程序输出是多少?
  • 首先得知道 x86 环境下为小端
  • *++p 的运算顺序为:先 ++,后进行取值。
  • int * 占四个字节
  • *p 指向的是 sz 的首地址。
  • 数组的大小端判断,可以把数组看成是一个特别大的一个数,再套用 计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。 。
各字符对应地址如下:

0	1	2	3	4	5	6	7	8	9
0x30	0x31	0x32	0x33	0x34	0x35	0x36	0x37	0x38	0x39

因此打印 *++p 的地址,也就相当于打印 4567 的地址。也就是 0x37363534

ESP8266

------------ 2018.5.5 -----------------------

补充:该文章写得不够全面,只是一些简单的使用,我会抽时间继续写有关这个模块更加深入使用的文章。而这篇文章适合刚刚接触esp8266新人阅读。
#7

ESP8266 模块基本使用

准备材料

  • 硬件

    • ESP8266 模块

    • USB_TLL 串口线

    • 3.3V 电源(可以使用 Arduino 的 3.3V 接口进行供电,如果没有 3.3V 的电压接口,可以使用 3V 的进行供电,5V 的接口电压太高会烧坏模块。

  • 软件

    • sscom4.2(也可以使用 Arduino IDE 自带的串口调试工具,现阶段不建议使用 Arduino IDE 自带的串口调试工具。

连接示意图

http://image.geek-workshop.com/forum/201601/06/234002of3b55cavjfg23et.jpg

针脚分布图

img

USB_TLL 与 ESP8266 连接图

调试 ESP8266 模块

  • 用 AT 指令调试 ESP8266 教程 http://www.geek-workshop.com/thread-25582-1-1.html
  • 注意事项
    • AT 指令要按给定的格式,不能多一个符号。如「空格」等。
    • 记得勾选串口调试工具记得勾上「发送新行」该选项。
    • 注意串口「占用」问题,要是出现被占用的问题,说明有另一个程序正在使用这个串口,把另一个程序关了即可。
    • 连接处接触「不良」的情况。接触不良可能会导致后面一系列的问题,如无法进行调试,调试过程中无法发送,无法接收等等。

ESP8266 AT 指令

这里只列出项目过程中使用较为频繁的指令,更多指令参考 ESP8266 帮助文档。

// 模块重启
AT+RST 

/*
工作模式设置
AT+CWMODE=1  ->  Station 客户端模式
AT+CWMODE=2  ->  AP 接入点模式
AT+CWMODE=3  ->  Station + AP 两种模式共存
*/
AT+CWMODE=3


// 连接到路由器,路由器名称 + 密码,路由器的名称不要使用中文
AT+CWJAP="123","0123456789"

// 连接到 TCP 服务器,地址 + 端口号
AT+CIPSTART="TCP","192.168.1.100",8080

// 发送数据,带上发送的字节长度,不要使用不带参数的发送指令
AT+CIPSEND=4

最后

如果以上没问题,也就说明 ESP8266 模块没问题了,可以接下来使用 Arduino UNO 板子连模块了。

线的问题会导致一直无法调试成功。

注意电脑兼容性问题,windows 10 连接调试模块表示躺枪,更新下驱动或者找兼容 windows 10 版本的驱动即可解决。

通过 Arduino 调试 ESP8266

针脚连接

连接时,按照 WIFI 模块的工作模式来接线。

即 GND 接 Arduino 的 GND ,

VCC 和 CH_PD 接 Arduino 的 3.3V 输出。

UTXD 和 URXD分别接 Arduino 的 13和12引脚

(即程序中由 SoftwareSerial 定义的 RX 和 TX)。

作者:rollingstarky
链接:https://www.jianshu.com/p/64024d8c3773
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

参考这份资料和 ESP8266 针脚的示意图,稍微修改了 Arduino 上连接的软串口,其实软串口选哪个没啥影响的。就是烧录程序的时候配置一下就可以了。

面包板是绝配,可以让线看起来不会那么乱,不然后期检查是否连错很麻烦……

下面是我的自己的连接方式

ESP8266 <---------> Arduino

GND <---------> GND

VCC, CH_PD <---------> 3.3V // 可以用 3.0V 电压

UTXD, URXD <---------> 11 和 10 引脚 // RX, TX 配置10、11为软串口

// Arduino UNO 上的 RX, TX  接口理论上可行,但我并未进行测试,自行可进行测试。

烧录

打开 Arduino IDE,板子型号和端口选好,然后上传即可。代码如下

#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX 配置10、11为软串口
void setup()
{
        Serial.begin(9600);
        while (!Serial);
        mySerial.begin(9600);
}

void loop()
{
        if (mySerial.available())
                Serial.write(mySerial.read());
        if (Serial.available())
                mySerial.write(Serial.read());
}
  • 烧录完成后,打开串口工具,和之前 AT 指令一样,测试是否连接有问题。
  • 这里推荐使用 Arduino IDE 自带的串口调试工具,要注意波特率的和换行方式。
    • 波特率看烧录时的规定。
    • 换行方式「 NL 和 CR」。

最后

如果以上都没问题,也就说明传输方面没问题了,剩下的可以和其他模块进行集成。如测电压模块,利用测电压的模块获得电压后,然后构造发送的数据报文,即可发送数据到服务器。

发送数据到服务器

ESP8266 AT 指令集成库

  • 有了这个库,你就不用自己手动构造一个 AT 字符串了,都帮你封装好了,然后调用函数接口即可。

  • https://github.com/itead/ITEADLIB_Arduino_WeeESP8266

  • 发送 HTTP 请求

    • 了解 HTTP 报文头的格式。
    • 构造报文头字符串。加入发送的参数等。
    • 使用封装好的 AT 指令发送。

发送电压到服务器参考代码

#include <Wire.h>
#include "Adafruit_ADS1015.h"
#include <SoftwareSerial.h>
#include "ESP8266.h"

//Adafruit_ADS1015 ads;

Adafruit_ADS1015 ads1015;
Adafruit_ADS1115 ads1115(0x49);

SoftwareSerial mySerial(13, 12); // RX, TX 配置10、11为软串口
ESP8266 wifi(mySerial, 115200);

// 服务器地址,WiFi 名称,WiFi 密码
const char url[] = "192.168.1.200";
const uint32_t port = 80;
const char ssid[] = "123";
const char pwd[] = "0123456789";

const float multiplier = 3.0;

// init
void setup()
{

	ads1015.begin();
	ads1015.setGain(GAIN_ONE);
	pinMode(10, OUTPUT);
	//ads.begin();
	Serial.begin(115200);
	while (!Serial) {
		;
	}
	wifi.restart();
	delay(1000);
}

double get_voltage()
{
	uint16_t adc0 = ads1015.readADC_SingleEnded(0);
	double res = (adc0 * 2) / 1000.0;
	Serial.print(">>>> voltage = ");
	Serial.print(res);
	Serial.print("\r\n");
	return res;
}

const char str_post_1[] = "GET /index.php?content=";
const char str_post_2[] = " HTTP/1.1\n"
		"Host: 192.168.1.200\n"
		"Connection: close\n"
		"Accept: text/html\n\n\n\n";

void loop(void)
{
	// 连接 WiFi
	if (wifi.joinAP(ssid, pwd)) {
		Serial.print(">>>> wifi connection success\n");
	} else {
		Serial.print(">>>> wifi connection error\n");
		wifi.restart();
		delay(1000);
		return;
	}

	while (true) {
		// 与服务器进行 TCP 连接
		if (wifi.createTCP(url, port)) {
			Serial.print(">>>> creat tcp success\n");
		} else {
			Serial.print(">>>> creat tcp error\n");
			continue;
		}

		float voltage = get_voltage();
		String get = str_post_1;
		get += voltage;
		get += str_post_2;

		if (wifi.send(get.c_str(), get.length())) {
			Serial.println(">>>> send success");
		} else  {
			Serial.println(">>>> send error");
			break;
		}
		Serial.println(">>> get_string ------------");
		Serial.println(get.c_str());
		Serial.println("------------ <<<\n");

		char result[100];
		wifi.recv(result, 100);
		Serial.println(">>>> result >>> ------------");
		Serial.println(result);
		delay(10000);
	}
	delay(2000);
	// 断开 WiFi 连接
	Serial.print(">>>> leave wifi ");
	Serial.println(wifi.leaveAP() ? "success" : "error");
}

Leetcode | 2的幂

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

示例 1:

输入: 1
输出: true
解释: 20 = 1

示例 2:

输入: 16
输出: true
解释: 2^4 = 16

示例 3:

输入: 218
输出: false

java

使用位运算。

我们对特殊情况先进行判断,小于等于 0 的肯定不是,等于 1 的肯定是 true

对于一般情况。如果该数为 2 的幂,最高位为 1,其他位为 0

8		->	0000 1000
16		->	0001 0000
32		->	‭0010 000064		->	0100 0000‬
...

对于 2 ^ n - 1 的数。

7		->	0000 0111
15		->	0000 1111
31		->	‭0001 1111
63		->	0011 1111
...

两者进行与运算(&)。

8		->	0000 1000
7		->	0000 0111
&		->	0000 0000

16		->	0001 0000
15		->	0000 1111
&		->	0000 0000

32		->	‭0010 000031		->	‭0001 1111
&		->	0000 0000

64		->	0100 000063		->	0011 1111
&		->	0000 0000

14		->	0000 1110
13		->	0000 1101
&		->	0000 1100
...

因此,我们只需要把这个数和比他小 1 的数进行与运算

你会发现得出的结果是 0 的,都是 2 的幂次

与运算之后不是 0 的就不是 2 的幂次

class Solution {
    public boolean isPowerOfTwo(int n) {
        if (n <= 0) return false;
        if (n == 1) return true;
        return (n & (n - 1)) == 0;
    }
}

Python 爬虫 302 重定向

python 中的 requests 是会跟着重定向,要想关闭自动重定向,需要在请求中关闭 allow_redirects=False 即可。

	r = requests.post(
		url=url,
		allow_redirects=False
	)
	print(r.headers['Location'])
  • 想获得重定向后的地址,可以从请求后的 headers 中获得 Location 的值即可。
  • 自己再构造提交即可跳转到新的页面。
  • 跳转后的网站,网站可能会检查 headers 中的 Referer 参数,如果不是从重定向来的地址,会请求失败。
  • 在请求跳转的网站时,headers 只要把 Referer 加上即可。

参考代码如下

	r = requests.post(
		url=url,
		allow_redirects=False
	)
	print(r.headers['Location'])
	r_new = requests.get(
		url=new_url,
		headers={
			"Referer": "".format(r.headers['Location']),
			"Host": host
		}
	)

CTF | RSA

RSA 参数

  • p q 是 素数,并且两者互质
  • n 是两个素数的乘积 n = q * p
  • e 是指数,并且小于 r 且与 r 互质
  • r = (q - 1) * (p - 1)
    • 根据欧拉函数, 两个数互质,φ(mn) = φ(m) * φ(n) = (m - 1) * (n - 1)
    • 前提是 mn 都为质数才成立。
  • d = (1 - r * i) / e 其中 i = 1, 2, 3 ...
  • (n, e) 为公钥
  • (n, d) 为私钥
  • c 表示密文
  • m 表示明文

求 d

已知 p,q,e

def get_d(p: int, q: int, e: int) -> int:
	r = (p - 1) * (q - 1)
	i = 0
	while True:
		if (1 + r * i) % e == 0:
			return '%d' % (1 + r * i // e) # 除 和 取整除 两者有区别
		i += 1


if __name__ == '__main__':
	p = 473398607161
	q = 4511491
	e = 17
	x = get_d(p=p, q=q, e=e)
	print(x)

用了网上别人代码,里面有一个坑,愣是坑了很久。

别人是用 python2 版本,我用的是 python3,在最后的 整除 这里有区别

得到的答案是 125631357777427552

整除 得到的答案是 125631357777427553

使用 整除 得到的才是正确答案。

或者使用 decimal 模块

求明文

已知 公钥 和 密文,求明文

  1. 已知 公钥,得到 ne
  2. 分解 n 得到 pq
  3. 得到 d
  4. 解密密文
if __name__ == '__main__':
        c =
        d =
        n =
        x = pow(c, d, n)
        print(bytes().fromhex('%x' % x))

decimal 模块

要求超过17位的精度分析

python默认的是17位小数的精度,但是这里有一个问题,就是当我们的计算需要使用更高的精度(超过17位小数)的时候该怎么做呢?

  1. 使用格式化(不推荐)

  2. 高精度使用decimal模块,配合getcontext

默认的context的精度是28位,可以设置为50位甚至更高,都可以。这样在分析复杂的浮点数的时候,可以有更高的自己可以控制的精度。其实可以留意下context里面的这rounding=ROUND_HALF_EVEN 参数。ROUND_HALF_EVEN, 当half的时候,靠近even.

谈谈关于Python里面小数点精度控制的问题

利用 decimal 模块,改良求 d 代码

from decimal import *

def get_d(p: int, q: int, e: int) -> str:
	r = Decimal((p - 1) * (q - 1))
	i = 0
	while True:
		if (1 + r * i) % e == 0:
			return '%d' % (1 + r * i / e)
		i += 1


if __name__ == '__main__':
	getcontext().prec = 100
	p = 473398607161
	q = 4511491
	e = 17
	x = get_d(p=p, q=q, e=e)
	print(x)

求明文

已知 p,q,e 和 密文 c,求明文

from decimal import *


def get_d(p: int, q: int, e: int) -> int:
	r = Decimal((p - 1) * (q - 1))
	i = 0
	while True:
		if (1 + r * i) % e == 0:
			return '%d' % (1 + r * i / e)
		i += 1


def get_n(p: int, q:int) -> int:
	return q * p


if __name__ == '__main__':
	getcontext().prec = 1000

	p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
	q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
	e = 65537
	c = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034
	d = get_d(p=p, q=q, e=e)
	n = get_n(p=p, q=q)
	print(d)
	print(pow(c, Decimal(d), n))

参考文献

wsl, Linux Systemd 和 frp

总结一下这几天折腾的东西

折腾了 wsl,awk,frp,前后端部署时跨域问题,nginx ...

wsl 用来开发 还是不怎么够用

很多资料网上都有,只要一查就有,我自己就不重复造轮子。

wsl

Windows 中的 子系统 中没有 systemd 相关命令,wsl 其实去掉了很多功能 ……

要想在 wsl 中使用 systemd 相关命令,需要配置一些东西,有点麻烦 ……

服务启动/停止只能用 service xxx start| stop | reload ...

网上好多关于 ubuntu 系统的配置方法都无法在 wsl 中使用。

wsl 适合练习 linux,不太适合用来开发。

systemd 教程

以下是 systemd 相关命令

picture

Arduino | 关于中断

终于把 arduino 中断的坑给填了。

中断分类

中断 描述
外部中断 外部设备导致的中断请求(如板子上的 reset 按钮
内部中断 程序中设置的中断指令
异常中断 程序或硬件出现异常或故障

外部中断

attachInterrupt() 函数

触发中断。attachInterrupt(interrupt, ISR, mode)

参数
  • interrupt:中断端口编号
  • ISR:中断服务程序,即中断后要执行的程序
  • mode:中断请求的方式,可以设置成下面4种:
    • LOW 当引脚为低电平时,触发中断
    • CHANGE 当引脚电平发生改变时,触发中断
    • RISING 当引脚由低电平变为高电平时,触发中断
    • FALLING 当引脚由高电平变为低电平时,触发中断
注意事项
  • 中断函数不能带参数,也没有返回值
  • 中断函数中,delay函数不能使用
  • 读取串口数据的话,可能会丢失
  • 中断函数里面,使用的变量需要定义为 volatile 型

digitalPinToInterrupt() 函数

将 引脚 转换成 中断信号。 digitalPinToInterrupt(pin)

参数
  • pin 引脚
  • 下面的表格为具体开发板对应的 中断信号
BOARD INT.0 INT.1 INT.2 INT.3 INT.4 INT.5
Uno, Ethernet 2 3
Mega2560 2 3 21 20 19 18
32u4 based (e.g Leonardo, Micro) 3 2 0 1 7

外部中断官方样例

关于 attachInterrupt() 和 digitalPinToInterrupt() 函数,官方给的参考代码。

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  state = !state;
}

说实话,这代码,我对于整段代码运行的前后顺序还是有点迷的。

因此下面将给出我自己的测试代码。

测试代码

在官方代码的基础上修改,利用串口调试,将方便我们观察效果。

并将 interruptPin 引脚状态设置为 INPUT

利用一根杜邦线,一边接在程序设置好的 interruptPin 接口上,另一边接在 GND 引脚上。

#include <SoftwareSerial.h>

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void serial_init()
{
	Serial.begin(115200);
	while (!Serial) {}
}

void setup()
{
	serial_init();
	Serial.println("setup function begin");
	pinMode(ledPin, OUTPUT);
	pinMode(interruptPin, INPUT_PULLUP);	
	attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
	Serial.println("setup function end");
}

void loop()
{
	Serial.println("loop start");
	digitalWrite(ledPin, state);
	Serial.println("loop end");
}

void blink()
{
	Serial.println("blink function start");
	state = !state;
	Serial.println("blink function end");
}

运行结果

setup function begin
setup function end
loop start
loop end
loop start
loop end
...
loop start
loop end
loop start
loop endloop start
loop end
loop start
loop end
loop start
loop end
loop start
blink function start
blink function end
blink function start
blink function end
blink function start
blink function end
bliloop end
loop start
loop end
loop start
loop end
...
loop start
loop end
loop start
loop end
loop stblink function start
blink function end
blink function start
blink function end
blink function start
blink function end
blirt
loop end
loop start
loop end
loop start
loop end
loop start
loop end
...
loop start
loop end
looblink function start
blink function end
blink function start
blink function end
blink function start
blink function end
bli start
loop end
loop start
loop end
loop start
loop end
loop start
loop end

结果分析

  • 设置完中断触发程序后,不管程序运行到哪一条都会打断目前运行状态,进入中断函数。
  • 由于是人工触发,手抖等情况,会导致多次触发中断从而进入中断函数
  • pinMode(interruptPin, INPUT_PULLUP); //将触发中断的引脚设置为 输入上拉 模式。
    • 意思也就是将这个引脚的电平嵌位在高电平。既为 VCC。不过不设置的话会处于悬空状态,不确定是高电平还是低电平。
    • 一旦用杜邦线接触 GND 引脚,就会触发中断,中断触发结束后,该引脚的状态仍然为高电平

内部中断

FlexiTimer2 为例。

测试代码

#include "FlexiTimer2.h"

void Flash() 
{
	digitalWrite(13, digitalRead(13) ^ 1);
}

void setup() 
{
	pinMode(13, OUTPUT);

	FlexiTimer2::set(1000, 1.0 / 1000, Flash); 
	FlexiTimer2::start();
}

void loop() 
{
}

实验现象

LED13 灯亮一秒,熄灭一秒。依次反复。

扩展

内部中断可以作为定时器使用。也就是隔一段设定好的时间执行某段程序。

利用该特性,我们可以定时发送数据来保持与服务器的通讯。如心跳包。

参考链接

测试代码

该文章的测试代码。test.zip

Leetcode | 第二高的薪水/第N高的薪水

编写一个 SQL 查询,获取 Employee 表中第 n 高的薪水(Salary)。

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+

例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null

+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200                    |
+------------------------+

第二高薪水还是第N高薪水,原理类似。注意一些细节。

使用子查询,比较任意的两份薪资,当 薪资 = N - 1 的时候,就是第 N 高薪水。注意使用 count 将薪资相同的聚合在一起(相同薪资的人可能有多个),并去重。

mysql

第二高的薪水

select max(e.Salary) SecondHighestSalary from Employee e

where (select count(distinct e1.Salary)

        from Employee e1 where e1.Salary > e.Salary) = 1

第 N 高的薪水

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT

BEGIN

  RETURN (

      # Write your MySQL query statement below.
      
      select max(e.Salary) SecondHighestSalary 

      from Employee e 

      where (select count(distinct e1.Salary)

             from Employee e1 

             where e1.Salary > e.Salary) = N - 1

  );

END

Spring Data JPA 相关使用总结

持久层 和 ORM

何谓“持久化”
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。

何谓“持久层”
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。

何谓“对象数据映射(ORM)”
ORM-Object/Relational Mapper,即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和 Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。

http://www.voidcn.com/article/p-ghpyiomy-cu.html

Spring Data JPA、MyBatis还有Hibernate有什么区别

  • Java 持久化 API 简称 JPA,是一个规范。
  • Hibernate 是 JPA 的一个实现。目前最流行的持久层框架。

Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。要说差异,主要是比较的是 MyBatis 和 Hibernate。

性能考虑

由于 Hibernate 比 MyBatis 抽象封装的程度更高,理论上单个语句之心的性能会低一点(所有的框架都是一样,排除算法上的差异,越是底层,执行效率越高)。

但 Hibernate 会设置缓存,对于重复查询有一定的优化,而且从编码效率来说,Hibernate 的编码效果肯定是会高一点的。所以,从整体的角度来看性能的话,其实两者不能完全说谁胜谁劣。

从 ORM 角度考虑

Hibernate 是完备的 ORM 框架,是符合 JPA 规范的,但 MyBatis 不是。MyBatis 比单纯写 JDBC 肯定是方便一点,但无可避免还是要写SQL,且无法做到跨数据库 。Hibernate 使用 JPA 就可以无需考虑数据库的兼容性问题。

使用 Hibernate 的一个难点是,如何来设计对象之间的关系。如果是关系型数据库的话,表和表是通过外键来进行关联的。而在 ORM 中,则需要从面向对象的角度出发,来设计对象之间的关联关系。这个是需要思路上做一个转变的。

与 Spring 的集成角度考虑

Spring 以及 Spring Boot 官方都没有针对 MyBatis 有具体的支持,但对 Hibernate 的集成一直是有的。

但这并不表明 MyBatis 无法与 Spring Boot 集成,毕竟现在互联网企业,还是很流行使用MyBatis 和 Spring 。MyBatis 官方社区自身也是有 对 Spring \ Spring boot 集成做支持的,所以在技术上,两者都不存在问题。相关框架可以见 https://github.com/mybatis/springhttps://github.com/mybatis/spring-boot-starter

https://www.imooc.com/article/19754

Spring Data JPA 生成表

  • 在类上加入 @Entity 注解,成员变量加入注解,如 @Column ,依情况而定。

  • 在 application.yml 或者 application.properties 中添加以下配置。(建议使用 .yml,看起来更加清晰)

  • jpa.hibernate.ddl-auto 以更细粒度的方式控制行为。以下是各属性的作用

    • ddl-auto: create 创建架构,销毁以前的数据。
    • ddl-auto: create-drop 当 SessionFactory 显式关闭时,通常在应用程序停止时删除模式。
    • ddl-auto: update 更新架构。(最常用)
    • ddl-auto: validate 验证架构,不对数据库进行任何更改。
  • jpa.generate-dll 作用

    • ddl 全称 Data Definition Language,数据库定义语言,常见的也就是数据库的增删改查(CRUD)。
    • JPA具有DDL生成功能,可以将这些功能设置为在数据库启动时运行。这是通过两个外部属性控制的:
    • spring.jpa.generate-ddl = false 打开和关闭该功能,默认是关闭。与供应商无关。
     spring:
       jpa:
         show-sql: true
         generate-ddl: true
         hibernate:
           ddl-auto: update
       datasource:
         url: jdbc:mysql://localhost:3306/<>?useSSL=false&createDatabaseIfNotExist=true&characterEncoding=utf8&serverTimezone=GMT
         username: root
         password: root

相关链接

Querydsl

QueryDSL是一个Java语言编写的通用查询框架,专注于通过JavaAPI方式构建安全的SQL查询。QueryDSL可以应用到NoSQL数据库上,QueryDSL查询框架可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建SQL。目前QueryDSL支持的平台包扣JPA、JDO、SQL、Java Collections、RDF、Lucene、Hibernate Serch、MongoDB等。

https://www.jianshu.com/p/7379173e1970

maven 配置(添加依赖

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>

maven 配置(添加插件,生成查询实列,之后点击 compile 即可生成实列。

<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<executions>
	<execution>
	<goals>
		<goal>process</goal>
	</goals>
	<configuration>
		<outputDirectory>target/generated-sources/java</outputDirectory>
		<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
	</configuration>
	</execution>
</executions>
</plugin>

Querydsl ,可以弥补 Spring Data JPA 不灵活,使用 Querydsl 查询只需要继承 QueryDslPredicateExecutor 即可

public interface UserRespiratory
        extends QuerydslPredicateExecutor<User>, JpaRepository<User, Integer> {
}

继续说说ESP8266

  • 感觉之前太小看 esp8266 这个模块了,只是把它当做单纯的 WiFi 模块,随着单片机的深入学习,还有师兄的提醒,发现这个模块太强大了。(我要把它吹爆

  • 另外更正有点,其实说 esp8266,太笼统了,它分了好几种型号,我用的型号,准确的名称是 esp8266-01 ,因为在找 esp8266 的资料时,会因为型号不一样,因此引脚分布也不一样,导致方案并不是通用的。

  • 以下的内容,是使用了 esp8266-01

esp8266-01
esp8266-01 针脚分布图

最最重要的是阅读官方文档,我看了官方的 AT 指令集文档,发现里面有几个有趣的指令。可以将配置的 AT 指令保存到 Flash 内,断电重启之后也不会丢失。这样子就不需要每一次重启配置连接的 WiFi 等等。

 AT+CWMODE_DEF — 设置当前 Wi-Fi 模式并保存到 Flash

 AT+CWJAP_DEF — 连接 AP,保存到 Flash

 AT+CWAUTOCONN — 上电是否⾃自动连接 AP

 AT+SAVETRANSLINK — 保存透传到 Flash

...

互相配合使用这些 AT 指令,可以提高设备的稳定性。

pip / anaconda 修改镜像

windows 环境。

pip

  1. win + E,打开文件管理器

  2. 地址栏输入 %APPDATA%

  3. 找到 pip 文件夹,如果没有的话就创建一个并进入该目录

  4. 如果没有的 pip.ini 的话,就创建一个 pip.ini 文件

  5. 然后在 pip.ini 输入以下内容

    [global]
    index-url = https://mirrors.ustc.edu.cn/pypi/web/simple
    format = columns

anaconda

  1. win + R 打开 cmd 窗口

  2. 复杂并粘贴一下几句命令即可

    conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
    conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
    conda config --set show_channel_urls yes

orther

  • 其他相关命令

    conda update conda # 更新 Anaconda
    conda update --all # 更新包
    
    pip list --outdate # 列出可更新的包
    pip install -U [pack] # 更新包
    
    pip install package_name # 安装包
    conda install package_name # 安装包
    

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.