使用macOS微信提取自定义表情

关于如何从微信提取自定义表情,你在百度上搜索出来的大部分内容都已经失效了。如果你恰好使用macOS,不妨试试以下方法。

测试环境

  1. macOS Monterey Version 12.5
  2. WeChat for macOS Version 3.5.0

步骤

  1. 使用终端打开文件夹
1
open ~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application\ Support/com.tencent.xinWeChat/2.0b4.0.9/
  1. 在文件夹中能看到一个或多个类似于96d6682a3f1174816126f477f66ad3b6的随机值文件夹。打开该文件夹里的Stickers文件夹。

  1. 找到 fav.archive 文件,并复制出来(比如复制到桌面),并改名为fav.archive.plist
  2. 在终端执行plutil -convert xml1 fav.archive.plist,将该文件从二进制的 plist 转为 XML 的 plist,然后你就可以用你喜欢的文本编辑器打开这个plist文件了
  3. 在文件里可以看到许多<string></string>中为链接,每一个链接都对应一个自定义表情,访问该链接即可下载。链接的顺序和自定义表情中的顺序一致。

注意:如链接中出现&amp;,需要手动替换为&

  1. 如需下载表情包中的表情,可以先将表情添加到自定义表情,然后重复步骤1-5。

NixOS 上手记录

在 jq 同学的一再安利下,我终于也入坑 NixOS 了)))

什么是 NixOS

NixOS 是独立开发的 GNU/Linux 发行,它旨在改进系统配置管理的现状。在 NixOS 中,整个操作系统,包括内核、应用程序、系统软件包、配置文件,统统都由Nix包管理器来创建。Nix 将所有软件包以彼此分离的方式进行存储,因此就不存在/bin/sbin/lib/usr之类的目录,而是保存在/nix/store中。NixOS 的其他创新特色包括可靠升级、回滚、可重现的系统配置、二进制代码基于源文件的管理模型、多用户包管理。

对于我来说,用 NixOS 的好处就是:

  1. 体验一把用程序写配置文件(?)
  2. 方便进行 VPS 的迁移和一键部署,使用 NixOS 只需要根据配置代码进行生成部署即可。
  3. 可以复制粘贴 jq 写的配置代码

安装 NixOS 至 ESXi 中

虽然可以直接使用其它发行版的 Linux 到 NixOS 的转换脚本,但是我的 ESXi 里也没装正经 Linux,所以不如直接全新安装。

  1. 下载 NixOS Minimal ISO image (64bit),上传到 ESXi 的 datastore 里。我下载的是21.11版本。
  2. 在 ESXi 中创建一个新的虚拟机,CD/DVD Drive 选择刚才 NixOS ISO 镜像,Boot Options 选择 BIOS(省事),并配置网络,开启虚拟机。
  3. 切换为root用户
    1
    sudo -i
  4. 检查网络配置。ESXi 会虚拟一个有线网络,使用ifconfig命令查看是否拿到 IP 地址。
  5. 创建分区表、格式化分区、挂载,创建一个 19G 的基本分区和一个 1G 的 Swap 分区。
    1
    2
    3
    4
    5
    6
    7
    [root@nixos:~]# parted /dev/sda -- mklabel msdos
    [root@nixos:~]# parted /dev/sda -- mkpart primary 1MiB -19GiB
    [root@nixos:~]# parted /dev/sda -- mkpart primary linux-swap -1GiB 100%
    [root@nixos:~]# mkfs.ext4 -L nixos /dev/sda1
    [root@nixos:~]# mkswap -L swap /dev/sda2
    [root@nixos:~]# mount /dev/disk/by-label/nixos /mnt
    [root@nixos:~]# swapon /dev/sda2
  6. 如果需要配置静态IP,使用如下命令。
    1
    2
    3
    4
    5
    6
    [root@nixos:~]# ifconfig <interface> <my-static-ip> netmask <my-netmask>
    [root@nixos:~]# ifconfig <interface> down
    [root@nixos:~]# ifconfig <interface> up
    [root@nixos:~]# route del -net 0.0.0.0
    [root@nixos:~]# route add default gw <gateway-ip>
    [root@nixos:~]# echo "nameserver 8.8.8.8" >> /etc/resolv.conf
  7. 开始安装。在安装的最后一步,会提示设置 root 密码。
    1
    2
    3
    [root@nixos:~]# nixos-generate-config --root /mnt
    [root@nixos:~]# sed -i 's#\# boot.loader.grub.device = "/dev/sda"#boot.loader.grub.device = "/dev/sda"#g' /mnt/etc/nixos/configuration.nix
    [root@nixos:~]# cd /mnt/etc/nixos/ && nixos-install
  8. 重启虚拟机(断开 CD/DVD Drive),完成安装

    如需在 GPT 磁盘中使用 UEFI 引导安装,或者解锁各种花里胡哨的安装方法,请参考文档

Nix 基本语法

官方文档介绍

举例 描述
基本类型
"Hello world" 字符串
"${pkgs.bash}/bin/sh" 包含表达式的字符串(展开为"/nix/store/hash-bash-version/bin/sh")
true, false 布尔类型
123 整数
./foo.png 路径
复合类型
{ x = 1; y = 2; } 集合,有 x 和 y 两个属性
{ foo.bar = 1; } 嵌套集合,等价于{ foo = { bar = 1; }; }
rec { x = "foo"; y = x + "bar"; } 集合中可以使用运算,等价于{ x = "foo"; y = "foobar"; }
[ "foo" "bar" ] 列表,有两个元素
运算符
"foo" + "bar" 字符串连接
1 + 2 整数相加
"foo" == "f" + "oo" 判断相等
"foo" != "bar" 判断不相等
!true 逻辑取反
{ x = 1; y = 2; }.x 选取属性
{ x = 1; y = 2; }.z or 3 选取属性,缺省值为3
{ x = 1; y = 2; } // { z = 3; } 集合合并(右侧会覆盖左侧)
控制结构
if 1 + 1 == 2 then "yes!" else "no!" 条件表达式
assert 1 + 1 == 2; "yes!" 断言检查,如果断言成立,则返回分号后的内容
let x = "foo"; y = "bar"; in x + y 变量定义
with pkgs.lib; head [ 1 2 3 ] 将给定集合中的所有属性添加到当前域
函数 (lambdas)
x: x + 1 传入整数x,返回x+1的函数
(x: x + 1) 100 函数调用,传入x=100,返回101
let inc = x: x + 1; in inc (inc (inc 100)) 函数绑定到变量名,并嵌套使用
{ x, y }: x + y 传入一个仅包含属性xy的集合,返回x+y的函数
{ x, y ? "bar" }: x + y 传入一个仅包含属性xy的集合(y的缺省值为"bar"),返回x+y的函数
{ x, y, ... }: x + y 传入一个包含属性xy的集合(其他属性被忽略),返回x+y的函数
{ x, y } @ args: x + y 传入一个仅包含属性xy的集合(绑定集合至变量args),返回x+y的函数
内置函数
import ./foo.nix 加载和返回 Nix 文件
map (x: x + x) [ 1 2 3 ] 将函数应用到列表中的每一个元素 (返回值为 [ 2 4 6 ])

开启 SSH 访问

通过 ESXi 的 Console 进行访问也不方便,毕竟 NixOS 也是 Linux,为什么不用简单好用的 SSH 来访问呢?其实 NixOS 自带了 SSH,但自动生成的配置也没有开启 SSH 访问,需要手动修改 configuration.nix 来开启 SSH 访问功能。

1
2
[root@nixos:~]# cd /etc/nixos
[root@nixos:/etc/nixos]# nano configuration.nix

其中有一行
1
# services.openssh.enable = true

将其改为
1
2
services.openssh.enable = true
services.openssh.permitRootLogin = "yes";

保存文件。然后使用以下命令重新构建系统。
1
[root@nixos:/etc/nixos]# nixos-rebuild switch 

关于nixos-rebuild命令的一些常见用法如下:

命令 描述
nixos-rebuild switch 根据新配置构建系统,使其成为 GRUB 启动时默认 profile,并尝试在当前系统中应用新的配置
nixos-rebuild switch -p test 根据新配置构建系统,创建一个新的 profile,在 GRUB 启动页面显示为NixOS - Profile 'test'
nixos-rebuild test 根据新配置构建系统,并尝试在当前系统中应用新的配置,但不使其成为 GRUB 启动时默认 profile
nixos-rebuild boot 根据新配置构建系统,使其成为 GRUB 启动时默认 profile,但不并尝试在当前系统中应用新的配置
nixos-rebuild build 根据新配置构建系统,但什么也不做,通常用来看是否编译正常

现在,可以使用ssh root@[虚拟机的IP]命令,输入密码进行登录。

但是,如果我想使用证书方式登录,应该怎么配置呢?首先,得找个地方存储我们的authorized_keys

