英文中连续字母频次统计

这只是个无聊的小把戏。

最近又看到关于qwerty键盘布局的讨论,有提到好的键盘布局标准之一:把连续的按键分散在左右两手上,这样第一只手击键的同时,第二只手可以提前做好准备,提高效率。

于是从网上找了一批古典英文小说的txt电子版,写了个傻傻的脚本,统计其中连续两个字母的频次(忽略大小写),其中频度最高的前30项如下表。这30项共计占总频次的43.3%.

如前述,“是否可以把连续击键分散在两只手上”,即表格最后一列。从结果上看,分散与否各15项,所以qwerty在这个标准上优势似乎并不明显。

当然,这并不能证明qwerty键盘是不合理的;毕竟评估因素非常多。

序号 字母组 频次 占比 双手分散*
1 he 390103 4.04% Y
2 th 366573 3.80% Y
3 in 234621 2.43%
4 er 221732 2.30%
5 an 205618 2.13% Y
6 re 163779 1.70%
7 nd 148125 1.53% Y
8 ha 135536 1.40% Y
9 ou 133357 1.38%
10 ed 132822 1.38%
11 on 132233 1.37%
12 at 128740 1.33%
13 en 124127 1.29% Y
14 ng 117178 1.21% Y
15 hi 116461 1.21%
16 to 112860 1.17% Y
17 it 108636 1.13% Y
18 is 103327 1.07% Y
19 as 102013 1.06%
20 ar 99665 1.03%
21 es 98518 1.02%
22 te 96810 1.00%
23 or 94932 0.98% Y
24 le 92578 0.96% Y
25 st 92218 0.96%
26 of 91493 0.95% Y
27 se 87755 0.91%
28 ve 84620 0.88%
29 me 80898 0.84% Y
30 ea 77540 0.80%

使用的小说素材有:傲慢与偏见, 安娜卡列尼娜, 巴黎圣母院, 悲惨世界, 格列佛游记, 白衣女人, 飘, 呼啸山庄, 尤利西斯, 德伯家的苔丝, 爱玛, 白鲸, 黑骏马。

傻傻的脚本。脚本本身区分大小写的,整理统计结果时在excel里合并了大小写。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

fp=open('english_novels_all.txt')
raw=fp.read()
fp.close()

ch='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
for i in range(len(raw)-2):
  if raw[i] not in ch or raw[i+1] not in ch:
    continue
  k=raw[i:i+2]    # k=raw[i:i+2].lower()
  if k in st:
    st[k]+=1
  else:
    st[k]=1

fp=open('st_out.txt','w+')
for k in st:
  fp.write('%s %s\r\n'%(k,st[k]))

fp.close()

Adblock Plus 的个人设置

FireFox 扩展Adblock Plus

使用原则:只阻止过于反感的广告,其他广告一律放行;亦即,自带的过滤列表一律不激活。

“Adblock Plus 设置 - 高级” 设置页

创建和编辑您的过滤列表

//pos.baidu.com/
//static.mediav.com/js/

算法收集

这是算法收集板

最大公共子序列(Longest CommonSubstring, LCS)Python版

def lcs(x, y):
    matrix = [''] * (len(x) + 1)
    for index_x in range(len(matrix)):
        matrix[index_x] = [''] * (len(y) + 1)
    for index_x in range(1,len(x) + 1):
        for index_y in range(1,len(y) + 1):
            if x[index_x - 1] == y[index_y - 1]:#这里利用属性一
                matrix[index_x][index_y] = matrix[index_x - 1][index_y - 1] + x[index_x - 1]
            elif len(matrix[index_x][index_y - 1]) > len(matrix[index_x -1][index_y]):#这里和下面利用属性二
                matrix[index_x][index_y] = matrix[index_x][index_y - 1]
            else:
                matrix[index_x][index_y] = matrix[index_x - 1][index_y]
    return matrix[len(x)][len(y)]

来源 https://www.jianshu.com/p/a1806f2fef52

娱乐一下:测试WinRAR与7-Zip压缩比(使用wordpress源码文件及相机照片)

测试环境

软件:采用当前(2019/03/28)的最新x64位版,软件都从官方下载。

  • WinRAR:  v5.70简体中文版      winrar-x64-570sc.exe
  • 7-Zip: 7-Zip 19.00 (2019-02-21) 7z1900-x64.msi