1
2
3
[root@nixos:~]# cd /etc/nixos
[root@nixos:/etc/nixos]# mkdir -p ssh
[root@nixos:/etc/nixos]# echo 'ssh-rsa AAAAB3NzaC...E= jogle@home' > ssh/authorized_keys

然后修改configuration.nix文件,添加以下内容
1
2
3
users.users."root".openssh.authorizedKeys.keyFiles = [
/etc/nixos/ssh/authorized_keys
];

最后当然是重新构建系统。
1
[root@nixos:/etc/nixos]# nixos-rebuild switch 

如果证书配置正确的话,重新使用ssh root@[虚拟机的IP]命令进行连接即可直接登录。

参考链接:NixOS Wiki - SSH public key authentication

配置静态 IP

有时候需要配置静态 IP,要不然没有网络连接,甚至都没法进行 nixos-rebuild。配置静态 IP 仅需要编辑 configuration.nix

1
2
3
4
5
6
networking.interfaces.<interface>.ipv4.addresses = [ {
address = "192.168.1.2";
prefixLength = 24;
} ];
networking.defaultGateway = "192.168.1.1";
networking.nameservers = [ "8.8.8.8" ];

安装 Vim

系统里只自带了一个 Nano 文本编辑器,为了方便以后的 Nix 配置修改,所以我要安装一个 Vim。没有 Vim 用我快要死了)

configuration.nix 中可以找到:

1
2
3
4
5
6
7
# List packages installed in system profile. To search, run:
# $ nix search wget
# environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
# wget
# firefox
# ];

可以在environment.systemPackages中声明要安装的包名,NixOS 就会自动为我们安装。这种方式称为声明式包管理(Declarative Package Management)。就像 apt-get 一样简单)

所以我们顺手把environment.systemPackages改成

1
2
3
environment.systemPackages = with pkgs; [
nano vim wget curl
];

执行nixos-rebuild switch后就可以在命令行里使用 Vim 了。

装完了 Vim,自然少不了配置.vimrc,以及配置插件,那么如何配置呢?这里给出一种方法:

/etc/nixos/下创建一个vim.nix文件,内容填写为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
with import <nixpkgs> {};

vim_configurable.customize {
# Specifies the vim binary name.
# E.g. set this to "my-vim" and you need to type "my-vim" to open this vim
# This allows to have multiple vim packages installed (e.g. with a different set of plugins)
name = "vim";
vimrcConfig.customRC = ''
syntax on
set nu!
set autoindent
'';
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
start = [ vim-airline fugitive ];
# manually loadable by calling `:packadd $plugin-name`
};
}

然后在configuration.nix中,修改刚才的environment.systemPackages

1
2
3
environment.systemPackages = with pkgs; [
nano (import ./vim.nix) wget curl
];

使用 Fish Shell

configuration.nix 中添加一行,为 root 修改默认 shell 为 fish。

1
users.users.root.shell = pkgs.fish;

执行nixos-rebuild switch后,还是使用默认的 bash shell。这是因为nixos-rebuild命令不会自动开启/关闭用户服务,而是为每一个开启了用户服务的用户执行daemon-reload。所以,nixos-rebuild无法直接打开一个新的 fish shell 来替换当前使用的 bash shell。

解决方法是:重启!

关于 fish shell 的插件配置,可以参考 NixOS Wiki - fish

开启 Flakes

在刚才的配置中,我们只配置了软件包开启,但是没有指定软件包的版本。为了能对软件包的版本进行控制,需要开启 Nix Flakes。由于官方担心 Nix 2.4 功能变化过大,尤其是会与旧版 Nix 的行为不兼容,NixOS 21.11 仍使用 Nix 2.3,且默认禁用 Flake 功能,所以需要手动进行开启。

configuration.nix 中添加一下内容:

1
2
3
4
5
6
7
8
9
10
11
12
{ config, pkgs, ... }:

{
# ...
nix = {
package = pkgs.nixUnstable;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
# ...
}

修改之后需要运行 nixos-rebuild switch 命令,将 Nix 包管理器升级到支持 Flake 的测试版。

然后在 /etc/nixos 里创建一个 flake.nix 文件。其中定义了一个软件源(input),是 NixPkgs 的 unstable 分支(也就是 master 分支)。文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
# 输入配置,即软件源
inputs = {
# Nixpkgs,即 NixOS 官方软件源
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};

# 输出配置,即 NixOS 系统配置
outputs = { self, nixpkgs, ... }@inputs: {
# 定义一个名为 nixos 的系统
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}

运行 nix flake update,会生成一个 flake.lock 文件。在下一次运行 nixos-rebuild switch 命令时,NixOS 会自动优先读取 flake.nix 而非 configuration.nix,把系统里的所有软件包升级(或降级)到这个特定的版本。但因为我们已经把 configuration.nix 加入至 flake.nix 中,所以系统配置还是保持不变。

远程部署

有些软件包在安装的时候需要编译,在低配置机器上执行 nixos-rebuild 会编译失败而出错。Flakes 支持远程部署,即先在本地进行编译,生成二进制文件,然后传输到目标机器上。仅需要在 flakes.nix 文件中增加相关的连接信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

# 新增下面几行
deploy-rs = {
url = "github:serokell/deploy-rs";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};

# 新增下面几行
deploy = {
sshUser = "root"; # SSH 登录用户名
user = "root"; # 远程操作的用户
sshOpts = [ "-p" "2222" ]; # SSH 参数,这里是指定端口 2222

# 部署失败自动回滚,建议关闭
# 因为 NixOS(尤其是 Unstable 分支)部署不太稳定,有时需要部署两次才成功
# 如果自动回滚了,反而适得其反,导致连续部署失败
autoRollback = false;

# 断网自动回滚,建议关闭
# 在你配置防火墙或 IP 出错把网络干掉时,自动回滚,这样你就不用去主机商控制面板连 VNC 或 IPMI 了
# 但如果你就是在调整防火墙或者 IP 配置,会有当时断网、但重启机器就可以应用新配置恢复正常的情况
# 自动回滚反而适得其反,因此建议关闭
magicRollback = false;

nodes = {
"nixos" = {
# 目标机器的地址,IP 或域名或 .ssh/config 中配置的别名均可
hostname = "192.168.56.105";
profiles.system = {
# 调用上面的 nixosConfigurations."nixos"
path = deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations."nixos";
};
};
};
};
};
}

参考资料:https://lantian.pub/article/modify-website/nixos-initial-config-flake-deploy.lantian/

利用proxifier实现真正的全局代理

由于一些代理使用的是sock5代理,一般情况下只有浏览器能直接支持。很多时候我们希望自己的应用也能够走代理,这个时候可以采用Proxifier的配合。

首先Proxifier是付费软件,在官网下载的是一个月的试用版。需要长期使用请去别处下载破解版。

打开软件,首先配置代理服务器。点击第一个按钮(代理服务器)。

在弹出的窗口中点击添加按钮。
地址填入127.0.0.1
端口需要根据代理软件配置来填写,这里填入1080。
协议选择SOCKS5
验证需要根据代理软件配置来填写,这里不启用。

点击检查按钮可以对代理进行检查,结果为绿色说明通过。
点击确定按钮保存。

第一次添加代理服务器,会弹出是否用作默认服务器,这里先点否。

接下来配置代理规则。点击第二个按钮(代理规则)

在代理规则中,配置如下两条规则。

第一条规则是代理放行规则,代理程序访问任意目标的任意端口,动作为Direct(即放行)。

默认规则,任意应用程序访问任意目标的任意端口,动作为发送给代理程序。

这样配置好后,即可实现真全局的代理。

东芝Satellite M800安装Sierra 10.12.6成功

配置一览

项目 内容
机型 东芝Satellite M800-T12B
处理器 Intel i3-2370m 双核
主板 Insyde EFI(不支持UEFI)Intel 7 Series Chipset Family
芯片组 英特尔 HM76 (Panther Point)
内存 2 GB 1333 MHz DDR3 + 4 GB 1666 MHz DDR3
独显 AMD Radeon HD 7670m (ven 1002 dev 6840
声卡 Conexant SmartAudio HD CX20590(ven 8086 dev 1e20,解码器 ven 14f1 dev 506e
有线网卡 Atheros AR8151 (ven 1969 dev 1083
无线网卡 Atheros AR9485 无>蓝牙(ven 168c dev 0032)(无解)换成BCM4322 (免驱)

安装日志

其实从10.12刚出正式版,就开始用上了,但是一直懒得写驱动帖,拖到了现在。

最开始10.12的时候,驱动和10.11并没有什么变化,除了AMD显卡的Framebuffer从24位变成了32位

那个时候clover的KextToPatch还不好使,也被clover坑了很多次。所以后来写了个直接给二进制文件打补丁的脚本,从此弃用KextToPatch

后来,尝试修复了关机变重启的问题,也让CPU获得了5档的变频。

升到10.12.4的时候,发现显卡黑屏,没法正常使用,于是退回10.12.3用了好久。

本以为显卡已经没救了,准备停留10.12.3的时候,突然发现有人说用变色龙引导显卡不黑屏。

于是抱着希望继续拖延……直到现在我才弄好变色龙,升级到10.12.5,用上了Night Shift。

不过用变色龙也会有一些问题,没法解决的就只能将就咯,毕竟显卡是好的,其它的都好说。

后来vit9696发布了WhateverGreen,惊喜的发现显卡在10.12.6下能正常休眠唤醒了!

这笔记本已经陪我四年了,也不知道它还能再用多久。

关于显卡

最新的ATI Connectors Data如下:

020000004000000009010000000000001001000200000000 000800000402000000710000000000001102010100000000 100000001000000000010000000000000000020300000000

建议使用的Framebuffer是Ipomoea或者Pondweed。文末会提供一个打补丁的脚本,可以方便进行替换。

同时还需要修改AMDRadeonX4000.kext的Info.plist,把

  1. <dict>
  2. <key>CAIL_DisableDrmdmaPowerGating</key>
  3. <integer>0</integer>
  4. <key>CAIL_DisableDynamicGfxMGPowerGating</key>
  5. <integer>1</integer>
  6. <key>CAIL_DisableGfxCGPowerGating</key>
  7. <integer>0</integer>
  8. <key>CAIL_DisableGmcPowerGating</key>
  9. <integer>1</integer>
  10. <key>CAIL_DisableStaticGfxMGPowerGating</key>
  11. <integer>1</integer>
  12. <key>CAIL_DisableUVDPowerGating</key>
  13. <integer>0</integer>
  14. <key>CAIL_DisableVCEPowerGating</key>
  15. <integer>0</integer>
  16. </dict>
    修改成:
  17. <dict>
  18. <key>CAIL_DisableDrmdmaPowerGating</key>
  19. <integer>1</integer>
  20. <key>CAIL_DisableDynamicGfxMGPowerGating</key>
  21. <integer>1</integer>
  22. <key>CAIL_DisableGfxCGPowerGating</key>
  23. <integer>1</integer>
  24. <key>CAIL_DisableGmcPowerGating</key>
  25. <integer>1</integer>
  26. <key>CAIL_DisableStaticGfxMGPowerGating</key>
  27. <integer>1</integer>
  28. <key>CAIL_DisableUVDPowerGating</key>
  29. <integer>0</integer>
  30. <key>CAIL_DisableVCEPowerGating</key>
  31. <integer>1</integer>
  32. </dict>
    经测试,显示器内屏LVDS,VGA,HDMI均可正常工作由于这个笔记本没有集成显卡,所以睡眠无解

如果使用clover,升级到10.12.6睡眠唤醒正常,可以调节亮度,由于采用了MBP8,1的SMBIOS,所以night shift需要工具开启。

关于声卡

和10.11相比,二进制补丁变了。现在的二进制补丁是:
8419d411 替换成 00000000 8b19d411 替换成 6e50f114

文末会提供一个打补丁的脚本,可以方便进行替换。

有的时候需要修改configdata才能让声卡正常工作。
修改AppleHDA.kext/Contents/Plugins/AppleHDAHardwareConfigDriver.kext/Contents/Info.plist
把其中一个dict改成这样

  1. <dict>
  2. <key>AFGLowPowerState</key>
  3. <data>AwAAAA==</data>
  4. <key>CodecID</key>
  5. <integer>351359086</integer>
  6. <key>ConfigData</key>
  7. <data>AZccUAGXHUABlx4hAZcfAQGnHBABpx2QAaceoQGnHwEB9xxAAfcdAQH3HhcB9x+QAjccEAI3HQECNx6gAjcfkAFHDAI=</data>
  8. <key>FuncGroup</key>
  9. <integer>1</integer>
  10. <key>LayoutID</key>
  11. <integer>12</integer>
  12. </dict>

关于网卡

之前采用的ALXEthernet.kext在10.12下会造成关机出现panic从而变成重启的问题。

替换成AtherosL1cEthernet.kext即可。

无线网卡BCM4322免驱。

关于电源管理和变频

如果使用clover,可以通过如下方法实现正常5档变频。

1. 放入正确的ssdt文件
2. Clover -> Acpi -> Drop Tables -> 加入MCFG和DMAR
3. Clover -> Kernel And Kext Patches -> 勾选Asus AICPUPM(我不知道这步是不是必须的)
4. SMBIOS选择MacBookPro8,1(按道理说8,2和8,3应该都可以)
5. 删除NULLCPUPowerManager.kext,采用原版的电源管理kext

如果使用变色龙,由于无法加入MCFG,只能采取折衷方案,采用异常的电源管理驱动,无法变频。

1. 加入AICPUPM的Patch
2. SMBIOS选择MacBookPro9,1
3. 删除NULLCPUPowerManager.kext,采用原版的电源管理kext

关于DSDT的修改

最近没改过DSDT,具体的修改内容见10.11的帖子。东芝Satellite M800安装El Capitan Developer Beta 1成功

关于电池电量

ACPIBatteryManager.kext。DSDT里不用做任何和电池相关的修改。不得不说这电池电量显示确实省心啊。

驱动分享

http://bbs.pcbeta.com/viewthread-1747205-1-1.html

斐讯K2配置子网使用IPv6的方法

双11在某东上买了“0”元路由器斐讯K2,但是据说该路由器涉嫌获取用户隐私,所以我刷了个Padavan的固件。

刷机教程 http://www.right.com.cn/forum/thread-191833-1-1.html

Padavan固件 http://www.right.com.cn/forum/thread-161324-1-1.html

在正常情况下,我电脑连接寝室墙上的网口是可以获取到校园网IPv6地址的。而刷完固件之后,子网不能使用IPv6,同时WAN口也没有获取到IPv6地址。需要进行一些配置使WAN和LAN获取到IPv6地址。

1.高级设置 -> 外部网络(WAN) -> IPv6设置
按照如图所示配置,LAN口即可获取到IPv6地址,外网连接类型显示为 动态IP。

snip20161118_1

2.高级设置 -> 内部网络(LAN) -> DHCP服务器
配置DHCP为DHCPv4+DHCPv6。

snip20161118_2

3.高级设置 -> 防火墙 -> NetFilter
启动PPPoE Relay

snip20161118_3

4.高级设置 -> 自定义设置 -> 脚本
在“路由器启动后执行”的末尾,添加:

logger "启动ipv6..."
modprobe ip6table_mangle
modprobe ebtable_broute
ebtables -t broute -A BROUTING -p ! ipv6 -j DROP -i eth2.2
brctl addif br0 eth2.2

5.重启路由器,子网也可以使用IPv6了。

snip20161118_4

修改docx, xlsx, pptx文档的创建时间和修改时间

在特殊情况下,我们希望修改一个Word文档的创建时间和修改时间,比如说证明文件没有被篡改。(真是高科技作弊=_=)

在文件属性中,有记录创建时间和修改时间。经过复制等操作时,创建时间会发生变化,所以更改这个时间没有任何意义。而修改时间是在文件真正被编辑时才发生改变。修改时间作为文件属性记录在文件系统中,通过touch命令的方式可以直接修改。

snip20170211_1
对于Word文档,Excel文档,PowerPoint文档而言,文件内部也记录了创建时间,修订次数等信息。这些信息是记录在文件内部的。那么如何修改这个属性呢?

snip20170211_2
首先你得明白一点,docx, xlsx, pptx文档其实都是zip压缩文件。如果是doc, xls, ppt文档则需要先另存为成docx, xlsx, pptx文档,才能用以下方法修改。

下面以docx文件为例,

snip20170211_3
snip20170211_4
  1. 将docx文件的后缀名改为zip,并解压zip文件。
  2. 打开docProps文件夹,打开core.xml文件。
  3. 可以看到,dcterms:created对应的值是创建时间,dcterms:modified对应的值是修改时间,时间的格式是yyyy-MM-ddTHH:mm:ssZ。
  4. 修改完成后,保持原有的文件夹结构,压缩成zip文件,修改后缀名为docx,完成!

以下是一个脚本,输入一个文件夹,会修改文件夹下所有docx文件的创建时间和修改时间为2016年6月的某个日期。

#/bin/sh

year="2016"
month="06"

# 输入文件夹目录
echo "请输入目录(路径中不要有空格): "
read dir

# 检测文件夹下是否有doc文件
cd $dir
for file in `ls`
do
if test -d ${dir}"/"${file}
then
continue
fi
extension=${file##*.}
if [ "$extension" == "doc" ]
then
echo "检测到doc文件,请先转换成docx"
exit -1
fi
done

cd $dir
mkdir origin

for file in `ls`
do
# 跳过输入文件夹中的文件夹
if test -d ${dir}"/"${file}
then
continue
fi

# 获取文件后缀
extension=${file##*.}
if [ "$extension" == "docx" ]
then
echo "处理文件:"${file}"..."

# 获取不含后缀的文件名称
filename=${file%%.*}

# 解压docx文件
cd ${dir}
mkdir ${filename}
unzip ${file} -d ${dir}"/"${filename} > /dev/null
mv ${file} ./origin/

# 获取旧时间
coreXml=`cat ${dir}/${filename}/docProps/core.xml`
docCreateTime=$(expr "'${coreXml}'" : '.*(.*).*')
docModifyTime=$(expr "'${coreXml}'" : '.*(.*).*')

# 生成新时间
((day=$RANDOM % 28 + 1))
((dayafter=$day + 1))
if (( $day < 10 )); then
day="0"${day}
fi
if (( $dayafter < 10 )); then
dayafter="0"${dayafter}
fi
((hour=$RANDOM % 16 + 7))
if (( $hour < 10 )); then
hour="0"${hour}
fi
((hourafter=$RANDOM % 16 + 7))
if (( $hourafter < 10 )); then
hourafter="0"${hourafter}
fi
((miniute=$RANDOM % 60))
if (( $miniute < 10 )); then
miniute="0"${miniute}
fi
((miniuteafter=$RANDOM % 60))
if (( $miniuteafter < 10 )); then
miniuteafter="0"${miniuteafter}
fi
((second=$RANDOM % 60))
if (( $second < 10 )); then
second="0"${second}
fi
((secondeafter=$RANDOM % 60))
if (( $secondeafter < 10 )); then secondeafter="0"${secondeafter} fi docCreateTimeNew=${year}"-"${month}"-"${day}"T"${hour}":"${miniute}":"${second}"Z" docModifyTimeNew=${year}"-"${month}"-"${dayafter}"T"${hourafter}":"${miniuteafter}":"${secondeafter}"Z" echo ${docCreateTime}" -> "${docCreateTimeNew}
echo ${docModifyTime}" -> "${docModifyTimeNew}

# 写入新时间
cat ${dir}/${filename}/docProps/core.xml | sed "s/${docCreateTime}/${docCreateTimeNew}/g" | sed "s/${docModifyTime}/${docModifyTimeNew}/g" > ${dir}/${filename}/docProps/core2.xml
rm -f ${dir}/${filename}/docProps/core.xml
mv ${dir}/${filename}/docProps/core2.xml ${dir}/${filename}/docProps/core.xml

# 还原docx文件
cd ${dir}"/"${filename}
zip -r ${dir}"/"${file} ./* > /dev/null
rm -rf ${dir}"/"${filename}

# 用touch修改文件系统中记录的修改时间
touch -t ${year}${month}${dayafter}${hourafter}${miniuteafter}"."${secondeafter} ${dir}"/"${file}
fi
done

生产实习日记

第1天 2016年7月1日 周五 项目内容

今天,戎烈锋学长通知我到实验室来一趟,说是要讲讲我们正在做的项目和相关的介绍。学长介绍说,我们即将要做的项目是给北京市科委开发的外文文献知识发现系统,其中包括文档检索,知识热点,前沿技术,成果追踪四大块。学长给我演示了一下这个系统的功能,我感觉这个系统的功能还是比较完善,但在一些细节上还做的不够好。学长说以后可能会需要我们在现有的基础上新增一些功能,让整个系统功能更丰富。

然后,学长跟我简单介绍了开发的环境。整个系统是一个网站,前端用的是HTML和JQuery,后端用的JFinal框架和Freemarker,服务器采用Tomcat部署,搜索功能利用了部署在分布式集群上的ElasticSearch。我的主要的工作是修改前端和后端。学长说主要的开发语言是Java,还需要一点HTML的知识。学长建议我这个周末下载一个Eclipse for Java EE并且配置好Java环境,方便以后的学习和使用。同时还要准备一个SSH客户端,在Windows下可以用putty,方便部署工程文件。

晚上回到寝室,我就下载了Eclipse for Java EE,根据网上的教程配置好了Java环境,写了个简单的Demo,可以运行成功。然后下载安装了putty。整个过程还算比较顺利。

第2天 2016年7月4日 周一 组会1

今天,戎烈锋学长通知我今天晚上有组会,并且组会定在每周周一,没有特殊情况都照常进行。

晚上,我按时到达开会地点。开会的有童咏昕老师,我们届的除了我之外有王立彬和符美潇,还有实验室的研究生和博士生。周一晚上的组会比较简短,基本上是个人汇报自己的进度。在会上,我认识了符美潇,他和我一起完成外文文献知识发现系统的项目。

组会结束之后,戎烈锋学长单独和我们开了一个小的会议。考虑到我们用到的是JFinal框架,所以这周我们需要自学JFinal框架的相关内容。戎烈锋学长给我们了一份JFinal的用户手册,供我们参考。同时他还建议我们结合现有的项目一起看用户手册。由于工程代码量较大,戎烈锋学长把代码分成了两个部分,符美潇负责看知识热点部分的代码,我负责看文档检索部分的代码,并在代码中写一些注释。在周末的时候,我们互相交流一下自己的学习成果。这样的话方便我们快速的阅读代码。戎烈锋学长说,这周主要是阅读代码,也可能会给我们安排一些写代码的任务,根据我们的完成情况进行安排。

由于刚开始进行实习,工作还比较轻松,但我也不能因此而懈怠,还是要认真地学习JFinal框架,认真地阅读工程代码,为后面的工作做准备。

第3天 2016年7月5日 周二 JFinal入门

今天,我准备自学一下JFinal框架的用户手册,对JFinal框架有了一定的理解。

通过用户手册的介绍,我知道了JFinal 是基于 Java 语言的极速 WEB + ORM 开发框架。它的特点是开发迅速、代码量少、学习简单、轻量级、易扩展。JFinal使用的是MVC 架构,MVC我在之前学习Objective-C的时候也接触过,这对我现在的学习有很大的帮助。还有一个特点是自动加载修改后的 java 文件,感觉这和php的热修改很相似。JFinal框架还有多视图支持,支持 FreeMarker、JSP、Velocity等,这个项目中采用的视图就是FreeMarker。

用户手册的第一章是快速上手,里面介绍了如何运行一个实例demo。我也根据用户手册上的介绍一步一步进行操作。配置工程,添加文件,都顺利完成了,但在调试项目时,我始终得不到用户手册上的结果,无法顺利启动项目。在网上也没有查找到相关的有用的资料。于是自己重新下载安装了一遍Eclipse for Java EE,发现还是不行。折腾了半天才发现,是因为JFinal框架需要使用自定义的Debug配置,我点击的是Eclipse的Debug按钮,打开的是默认的Debug配置;正确的做法是点击右侧的小三角,选择相应的配置。看来还是得细心,要不然会把时间无谓花在解决这些问题上。

第4天 2016年7月6日 周三 JFinal进阶

今天,我继续阅读了JFinal框架的用户手册,并结合代码能够大致理解JFinal框架的工作过程。

看了用户手册的第二章,写的是JFinalConfig相关的内容。基于 JFinal 的 web 项目需要创建一个继承自JFinalConfig类的子类,该类用于对整个 web 项目进行配置,可以配置常量、访问路由、数据库访问插件、全局拦截器、事件处理等功能。

在看第三章的时候,我就开始似懂非懂了,第四章之后更是不明白这是什么东西。用户手册写的简单而精辟,比较适合熟悉Web开发的人来阅读,对于我这样的新手,还是结合项目学习更加实际一些,于是我打开了项目。

项目中的FLKDConfig.java应该就对应着JFinalConfig。里面配置了开发模式为false;配置了路由,即各个页面对应的Java类;配置了一个Postgre SQL的连接插件;配置了类对应的ActiveRecord。没有配置全局拦截器、事件处理。有了例子,再根据用户手册上的介绍,我仿佛明白了一些JFinal框架的工作过程。

在配置中定义的这些东西正是为了方便后面的使用。配置了路由即指定了某个网页收到请求后,由哪个类的哪个方法进行响应,做出相应的动作,或渲染新的页面。使用数据库就需要配置一个C3p0数据库连接池插件,注明数据库种类,地址,端口,用户名,密码等信息。配置ActiveRecordPlugin则是为了方便数据库的读取,将数据库中的表直接与MVC中的Model类对应,可以采用一系列方法进行简单查询,也可以用DAO对象进行SQL语句查询。

第5天 2016年7月7日 周四 阅读代码-关于MVC

今天,我开始阅读项目的代码。感觉自己已经能够大致理解JFinal框架的工作过程了,工程里用到的Controller,ActiveRecord,Plugin等部分的内容我也大致熟悉了,其它部分的内容不是很理解。询问了一下学长,他说可以先不看那些部分的内容,先看项目代码。等有需要的时候,再来回头看没懂的部分。

打开项目工程文件,就看到工程中有很多个包,看得出来这个项目不像是以前我接触过的那类项目,一个人几天就能完成的。同时,对于这种大型的项目,一个好的代码风格是至关重要的。由于JFinal框架采用的是MVC模型,Model类全部放在了org.mbyd.flkd.model包中,Controller类全部放在了org.mbyd.flkd.controllers包中。在Java文件中没有找到View类的文件,后来一想,这里的View应该是HTML,可能是用Freemarker描述的文件。不出所料,果然在WebRoot/views中找到了相应的Freemarker文件。

由于工程文件众多,我觉得可以先看需要看的内容。我先把项目运行,然后打开检索页面,看到地址栏显示网址为/articles/search,于是我先查看FLKDConfig类中定义的/articles对应的类是ArticleController.class,然后在ArticleController中找到了search()方法。通过阅读方法中的代码,我知道了,先通过函数getPara()获得传入的参数,进行处理与检索后,将需要呈现在页面的元素通过setAttr()方法放置到页面中,通过render()方法指定要显示的页面。找到了查看代码的方法,接下来的阅读代码的工作也会更加顺利。

第6天 2016年7月8日 周五 阅读代码-关于Freemarker

今天,我继续阅读项目的代码,并根据需要写了一些注释。今天重点看了一下Freemarker的部分。在JFinal中有很多的HTML文件,但其实里面的内容是Freemarker。这些页面都不是完整的HTML内容,还有部分内容需要动态生成。昨天在Java代码中看到了setAttr()方法,通过这个方法设置了某些字段的值。在render方法渲染的页面文件中,用${…}标注了对应的字段所处的位置,表示对应的值应当插入到这里。同时,我也看到,项目中有一些奇怪的记号。在网络中搜索后,我发现在Freemarker中也能对字符串、数组等高级数据结构进行一系列处理,比如获得长度,遍历元素,获取元素下标、转换成HTML格式等。项目中原有还有一些地方用到了条件语句,通过学习Freemarker的语法,发现自己也能看懂这些语句表达的意思。结合运行起来的项目,我基本上明白了JFinal的工作流程了。

下午我和符美潇讨论了一下这几天学习的成果。符美潇看的是知识热点部分的代码,他说,热点的控制部分主要在HotspotController里面。发现的部分数据存在服务器中,界面上有一个二级菜单进行显示。追踪的部分主要调用的是搜索的部分,将搜索结果以多样化的方式显示在网页中,包括趋势图像,列表等。此外,热点追踪还有一个导出到word的功能,其实也是利用了freemarker的动态替换的特性,将已有的doc文档模板进行填充,最后提供给用户下载。这次讨论让我收获颇多,对热点部分的内容也有了一定了解。

第7天 2016年7月11日 周一 组会2

今天通知有组会,晚上我便按时去了。今天晚上的组会,大家先汇报了各自的工作进展,然后由师兄做了一个展示,他给我们讲了讲最近读的一篇论文以及自己思考的有哪些地方可以继续深入研究。听完之后也不是很懂,这说明我有些知识不是很明白,以后还需要多多学习。然后,童咏昕老师给我们放了学堂在线MOOC平台的计算几何课程,主讲人是清华大学的邓俊辉老师。在介绍了什么是计算几何之后,他开始向我们讲解凸包的知识,以及如何找到一个凸包。由于时间的原因,在组会上没时间放完整个课程,童咏昕老师建议我们自己课下观看。

组会完后,戎烈锋学长单独跟我们聊了聊阅读的内容以及进度,并给我们布置了新的任务。鉴于我们能够基本了解JFinal框架的内容,戎烈锋学长给我们安排了一些写代码的任务。给符美潇布置的任务是利用EChart插件,给热点部分添加一个云图;给我布置的任务是做一个注册登录的界面,可能需要在数据库中新建一个表,建议我用Navicat进行连接,并且把数据库的连接方式告诉了我。最后,我们还互相留了自己的联系方式,并且建立了一个QQ群,方便互相交流,以及可以直接在线上讨论问题。

第8天 2016年7月12日 周二 数据表设计

今天,我开始给项目增加注册登录的功能。注册登录虽然看起来很简单,但是其实还是需要考虑很多东西。我习惯于先从数据开始考虑。在登录时,用户需要输入用户名和密码,来进行登录的操作;在注册时,用户至少需要输入用户名和密码,可能还需要其它的信息。询问了戎烈锋学长之后,他说现阶段只需要注册的时候输入用户名、密码、手机号和电子邮箱。既然这样,数据库中就应该记录用户id、用户名、密码、手机号和电子邮箱这些信息。除了用户id采用int,其它的属性应该用varchar进行存储。同时,考虑到数据安全问题,我觉得数据库中的密码项应该记录密码的MD5值,这样能够提高安全性,避免了明文传输密码。

于是我下载了Navicat for Postgre SQL,成功连接上了服务器,小心谨慎地创建了一个叫user的表,创建了相应的属性。数据表就这么顺利的创建完成了。

其实仔细想想,我在数据库课程中学会了如何设计一个数据库,在计算机网络安全课程中学会了加密和消息认证的相关内容。这些都是在平常上课学到的东西,真正运用到实际的工程中,也还是需要一番思考。学以致用,才能让自己收获更多。

第9天 2016年7月13日 周三 页面和后端

今天,我继续来做注册登录的功能。昨天已经弄好了数据库,今天就先开始绘制界面吧。好在我之前就自学过一些关于HTML的知识,画起界面应该会得心应手。为了保持整个界面的统一,我就把项目的首页拷贝了一份,直接在上面进行修改,修改标题为登录,添加了一个表单,表单中有两个输入框,一个提交按钮;注册界面也是同理。然后又在注册界面和登录界面上加上了它们的跳转链接。界面很轻松就画好了。

接下来开始编写后端。首先新建一个User类作为数据库访问类,在项目配置文件中增加一个ActiveRecord,将user表映射到User类上。接着添加路由,将/login对应到LoginController类中,将/register对应带RegisterController类中。最后在相应的控制类中,编写相应的代码。在页面加载时,没有传递参数,所以基本只需要渲染页面即可。页面上通过POST方法向控制类传递请求,LoginController中需要判断输入的用户名和密码是否正确,如果正确跳转至原来的首页;RegisterController中需要判断用户名是否在数据表中存在,如果不存在则向数据库新增一个数据项目。经过一番调试,整个界面能够正常运行起来了。

今天开始写代码了,感觉比阅读代码要累一些,但是我觉得这样更有意思。以后写得熟练了,项目做得应该快一些吧。

第10天 2016年7月14日 周四 功能完善

昨天做了注册登录的功能,但是今天想想,还是有不完善的地方。

最开始我预计是在POST表单中只传递密码的MD5,而现在只是简单的传递了密码。所以我需要做一些调整。首先,在POST表单之前,就应该对密码进行处理,获得其MD5值。具体的算法我也记得不是很清楚了,于是在网上找了一个JavaScript版本的计算MD5的函数。这样就可以保证传输时没有明文传输密码。于是我清空了user表,进行了测试,发现没有问题功能正常。

还有一个问题就是我希望能够判断用户现在是否是已经登录了,如果没有登录,访问主页会自动跳转至首页。思考了半天也没想出应该怎么写,和符美潇讨论了一下,他说JFinal用户手册里有写session的内容,应该用session就可以解决。于是我回过头来看了看手册,找到了关于session的内容,经过一番学习和尝试,终于实现了这一功能。

最后我还考虑到了一个问题,作为一个未登录的用户,我可以通过网页上方的快捷导航栏直接点击进入其它页面。所以我需要对注册和登录界面,把上面的导航栏换掉。然而我查看之前写的HTML代码,并没有看到导航栏的部分。仔细阅读代码,发现第一行有一个include _layout.html,我在这个文件中,顺利看到了导航栏的内容。复制了一份之后,修改成不带导航栏的样式,再将注册和登录界面的include指向不带导航栏的样式,就这样完成了。

第11天 2016年7月15日 周五 合并代码

昨天我把注册登录部分做完了,而符美潇还有一些工作没做完。今天给戎烈锋学长看了一下,他说做的还不错。戎烈锋学长要我和符美潇各自写一下自己修改了哪些部分,方便之后代码的合并。我就凭着自己的记忆,大致写了一下。

下午,在看完符美潇那部分的工作后,戎烈锋学长说由我来把代码合并一下。他告诉了我修改了哪些部分,感觉他修改的不多,准备就根据他修改的部分,在我的代码基础上面合并。合并完成发现他的那部分不能运行。检查了一会还是没能发现问题,于是我决定在他的代码的基础上合并我的代码。虽然我修改的部分比较多,但是一旦出错,我也方便很快发现问题。花了些时间,终于合并完成了。

感觉我花了很长时间来进行两个人代码的合并,要是团队里人多起来,人工合并代码就变成一件麻烦的事情了。两个人的代码没有太多重合的部分,合并起来冲突的地方也少,一个团队很多个人,合并代码时肯定会出现冲突,比如重名变量,语句的保留与删除等。其实我觉得,合并代码这件事,不如交给机器去做,既省时,又省力,现有的Github就是一个很好的平台。我向戎烈锋学长提出了建议使用Github,学长也说我们也着手准备把项目放到Github上去。

第12天 2016年7月18日 周一 组会+发布到Github

又是周一,又到了开组会的时间。这次组会还是先进行个人情况的汇报,然后大家一起观看计算几何的课程内容,这次的课程讲的是寻找凸包算法的优化,让复杂度降低了很多。

组会开完之后,戎烈锋学长又单独和我们一起交流了一下项目的工作进展,并给我们分配了新的任务。给符美潇分配的任务是在知识热点部分,增加曲线趋势评价,给我分配的任务是给搜索引擎的部分添加一个新的功能,对输入的搜索词,如果在维基百科中有相关的词条,则显示搜索词的简介,并且给出一个链接到维基百科词条的链接。

同时,学长说从这个星期开始,我们要统一Eclipse版本,并且把项目导入到GIthub上,方便进行代码的版本控制。正好我的Github可以创建私密的仓库,可以防止被外人看到,所以就由我来搭建仓库。

通过在网上搜索如何在Eclipse中使用git,我找到了了一篇博客文章,根据所写的步骤一步步完成了项目代码的上传,并尝试提交了一次修改。我根据这篇文章的内容,结合项目的情况,写了一篇比较详细教程发到了群里,供大家参考。有了git这么高效的工具,终于可以不用费力合并代码了。

第13天 2016年7月19日 周二 维基百科解释

项目上传到Github上后,我便开始制作用维基百科解释搜索词的功能。首先一个初步的打算是先找找有没有维基百科的API可以直接进行使用,如果没有,就只能通过抓取网页的方式进行。

使用Google搜索了一下,顺利找到了维基百科的API,这样事情就方便了很多。打开维基百科的API的首页,发现里面的内容有很多。通过一番查找和学习,终于找到了一个可以实现的方法:首先得到用户输入的搜索词;再调用API,查询词语在哪些词条出现过,API会返回一系列id;程序取出第一个id,也即是最接近该词语的一个词条,再次调用API,获得词条的简介内容和链接。

维基百科的API可以选择返回json格式或者xml格式,在网上找到一个处理json的包,应该比较方便,就学习了一下,直接拿来使用。之前也从来没有写过通过Java发送GET请求,也在网上找到了类似的代码,经过一番学习和修改,可以成功调用API。程序拿到简介内容,就可以显示在网页中了。我修改freemarker文件,加入了一个div标签,里面的内容是由传进来的wikicontent参数指定。在对应的Java函数中,将简介内容和链接通过setAttr()方法设置,就完成了。

这个功能实现起来比较简单,得益于有维基百科的API,以及现成的json处理库。实现的效果还可以,有一些缺点是由于维基百科采用https,两次发送请求会让网页打开速度有所降低,希望在以后的工作中能够解决这个问题。

第14天 2016年7月20日 周三 导出到Word

昨天项目进展比较顺利,所以今天戎烈锋学长给我分配了新的任务:给知识热点界面也加上维基百科解释,同时由于页面中增加了对热点趋势的评价,所以导出到word功能需要进行一定的修改。

给知识热点界面也加上维基百科解释,这个非常简单,直接复用昨天写的代码就好,当然还需要把知识热点界面加上显示维基百科简介的位置。

我之前有阅读过导出功能相关的代码,知道导出到word功能也是利用了freemarker的替换功能,将关键信息填写到对应位置,但是没有深入研究导出模板的内容。打开模板,我看到了一堆乱码一样的东西。在确定自己的打开方式正确以后,我开始仔细看里面的内容。最后发现,最后一行才是真正可读的内容,格式好像是xml。之前我也曾在闲暇时间阅读过关于office文件的格式的文档,现在发现这个格式和我当时阅读的内容很是接近。我把最后一行单独复制出来,手动将其化成有缩进的xml格式。整个文档的布局也就很清晰了。然后根据我的需要,插入了两个xml块,一个用来显示维基百科的内容和链接,另一个用来显示热点趋势评价,还是在Java中通过setAttr()方法传递内容。最后,我去掉换行符和制表符,整理成一行,替换原来的部分,成功完成今天的任务。

今天的任务需要一定的思考。也得益于我之前的阅读,让我能顺利完成这个功能。

第15天 2016年7月21日 周四 相关词汇优化

最近我们在测试搜索部分的各个功能,看有什么明显的问题。之前发现在搜索时,程序推荐的相关词汇有的时候会很奇怪,比如搜索deep learning,会出现一些没有意义的词组。今天戎烈锋学长给我的任务是优化相关词汇的结果。

之前阅读代码时,我主要看的是关于界面的代码,关于搜索的代码看得并不多。正好借这个机会,看看搜索的内部是怎么工作的,并且解决这个问题。我通过反推的方式,从存储结果的变量,找到修改这个变量的语句,进而找到了调用搜索的方法。所有关于搜索的内容都在SearchService类中。其实关于搜索的代码并不多,都是直接调用elasticsearch,并处理返回的结果。而相关词汇这部分,实际上做的非常简单:在数据库中找到返回结果的文章的关键词,进行词频统计,返回词频最高的5个。所以说出现的没有意义的词组也是关键词?我将所有关键词都打印了出来,发现果然有没有意义的关键词词组。我把这一情况告诉学长后,学长说数据库中有些数据确实有些问题,没有做数据清洗,让我试试能不能先通过一些处理先过滤掉这些词语。

我看了看关键词表,发现了一个特点,出现没有意义的关键词词组的论文,一般都有很多个关键词;而我们知道,一般论文的关键词不会超过5个。于是我在统计词频的地方加了一个判断,如果关键词超过5个,则该论文的关键词全部过滤。做完之后发现结果还不错。虽然改动不多,但是今天确实思考了很多。

第16天 2016年7月22日 周五 高级检索-多词检索

之前搜索部分有个功能,如果用户输入的是中文,在搜索时,会自动调用bing翻译API翻译成英文,再进行搜索。学长说在这种情况下,需要改成返回多个词,供用户选择采用哪个翻译结果,或者跳转到高级搜索界面进行多词检索。

既然要得到多个翻译结果,我先去bing API官网找了找有没有返回多个结果的API,结果网站上明确写着,中文不支持返回多词。只能看看别的翻译API了。终于,我在有道词典API的官网上,发现可以返回多个网络释义,也算是多个翻译结果吧。于是写了一个调用有道API的方法,处理返回的json串后,成功拿到了翻译结果。修改freemarker,让网页显示多个结果,并添加了一个按钮,跳转到高级检索界面。

然而高级检索界面还不支持传入参数,于是我又要对高级检索界面进行修改,通过getPara()方法接收参数,并显示到界面上。

终于做完了之后,我尝试将最近的代码提交到Github上去。结果发现出现了错误。根据错误信息,我先删除了jfinal.log的日志文件,再次提交。然后提示代码有冲突,我在网上搜索了一下,解决方法是这个时候只能手动合并代码。好在我的代码和符美潇的代码重合的地方并不多,根据git的指示简单改改就好了。于是顺利提交上了代码。

第17天 2016年7月25日 周一 科委开会+分配任务

今天童咏昕老师、戎烈锋学长、符美潇、我、还有两个学长,一起去北京市科委参加需求讨论会。之前也有这样的会议,由于我和符美潇对这个项目的深入,所以让我们也去听听需求方的意见,这样才能更好的完善我们的项目。

因为马上我们就要准备前沿技术部分的制作了,所以这次需求讨论会的主要目标是讨论清楚前沿技术这部分有哪些需求。戎烈锋学长先回顾了一下之前的项目进展,并进行了项目的演示。然后,我们就讨论了前沿技术的一些需求。我们的初步想法是,前沿技术分为发现和跟踪两个部分。发现部分是采用自然语言处理、数据挖掘、统计学等方法发现并查找出文献数据中蕴含的前沿技术;跟踪部分是用户输入一个需要跟踪的主题词,系统提示10个关键词,用户可以选择其中的若干个词或者手动输入词语,进行辅助检索。需求方觉得可行,并提出增加可视化展示的部分。

之后,我们又对前沿技术的定义进行了讨论。前沿技术应该具有以下特性:前瞻性,即未来有成为热点的潜力;先导性,即技术下位词很少或没有;探索性:研究热度呈上升趋势,但尚未到达拐点;高端性:概念由顶级会议、期刊或高等大学等知识聚集地发现和提出。明白了这些,对我们后续的工作有了很大的帮助。

第18天 2016年7月26日 周二 TF-IDF方法

今天,学长让我自学一下TF-IDF方法,说是为做前沿技术发现做准备。

通过相关资料的查阅,我知道了TF-IDF是一种用于资讯检索与资讯探勘的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜寻引擎应用,作为文件与用户查询之间相关程度的度量或评级。

在一份给定的文件里,词频TF指的是某一个给定的词语在该文件中出现的次数。逆向文件频率IDF是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。

通过学习TF-IDF统计方法,可以找出文章里的易于区分的词汇,这些词语也很有可能是前沿科技词汇。总之,这个方法是否有效,需要到实际代码的编写中,才能知道效果好坏。

第19天 2016年7月27日 周三 LDA主题模型

今天,学长让我自学一下LDA主题模型,由于知识热点也采用这个方法,所以推荐我先去看看。

通过在互联网上搜索,我知道了LDA是一种文档主题生成模型,也称为一个三层贝叶斯概率模型,包含词、主题和文档三层结构。一篇文章的每个词都是通过“以一定概率选择了某个主题,并从这个主题中以一定概率选择某个词语”这样一个过程得到。文档到主题服从多项式分布,主题到词服从多项式分布。

LDA是一种非监督机器学习技术,可以用来识别大规模文档集或语料库中潜藏的主题信息。它采用了词袋的方法,这种方法将每一篇文档视为一个词频向量,从而将文本信息转化为了易于建模的数字信息。每一篇文档代表了一些主题所构成的一个概率分布,而每一个主题又代表了很多单词所构成的一个概率分布。

对于语料库中的每篇文档,LDA定义了如下生成过程:

1.对每一篇文档,从主题分布中抽取一个主题;

2.从上述被抽到的主题所对应的单词分布中抽取一个单词;

3.重复上述过程直至遍历文档中的每一个单词。

在推导和计算部分有很多公式,没有看得特别懂。但是,算法的过程基本上理解了,能够在需要的时候写出来。

第20天 2016年7月28日 周四 科技热点代码学习

今天,我和符美潇一起看了科技热点发现部分的代码。

根据学长写的指导文档和代码,我们明白了如何发现科技热点:

首先需要从数据库导出数据,以年为单位,在flkd项目seeds.rb中修改year这个变量。在flkd目录下运行rake db:seed 就能把对应年份的数据保存在设置的目录下。

然后是得到元数据,需要抽出生物医药和电子信息两个领域的数据,在data目录下g.rb脚本完成这一任务,最后分别存到bm(biomedicine)和ei(electronics)两个目录下。

接着将bm和ei目录拷贝到inspiration/ds中,接下来的工作都在inspiration下进行,这是ruby开发的一个命令行工具,包括数据预处理、跑LDA、主题命名,收集热点和新技术等。ruby process.rb 可以查看帮助信息。重要的参数是主题个数”-p”设置。process.rb是比较重要的脚本,它整合了所有的处理流程,如果某个阶段跑失败了,不必从头跑一篇,比较取巧的做法是把这个阶段之前的阶段注释掉,ruby用”=begin” “=end”注释块。

最终在bm文件夹下得到这些文本文件,其中201401.t1 表示一级热点,201401.t2表示二级热点,201401.aut 、201401.org对应的学者和机构 201401.nt表示新技术名词,201401.naut、 201401.norg为对应的学者和机构,将它们导入对应的数据表,就可以了。在数据表里人工筛选一下,一二级热点和新技术名词,明显不合适的就删除掉。

第21天 2016年7月29日 周五 前沿技术代码编写

今天我着手开始写前沿技术发现模块。之前和戎烈锋学长和童咏昕老师讨论了一下具体的实现,初步定的方法是先从数据库中取出最新一年的顶级期刊和论文,通过综合统计关键词和标题里的词语的热点走势情况,进行初步的筛选。然后对剩下的词语调用维基百科API,获得对应词条的编辑情况,再根据维基百科修订情况的数据,判断词语是否不是前沿技术。最后,再人工对结果进行筛选,得到最终的结果。

为了提高搜索速度,我决定直接调用elasticsearch从数据库中取出期刊和论文信息。关于如何从标题中分离出关键词,我的想法是根据停用词表对标题进行切割,得到的串应该就是关键词了。同时,关键词还存在单复数的问题,我从网上找了一个词元化的工具,顺利解决了这一问题。维基百科API我已经很熟悉了,再查查API首页,我也顺利找到了获取修订记录的方法。

整个过程看起来虽然容易,但实际做起来却不那么简单。工作量也很大,周五周六两天,我都在编写和调试这个代码。终于功夫不负有心人,我终于把它写完了。尝试跑了一遍结果,效果一般,仍然还需要进一步优化。

第22天 2016年8月1日 周一 分配任务+优化

今天,戎烈锋学长通知说没有组会。上午,我尝试对前沿技术发现模块进行优化。之前对标题的分词,采用的是用停用词进行分割。但是实际上,这样很有可能会得到形容词+名词的组合。为了解决这一问题,我一步步缩减单词长度,每次删去最左的一个词,直至这个词为空或者在数据库中被找到。这样提高了数据的准确性。

同时,我还观察了一下维基百科返回的数据,修订数据增加的内容用正数表示,删减的内容用负数表示。代码中统计年修订量的时候,直接相加了。正确的做法应该是取绝对值相加。这么做之后,也让结果准确了一些。

晚上,戎烈锋学长和我们开了一个小会。在交流了工作进度之后,戎烈锋学长分配给我这周的任务:一是制作前沿技术的发现的界面,二是制作前沿技术的追踪的界面,三是如果还有时间,优化一下之前的发现模块。

鉴于发现模块的结果还不是很准确,也许在后期需要更换算法,所以我决定先制作科技热点的发现和追踪的界面,先把界面画好,这样也不会显得没有进度。

第23天 2016年8月2日 周二 前沿技术发现界面

今天我来开始做前沿技术发现界面。其实前沿技术发现的界面比较简单,只需要把词语从数据库中取出并显示出来。这一部分可以复用科技热点发现的内容。

有所不同的是,这一部分需要能以分页的形式显示每一期的关键词。我觉得这个时候需要对数据库进行一些重新设计。记录前沿技术的数据库中没有记录是哪一年第几期,同时还需要一个总期数,以方便更好的进行处理。

所以,我先对数据库进行了一个备份,然后给数据库进行了一些修改。在原有的基础上,把hot_year换成了记录年份和期数的nt_year和nt_year_vol,还增加了一个总期数nt_vol。然后把原来的词语全部定义为2016年第1期和第2期。在获取某一页的数据时,会先从数据库获取总期数,计算该页对应的是第几期,然后从数据库获取这一期对应的信息,并显示到页面上。

另外,还要做一个上一期,下一期的链接。需要在显示的时候,知道当前要显示的是第几期,这样可以判断是否有上一期或下一期,也可以轻松更换链接的内容。今天的内容比较简单,所以很快就制作完成了。

第24天 2016年8月3日 周三 前沿技术跟踪界面1

今天我开始做前沿技术的跟踪部分的界面。需要做一个搜索框,在点击检索之后,找到相关的词汇,用户可以选择,来进行多个词的辅助搜索;用户也可以自己添加辅助搜索的词汇。

真正让我思考了一会的是如何制作自定义添加词语的模块。初步的想法是另外单独再做一个列表来显示。但是这样会使界面看起来很臃肿。所以后来我在想把自定义的词语和搜索找到的相关词汇放在同一个table里,这样比较简洁直观。那么如何动态添加表格呢?这里不能用freemarker的语法,因为它只在渲染界面的时候起作用。而添加的动作发生在网页加载完成的时候,所以我只能通过JavaScript或者jQuery来实现。这个时候需要在函数中动态改变HTML。经过查找,我发现jQuery中一个叫append()的函数可以在元素的末尾添加HTML内容,这正是我所需要的。我只需要判断新增的格子的位置,然后添加到正确的<tr></tr>或<td></td>中,这样就实现了功能。

还有一个改进是需要在一个图表中显示多个结果的曲线,我去EChart官网查询了相关的参数,直接修改参数就实现了这个功能。今天的工作量也不大,但是还是要认真仔细的做,这样用户界面才不会有太多问题。

第25天 2016年8月4日 周四 前沿技术跟踪界面2

今天我继续做前沿技术的跟踪部分的界面,争取在今天把大部分功能都实现了。今天要做的是显示关键词的相关领域、相关作者、相关机构的信息。之前知识热点部分是从数据库中进行检索,得到相关的信息。而对于前沿科技,数据库中相关的论文数量较少,得到的相关领域、相关作者、相关机构的信息不是很准确,所以我想从bing学术搜索中获得相关数据。

在网上搜寻了半天,发现bing学术搜索没有提供相关的API,所以只能自己通过抓取的方式获得数据。我先通过HTTP请求,获得整个网页的信息,存储到一个字符串中。接下来,通过网页的特征点,定位到截取数据的位置,在这里,我通过一个<span>标签就可以确定位置。然后我发现,数据全部在一个列表里,于是写了一个方法用于处理和获取<li>标签里的内容,由此,可以拿到数据。虽然看起来简单,但是涉及到字符串处理,写起来也是很费劲的。通过细心的调试和测试,终于能从网页爬到正确的数据。

接下来把数据显示到我们的网页上,我也做过很多遍这种类似的工作了。这次自然比以前更加熟练:在freemarker中用<#list>显示相关内容,在Java代码中用setAttr()方法设置数据。

今天的感受是,用Java做一个爬虫确实是很累,要观察HTML代码,还要在编码的时候非常细心。但也正是严谨细致的编码才能让项目做得更加完美。

试用Windows 10周年版的Linux子系统

Windows 10 周年版1607在8月正式推送。由于我之前的Windows 10系统升级总是出错,同时系统盘也没有重要的数据,于是准备直接用完整镜像进行全新安装。

安装过程还是一如既往的平淡无奇,但是快要安装完成的时候,安装程序却开始吟诗了…

海内存知己,天涯若比邻
请稍等…

有朋自远方来,不亦乐乎
欢迎您升级操作系统

青,取之于蓝而青于蓝;冰,水为之而寒于水
Windows将稳步更新,为你的网络之旅保驾护航

休将故人思故国,且将新火试新茶
敬情开始吧

倒是添了几分人(zhong)文(er)气息…

装好系统,就可以开启Linux子系统了。

1. 在传统模式的控制面板中,点击“启用或关闭Windows功能”,在弹出的窗口中勾选“适用于Linux的Windows子系统Beta” 。/这个翻译好像哪里不对…/ 更改完成后先不要重启。

%e6%8d%95%e8%8e%b71

2. 在“设置”应用中,选择“更新与安全”,在左侧点击“针对开发人员”,在右侧点击开启“开发人员模式”。更改完成后重启电脑。

%e6%8d%95%e8%8e%b72

3. 打开命令提示符或者Powershell,输入bash进行下载和安装。

4. 根据上面的提示信息,可以发现安装的是Ubuntu子系统。所以apt-get是可以用的。可以从源安装各种各样的程序。

%e6%8d%95%e8%8e%b73
看来以后在Windows中可以抛弃难用的命令提示符(cmd),转而用bash。讨厌Windows的理由又少了一条呢→_→

将Telegram加入ShadowsocksX的自动代理模式

由于众所周知的原因,Telegram在国内不能直接访问。最近遇到个奇怪的问题,在macOS中,ShadowsocksX使用自动代理模式的时候Telegram不能联网,但使用全局模式的时候一切正常。究其原因,应该是自动代理模式中的PAC列表没有加入Telegram的服务器。

所以只需要找到Telegram的服务器地址,并添加到PAC列表中。

先开ShadowsocksX全局模式,登录Telegram。

snip20170211_1
然后打开抓包软件,这里以Wireshark为例。设置过滤规则为http,关闭ShadowsocksX,打开Telegram,让Wireshark刷一会。

观察抓到的报文,有很多类似的POST请求。我们记下所有的目的地址。

snip20170211_2
我这里的地址有:

149.154.175.100
149.154.175.50
91.108.56.194
91.108.56.165
149.154.167.51

在不同的地区,可能会连接到不同的服务器,所以建议自己通过抓包的方式获得服务器地址。

编辑PAC列表,按照格式填入gfwlist.js中,注意输入的格式,不要忘记英文状态的引号和逗号。

snip20170211_3
然后关闭ShadowsocksX,然后重新打开,开启自动代理模式,Telegram也能正常使用了。

编程之美2016复赛题目思路

编程之美2016复赛题目可参见此处

简单看了一下题目,发现题目描述中定义了很多符号,不是很清晰,于是先看了看实体属性的定义。官方文档里定义了很多属性,这里我们只需要关注题目中出现的属性即可(以下加粗部分)。

符号与解释

  • Id:实体的id号
  • Ti:论文标题
  • Y:论文发表年份
  • D:论文发表日期
  • CC:被引用次数
  • AA.AuN:作者姓名
  • AA.AuId:作者的id号
  • AA.AfN:单位的名称
  • AA.AfId:单位的id号
  • F.FN:研究领域的名称
  • F.FId:研究领域的id号
  • J.JN:期刊名称
  • J.JId:期刊的id号
  • C.CN:会议名称
  • C.CId:会议的id号
  • RId:引用的论文的id号
  • W:关键字
  • E:扩展的元数据
    显然,可以把这里的“实体”理解为论文,所以实体的id就是论文的id。看起来,论文是在哪个会议/哪个期刊发表的,研究领域是什么,作者是谁,引用论文,这些数据都有定义。

详细数据结构

上面的实体属性定义,看起来不是很复杂。结果后来我随便拖了些数据来看,发现有些地方还是有坑的。用struct的方式大致描述一下数据结构(只涉及重要的属性)

struct entity{
int Id;
List RId;
struct Field {
string FN;
int FId;
};
List F;
struct Conference {
string CN;
int CId;
} optional C;
struct Journal {
string JN;
int JId;
} optional J;
struct AuthorOrAffiliation {
string AuN;
int AuId;
string optional AfN;
int optional AfId;
}
List AA;
}

所有标注为optional的字段都是可以没有的。AuId和AfId可能是一对多的关系,即一个作者可能对应多个单位,也可以一个单位也不对应(其实也是很符合实际情况的)。

查询方法

Academic Knowledge API提供了三个查询类型Interpret(翻译),evaluate(计算),calchistogram(统计直方图)。可能需要用到的就只有evaluate。

往下面这个网址发送GET请求,就会返回一个结果,为JSON字符串。

http://oxfordhk.azure-api.net/academic/v1.0/evaluate?{expression}&subscription-key=f7cc29509a8443c5b3a5e56b0e38b5a6

其中,{expression}需要被替换成一个查询表达式,形式大概是这样:

expr=Composite(AA.AfId=82880672)&count=10000&attributes=Id,AA.AuId,AA.AfId

意思是,查询这样的论文:作者列表中包含id为82880672对应的单位(其实对应的就是Beihang University),返回的最大数量为10000个,返回的属性有:论文id,作者id,单位id。

写成SQL语句的形式就是这样:(由于MAG不是关系型数据库,所以写成这种形式不是很恰当)

SELECT TOP 10000 Id, AA.AuId, AA.AfId FROM MAG WHERE AA.AfId=82880672

问题分析

问题是给定MAG中一对实体的标识符,能够找到所有连接它们的1跳,2跳,3跳路径。而且,给定的实体的标识符是[Id, Id], [Id, AA.AuId], [AA.AuId, Id], [AA.AuId, AA.AuId]。

仔细想想,情况也不是很复杂。按输入分类讨论如下:

输入为[Id, Id],可能的路径:
Id——Id
Id——Id——Id
Id——F.FId——Id
Id——C.CId——Id
Id——J.JId——Id
Id——AA.AuId——Id
Id——Id——Id——Id
Id——Id——F.FId——Id
Id——Id——C.CId——Id
Id——Id——J.JId——Id
Id——Id——AA.AuId——Id
Id——F.FId——Id——Id
Id——C.CId——Id——Id
Id——J.JId——Id——Id
Id——AA.AuId——Id——Id

输入为[AA.AuId, AA.AuId],可能的路径:
AA.AuId——Id——AA.AuId
AA.AuId——AA.AfId——AA.AuId
AA.AuId——Id——Id——AA.AuId

输入为[Id, AA.AuId],可能的路径:
Id——AA.AuId
Id——Id——AA.AuId
Id——Id——Id——AA.AuId
Id——F.FId——Id——AA.AuId
Id——C.CId——Id——AA.AuId
Id——J.JId——Id——AA.AuId
Id——AA.AuId——Id——AA.AuId
Id——AA.AuId——AA.AfId——AA.AuId

输入为[AA.AuId, Id],可能的路径:
AA.AuId——Id
AA.AuId——Id——Id
AA.AuId——Id——Id——Id
AA.AuId——Id——F.FId——Id
AA.AuId——Id——C.CId——Id
AA.AuId——Id——J.JId——Id
AA.AuId——Id——AA.AuId——Id
AA.AuId——AA.AfId——AA.AuId——Id

由于数据量巨大,所以两跳和三跳的情况只能从两头往中间查找。