测试素材: 1) WordPress源码解压缩后的文件夹,是 wordpress-3.5.2-zh_CN.zip 比较老,没有特别原因,只是电脑上正好有这个zip包而已。  2) 一批数码相机照片,计400多张,370M.

测试项目

使用WinRAR与7-Zip分别压缩,含标准压缩、最大压缩,压缩成各家独特格式、zip格式,然后比较压缩包大小。

结果

$ ls -l --block-size=K
total 1566840K
drwxrwx---+ 1 feng None      0K Mar 28 17:50 photos

-rwxrwx---+ 1 feng None 377416K Mar 28 17:52 photos_7z19_stand.7z
-rwxrwx---+ 1 feng None 379106K Mar 28 17:51 photos_winrar57_stand.rar

-rwxrwx---+ 1 feng None 377081K Mar 28 17:55 photos_7z19_max.7z
-rwxrwx---+ 1 feng None 379106K Mar 28 17:53 photos_winrar57_max.rar

drwxrwx---+ 1 feng None      0K Jun 22  2013 wordpress

-rwxrwx---+ 1 feng None   6199K Mar 28 17:31 wordpress_winrar57_stand.rar
-rwxrwx---+ 1 feng None   4996K Mar 28 17:19 wordpress_7z19_stand.7z

-rwxrwx---+ 1 feng None   6196K Mar 28 17:45 wordpress_winrar57_max.rar
-rwxrwx---+ 1 feng None   6374K Mar 28 17:58 wordpress_winrar57_max.zip

-rwxrwx---+ 1 feng None   6381K Mar 28 17:58 wordpress_winrar57_stand.zip
-rwxrwx---+ 1 feng None   6337K Mar 28 18:00 wordpress_7z19_stand.zip

-rwxrwx---+ 1 feng None   4969K Mar 28 18:02 wordpress_7z19_max.7z
-rwxrwx---+ 1 feng None   6292K Mar 28 17:57 wordpress_7z19_max.zip

-rwxrwx---+ 1 feng None   6172K Mar 28 17:19 wordpress_winrar371_stand.rar

[注] 为方便对照,调整了条目的次序,并非ls原始次序。

简评

从结果上看,所有测试项目都是7-Zip胜出,这在意料之中。但同样压缩成zip格式,7-Zip都好于WinRAR.

甚至,winrar 3.71版比 5.70版压缩率还更高!实在让人大跌眼镜!

存储过程实现MySQL表数据变更记录追踪(含历史快照)

直接上代码及调用示例,调用示例:

假设有一张表user_status_in_playground,存储了用户在每个游戏场的统计数据,如总积分 、玩过次数、最近玩的时间、最近次得分。逻辑上 user_id + playground_id 是个惟一键,我们希望按照这个惟一键追踪其它字段的数据变更记录(建数据快照)。需要快照时执行一次存储过程,快照数据存到表archive_user_status_in_playground里。其中归档表只需要指定表名即可,将由存储过程自动创建。

SET @data_source='source_db.`user_status_in_playground`';
SET @archive_target='archive_db.archive_user_status_in_playground';
SET @identify_columns='user_id,playground_id';
SET @track_columns='score,paly_times,last_play_time,last_score';

call revision_archive(@data_source, @archive_target, @identify_columns, @track_columns, @dmesg);

如果我想按指定的时间点倒出当时的完整快照,其中revision是时间点(年月日时分秒),快照表也是自动创建的,指定表名即可。

SET @archive_location='archive_db.archive_user_status_in_playground';
SET @dump_location='`tmp`.`user_status_in_playground__dump`';
SET @identify_columns='user_id,playground_id';
SET @track_columns='score,paly_times,last_play_time,last_score';
SET @revision=20190603100255;

call revision_dump(@archive_location, @dump_location, @identify_columns, @track_columns, @revision, @dmesg);

(原始存储过程见附件)

db_revision_archive.mysql.sql

MySQL语句实现字段拆分成多行

问题简述

如上图,拆分字段为多行,只使用MySQL本身功能,不借助其它工具。

下页结合实例详述原理,最后是完整的拆分语句。实际使用中,只需把最后一节的语句当作模板,修改字段名、表名即可。

核心知识点

    • 表连接关系 JOIN... ON 中使用小于号,而非通常用的等号
    • MySQL函数 SUBSTRING_INDEX() 详参MariaDB文档MySQL文档

示例数据表article_tags

文章标签表,文章编号aid,及文章标签`tags` ;即待拆分tags数据,分隔符为 " | ",竖线及前后空格共计三个字符。

CREATE TABLE `article_tags` (
  `aid` int(11) NOT NULL COMMENT 'article id',
  `tags` varchar(255) NOT NULL DEFAULT '',
  KEY `aid` (`aid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

辅助表sp_idx

拆分过程需要一张辅助表,包含了正整数的序列的一列数据。可按下页语句构造1024行。

CREATE TABLE `sp_idx` (
 `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `x` bit(1) NOT NULL DEFAULT b'0',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='拆分辅助表(正整数序列)';
INSERT into `sp_idx`(`id`,`x`) values(1,0);
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;

计算每条记录拆分条数

计算每行要拆分成的行数据,即有多少条数据通过分隔符拼接在一起。即计算字符串中删除分隔符后长度的减少量,即是 分隔符的个数*分隔符长度。分隔符个数+1 即是拆分条数。

这里使用char_length()计算长度;因为分隔符可能包含ASCII码,故不用length()。

SELECT r.`aid`, `tags`,
 (char_length(`tags`)-char_length(REPLACE(`tags`,' | ',''))) DIV char_length(' | ') + 1 as kw_count 
FROM `article_tags` r ;

联入入辅助表

按上步计算出的kw_count,联入相应条数的序列数据,从1 到 kw_count,序列数据从辅助表取。联表关系使用 < ,而不是通常使用的等于。

SELECT r.`aid`, `tags`
 ,x.`id`
FROM `article_tags` r INNER JOIN `sp_idx` x 
ON x.`id` <= (char_length(tags)-char_length(REPLACE(tags,' | ',''))) DIV char_length(' | ') + 1
;

按序号取子串

结果记录集最后一列id,事实上就是原始字符的排序号。每条记录按该序号截取子串即是结果了。这里使用 SUBSTRING_INDEX() 函数,截取前N段或后N段子串。

第一步,如下,截前N段子串的结果,再截最后一段即是每行所需结果。

SELECT r.`aid`, `tags`
 ,x.`id`, substring_index(`tags`,' | ',x.`id`) as step_1
 ,substring_index(substring_index(`tags`,' | ',x.`id`),' | ',-1) as step_2
FROM `article_tags` r
INNER JOIN `sp_idx` x 
on x.`id` <= (char_length(tags)-char_length(REPLACE(tags,' | ',''))) DIV char_length(' | ') + 1
;

总结整个过程

SELECT r.`aid`, `tags`
,x.`id` as idx, substring_index(substring_index(`tags`,' | ',x.`id`),' | ',-1) as piece
FROM `article_tags` r INNER JOIN `sp_idx` x 
ON x.`id` <= (char_length(`tags`)-char_length(REPLACE(`tags`,' | ',''))) DIV char_length(' | ') + 1 
WHERE r.`tags`!='' HAVING `piece` !='';

该语句,可以当作模板使用,其中:

  • `tags` 替换为待拆分字段(共4处),' | ' 为分隔字符串(共4处);
  • `piece`为拆分结果列,`idx` 为 `piece`在原始`tags`中的序号;
  • 比前面步骤增加了WHERE及HAVING子句,用来排除原表tags为空字符串的行、及拆分piece为空字符串的行。

辅助表`sp_idx`需要事先建好,事实上只要从是从1开始的整数序列即可;如果待拆分子串数超过1024,则要扩充本表,多跑几次该句即可:INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;

如果实在不方便建辅助表,可以使用`mysql`. `help_topic`表的help_topic_id字段(不推荐)。要能保证其完整性,而且最大拆分子串数不大于`help_topic`的总行数。而且还要多加个where条件 help_topic_id>0,否则会拆分出多余的空串行,因为help_topic_id字段是从0开始编号的。

附件:

示例数据及SQL脚本 split_field_to_lines_in_mysql.zip

amazon lightsail使用记录及主要配置项(CentOS 7实例)

从朋友处得知amazon的lightsail,有东京机房而且网络质量还不错,价格还公道(512M最低配3.5刀),于是想换掉已经使用了7年的linode,正在用的早已绝版的东京1也4年了,网络抽风的频率有点高。试用过一段时间的东京2,网络质量确实有待改善。好几年前试用过aws的1年免费,啥都没干而浪费掉了,那时还跟linode一样是20刀1月。

英文国际版 https://aws.amazon.com/lightsail/

中国优化版 https://amazonaws-china.com/cn/lightsail/

网上有人说lightsail中文页面价格比英文高,可能是以前吧,现在并不是,所以跟随aws的提示进优化过的中文版,而且英文版可能还要科学上网(否则页面加载不完整)。

直接购买,目前最低配的首月免费,位置选择东京,平台选Linux/Unix,蓝图选仅操作系统-CentOS ,实例计划选最低的3.5刀版本,创建即可。关于系统版本,不选用预装应用(如Wordpress, Nginx等)的版本,是希望拿到干净的系统,按自己的需要定制。其实这点全看个人喜好了。

VPS实例初始化很快,搞一个静态IP地址,关联上去,免费的。

用户登录

默认不允许用户名/密码的登录方式,使用密钥对登录,按照提示进行即可。自带的用户名是centos,也可以先使用Web版SSH连接到服务器。关于用户方案推荐如下:

  • 先用web版ssh登录,添加一个新用户,设置密码,默认用户centos不去动它,似乎lightsail管理后台要用。useradd {yourname}; passwd {yourname}
  • 如果你使用linux/cygwin/maxosx,推荐把你的ssh公钥添加上去,以便直接ssh公钥登录,这比密码更安全,注意要 chmod 600 /home/{yourname}/.ssh/authorized_keys
  • 按后台提示创建适合putty用的密钥对,保存好不要泄漏。
  • 开启密码登录(只使用ssh密钥可略过):修改/etc/ssh/sshd_config 找到其中 PasswordAuthentication no 一行,删除或改成 PasswordAuthentication yes
  • (接上条)增强ssh,不允许root用户直接登录、只允许指定用户(默认用户centos及前面新建用户)登录,/etc/ssh/sshd_config 结尾追加如下两行
    AllowUsers {yourname} centos
    PermitRootLogin no

    记得systemctl restart sshd 重启sshd

防火墙/iptables

lightsail后台防火墙默认只开了几个端口,而iptables则完全开放(添加一条规则“所有TCP+UDP” 即可),推荐防火墙全开,而开启linux的iptables。如果不能掌握iptables基础使用,还是使用后台防火墙吧。

yum install iptables-services
systemctl start iptables
systemctl enable iptables

这样 iptables -L就显示默认规则了,按需要开启端口

iptables -I INPUT 5 -m state --state NEW -p tcp --dport 80 -j ACCEPT
iptables -I INPUT 6 -m state --state NEW -p tcp --dport 443 -j ACCEPT

[tip] lightsai防火墙没有开启ping或icmp的选项,但当允许了“所有TCP+UDP” 后,就可以ping了。

SELinux

lightsail的CentOS 7默认是开启SELinux的,玩不来还是关了的好. 临时关闭setenforce 0,修改配置文件/etc/selinux/config永久关闭。

 

python pip安装ConfigParser/MySQL-python的Hack方案

python3.x下pip报错No module named 'ConfigParser'

cygwin下virtual-env的python3.6下安装 MySQL-python报错,错误消息如后,提示找不到ConfigParser模块,而事实上该模块在python3.x后改名了。那就照旧名做个包,引用进去。

文件 /path-to-you-python/site-packages/ConfigParser.py

内容如下(事实上只要第二行就行),再运行正常了。(当然mysql-python的其他依赖包之前已经安装过。)

"""compatible configparser"""
from configparser import *
$ pip install MySQL-python
Looking in indexes: https://mirrors.ustc.edu.cn/pypi/web/simple
Collecting MySQL-python
  Downloading https://mirrors.ustc.edu.cn/pypi/web/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip (108kB)
    100% |████████████████████████████████| 112kB 799kB/s
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "", line 1, in 
      File "/tmp/pip-install-pasqnywc/MySQL-python/setup.py", line 13, in 
        from setup_posix import get_config
      File "/tmp/pip-install-pasqnywc/MySQL-python/setup_posix.py", line 2, in 
        from ConfigParser import SafeConfigParser
    ModuleNotFoundError: No module named 'ConfigParser'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-pasqnywc/MySQL-python/
(py36)

python3.x下pip安装 pymysql 代替MySQL-python 的方案

MySQL-python/MySQLdb 模块实在太久没有更新了,MariaDB发布后,其devel包的文件与mysql-devel有所不同,将造成编译失败。网上有hack方案,照着做补丁编译实在太麻烦,其实完全可以使用pymysql代替。

pip install pymysql

安装pymysql

新建个文件 /path-to-you-python/site-packages/MySQLdb.py 内容如下

"""compatible MySQLdb"""
from pymysql import *
install_as_MySQLdb()

完工。

前述的错误消息示例如后。如果还没安装mysql-devel包,报错将如下:

    _mysql.c:44:10: fatal error: my_config.h: No such file or directory
     #include "my_config.h"

装过MariaDB的devel包安装会报如下编译错误:

    In file included from _mysql.c:44:0:
    /usr/include/mysql/my_config.h:3:2: warning: #warning This file should not be included by clients, include only <mysql.h> [-Wcpp]
     #warning This file should not be included by clients, include only <mysql.h>
      ^~~~~~~

 

 

dokuwiki经验总结

设置

尽量不要直接修改doku程序源码(原因:容易出错,官方发布更新后还得麻烦一次);除非你对php程序足够自信、且不怕麻烦。

fnencode

linux/unix环境下,推荐在 管理-配置管理器-高级设置  里把 fnencode 改成utf-8,这样服务器上的中文文件名也将显示为原始文件名,会方便很多。(不要像网上有些文章里说的直接修改代码)。如果已经有非ascii文件名,可以先将它们改成英文文件名,改过fnencode后再从英文文件名改回来;否则非ascii码文件在前台将全部不可见。如果media附件也有非ascii码,好像没办法改。

扩展(插件/主题)

douku的扩展有两大类,插件与主题。以下都是插件,并且都可以在dokuwiki后台在线安装。尽量不要从别处下载扩展,以免夹带不好的私货。后台扩展管理界面,

  • Move 页面改名,并能自动修正所有相关链接
  • edittable 可视化编辑表格;从excel、网页等复制表格后,可以直接在可视化编辑界面粘贴
  • IPGroup 可以把某个ip的用户直接授权为相应的用户组。doc
  • WordPress authentication  允许使用wordpres站点的用户作为doku用户。

修改git提交的历史

前言

如果是团队协合中的git仓库,其历史最好不要修改;但如果是没有别人参与的个人项目,那就随便了,自己开心就好。以下所有操作前,都最好把整目录备份一下,方便出错时从头再来。

删除文件

假设要删除passwords.txt的文件,让在整个git历史像不存在过一样

git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

单个文件改名/单目录改名

git filter-branch --tree-filter 'if [ -f old-name.txt ]; then mv old-name.txt new-name.txt; fi' HEAD
git filter-branch --tree-filter 'if [ -d old-name ]; then mv old-name new-name; fi' HEAD

如上两行,分别是对单文件改名(old-name.txt  -> new-name.txt), 单目录改名(old-name -> new-name) 。

注意 .git/refs/original/refs/heads 目录要为空。否则会报错说"Cannot create a new backup. A previous backup already exists in refs/original/  Force overwriting the backup with -f",然而加-f参数似乎并没有用。  似乎执行一次这个命令,就会出现一次.git/refs/original/refs/heads/master,大概是filter-branch的后悔药(备份目录)

如果有多个分支,可以强行将分支们合并,改过名后再滚回到合并前,这样通常更方便些:可一次完成所有分支里的改名,同时避免被一大堆各种分支搞晕。(方法来源于stackoverflow,具体链接忘了)。

子目录变根目录

要把某个子目录foodir/ 下的所有文件(包含其历史),独立出去,成为一个单独的项目。其它文件自然被丢弃了,所以操作前要把整个仓库备份一下,或者在克隆的新仓库上操作。

git filter-branch --subdirectory-filter foodir -- --all

整个项目作为项目的子目录(根目录改子目录)

因为功能扩充、重构等原因,要把项目所有文件移到子目录里,git mv 不能被真正的跟踪。按如下操作,可移到newsubdir子目录中

git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

参看git联机文档 git filter-branch --help

合并提交(把多次commit合并成一个)

变基(rebase)相关操作,如 git rebase -i HEAD~3 更多参考git-book 重写历史

修改提交历史中的邮箱地址

git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD

删除指定姓名的所有提交历史

这个作法似乎不好

git filter-branch --commit-filter '
       if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
       then
               skip_commit "$@";
       else
               git commit-tree "$@";
       fi' HEAD

 

Pages: 1 2 3 4 5 6 7 8 9 10 ... 12 13 14 Next