豌豆

没有荚

1 字节顺序

鸡蛋有几种吃法?也许你从未注意。Jonathan Swift 的小说 Gulliver’s Travels 描写了这么一个故事: Lilliput 国的皇帝因按古法打鸡蛋时弄破的手指,于是下令全体臣民吃鸡蛋时必须先打破鸡蛋较小的一端(little-endian)。但百姓这对项命令本极度反感,并为此发动叛乱。

在计算机时代,计算机网络的开创者之一, Danny Cohen 开始使用 Swift 的小说中的词语 大端、小端 来描述字节的顺序,大小端这一术语开始被人们所接纳。 在计算机时代的远古时期,计算机刚被发明的时候,由于不能统一规则,对多字节对象在存储器上的存储方式有两种不同的方式。但它们有一个共识,即,多字节的对象都被存储为连续的字节序列,而对象的地址则这段连续序列中的最小的字节的地址。

例如,一个 int 类型的变量 n 的地址为 0x100,即 &n == 0x100,那么 n 将被存储在 0x100,0x101,0x102,0x103 这段位置。 假定有一个 w 位的整数,其 位 表示为 [bw-1, bw-2, … ,b1, b0] , 其中 bw-1 为最高位,b0 为最低位。这些位按每 8 位,即一个字节分组, 表示为 [Bx-1, Bx-2, … , B1, B0] ,其中 Bn = [bn8-1, bn8-2, … , bn*8-8] 。 有的机器选择选择在存储器按照从低字节到高字节的顺序来存储,这种方式称为 小端法(little-endian) ,基本所有的 Intel 机器都采用这种方式。而别一部分机器则选择按照高字节到低字节的方式来存储,称为 大端法(big-endian) ,大部分 IBM 和 Sun 的机器都采用这种规则。

所以,当一段数据从使用小端规则的机器传送到使用大端规则的机器上时,数据是无法正确解析的。除非明确的告诉计算机,这段数据需要使用哪种规则来解析。这计算机网络发明以后,这种不兼容的带来的负面作用显得尤为突出。于是一个规则产生了,在网络传输过程中,数据的发送方必须将多字节数据转换成大端法表示后再发送。而数据的接收方则需要按照大端法来解析多字节数据,再转换为它的内部表示。这样,在网络中,终端只关心本机的字节序与网络字节序,而不用关心网络另一端的机器使用什么样的字节充。所以又将大端法称为 网络字节序 ,以区分 本机字节序

Read more »

1 科谱:密码学中的常见概念

1.1 密钥

根据密钥的使用方法,可以将密钥分为两种:

  • 对称密钥 是指在加密和解密时使用同一密钥的方式

  • 公钥 加密和解密使用的不是同一种密钥,也称 非对称密钥

1.2 单向散列

单向散列是由单向散列函数计算出来的一组数值。它不能保证数据的机密性,是用来保证数据的完整性的。例如,有安全意识的软件发布者会向用户公布软件的散列值,一般是 MD5. 可以通过检验软件的 MD5 散列值,判断软件是否被篡改过。

1.3 随机数

随机数可以通过硬件生成,也可以通过软件来生成。通过硬件生成的随机数列,是根据传感器收集的热量、声音的变化等事实上无法预测和重现的自然现象信息来生成的,可以称为 真随机数 . 而一般由软件生成的随机数是可以周期性重现的,这种随机数称为 伪随机数 。生成随机数的软件叫做 伪随机数生成器

2 对称加密

常用的对称加密方法有 DES 与 AES 两种。

2.1 DES

DES 是一种将 64bit 的明文加密成 64bit 密文的对称密钥算法,它是密钥长度也是 64bit,但是它的密钥每隔 7bit 会设置一个用于错误检查的 bit, 所以实质上其密钥长度是 56bit . 因为 DES 每次只能加密 64bit 明文,所以需要对超过 64bit 的明文进行分组。以分组为单位进行处理的密码算法也称为 分组密码 。分组后的明文以什么样的方式进行加密,我们称之为 模式(mode) 。 DES 的基本结构以其设计者的名字(Horst Feistel)命名,也称 Feistel 网络可 Feistel结构。在 Feistel 结构中,将 64bit 明文再分作两部分,每部分 32bit。假如标记为 A B 两部分,那么有如下步骤:

  1. 对 B 使用子密钥 subkey 进行加密运算,生成 B’: f(B,subkey) = B'

  2. B~ 与 A 进行异或运算,生成 C : B'`` XOR A = C

  3. C 与 B 组合,生成密文 D : C # B = D

这三个步骤称为一个 轮(round) .可以看到,密文D的后半部分 B 没有被加密。我们将组成 D 的 C 与 B 部分进行互置,即令 A = B, B = C,再重复上面的步骤 1 ~ 3 ,这样明文的每一个部分都被加密了。 DES 就是一种由 16 个轮循环组成的 Feistel 网络。每一轮都会生成一个不同的密钥,所以称为子密钥。

Read more »

子模块 (Submodule)

添加

git submodule add [remotegiturl] [localdir]

更新

git submodule update --init --recursive

clone 时更新子模块

git clone [remotegiturl] --resuresive

远程更新(更新到最新)

submodule 不会detach到源的任何一个分支,而只是源的某一个commit,
使用 git submodule update --remote
将本地的子模块更新到最新。如果需要将远程仓库的子模块了更新到最新,可以先更新本地,再push到远程仓库:

1
2
3
git add .
git commit -m "submodule update"
git push

删除

git 没有提供删除子模块的命令

Read more »

TCP(TRANSMISSION CONTROL PROTOCOL,即传输控制协议)

是当今网络中使用得最为广泛的协议。与 UDP不同,TCP 提供了一种 面向连接(connection-oriented) 的、可靠的字节流服务。“面向连接”,是指使用 TCP 的两个应用程序 必须在它们可以交换数据之前,通过相互联系建立起一个 TCP 连接。建立起连接的两端称为两个 端点(endpoint) 。因为 TCP 是面向连接的,所以像广播和组播这样的概念在 TCP 中是不存在的。 TCP 提供了流的概念,应用程序可以将任意大小的数据交给 TCP而不用关心如何发送。如,一个应用程序在一端先后写入 10 个字节、20个字节、50个字节的数据,连接的另一端的应用程序是不需要关心这个过程的,应用程序可以选择自己读取数据流的大小,如一次读20字段分4次读取,也可以一次读入80个字节。

一个 TCP 块包含了 TCP头和应用程序数据,称之为 报文段(segment) 。TCP 是依赖于 IP 协议的,TCP 必须将报文段转换成一个 IP 可以携带的分组,这被称为一个 组包(packetizition)

如图,显示了 TCP 在 IP 数据报中的封装,以及 TCP 头部的结构。其中,着色部分用于与该报文的发送方关联的相反方向上的数据流。

  • 端口号 每个TCP 头包含了源和目标的端口号,这两个值与IP头部的源和目标IP地址一起组成了唯一标识。一个IP与一个端口的组合被你为一个 端点(endpoint) 或一个 套接字(Socket) ,每一个TCP连接由一对套接字唯一标识。

  • 序列号 字段标识了TCP发送端到接收端的数据流的字节数,代表着该报文段的数据中的第一个字节。它是一个32位无符号数,到达 232-1后再循环到0. 使用序列号,TCP的接收端可以丢弃重复的报文,并归整以杂乱次序到达的报文。TCP接收到报文的顺序是不可控的,然而TCP是一种流协议,它不能向程序程序提供顺序错乱的数据,因此,TCP接收端在向上层提供数据时,会等待较小序列号的报文,并对报文进行排序。

  • 确认号 可以认为,TCP 发送的数据的每一个字节都已被编号。当TCP接收到另一端发送的数据时,它会发送一个确认。但这个确认可能不会立即发出。当接收方向发送方确认接收时,确认号表示发送言期望收到的下一个报文的序列号。所以确认号应该为 序列号 + 收到的数据的字节数据 + 1 。确认号字段只有在ACK字段启用后才有效。TCP使用确认是积累的,可以认为,一个确认号 N 表示 N-1 个字节已经被成功接收。

  • 头部长度 字段给出了头部的长度,以32位字为单位。该字段只有4位,那么它能表示的最大头部长度为 60 字节。((2^{4}-1) * 32 / 8)。即一个TCP报文的头部长度的范围为 20 ~ 60 字节。

  • 窗口大小 字段用来控制流量。该字段以字节为单位,因为它是 16 位的,故限制了窗口大小为 65535 字节,从而限制了 TCP 的吞吐性能。

  • TCP校验和 字段由发送方计算和保存,由接收方校验。它用于检测传送过程中的比特差错。如果一个报文的校验和无效,那么TCP会丢弃它而不会返回任何确认信息。然而TCP可能会对一个已经确认过的报文再次确认,以帮助发送方计算它的拥塞控制。

  • 紧急指针 字段只有在URG字段设置时才有效。它表示从报文的序列号开始的一个 正偏移 ,用以产生紧急数据的字段一个字段的序列号。

  • 选项 是可变的。最常见的选项是 “最大段大小” 选项(MSS),连接的每一个端点一般在它发送的第一个报文上指定该选项(即设置SYN位字段的那个报文),指定该选项的发送者在相反方向上希望接收到的报文段的最大值。

  • 标识位

    • CWR: 拥塞窗口减(发送方降低它的发送速率)

    • ECE: ECN 回显(发送方接收到了一个更早的拥塞报告)

    • URG: 紧急(紧急指针字段有效)

    • ACK: 确认(确认号字段有效)

    • PSH: 推送(接收方应该尽快给应用程序传送这个数据 —然而没有被可靠实现或用到)

    • RST: 重置连接(连接取消,经常是因为错误)

    • SYN: 用于初始化一个连接的同步序列号

    • FIN: 结束向对方发送数据

当TCP发送一组报文段时,通常会设置一个重传计时器,等待对方确认接收。当对方的确认报文到达时,该计时器会被更新,如果确认没有及时到达,这个报文就会被重传。 TCP提供了一种可靠、面向连接、字节流、传输层的服务。在接下来,我们将对TCP的细节进行研究。

必应有一个 API 可以获取背景图的信息:

1
http://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1

其中,idx 为向后的天数,idx<=10. n 为获取信息的条数,n<10.

返回json格式的数据:

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
{
"images": [
{
"startdate": "20170405",
"fullstartdate": "201704051600",
"enddate": "20170406",
"url": "/az/hprichbg/rb/FreshSalt_ZH-CN12818759319_1920x1080.jpg",
"urlbase": "/az/hprichbg/rb/FreshSalt_ZH-CN12818759319",
"copyright": "淡水和盐水在埃斯塔蒂特附近的三河河口交汇,西班牙 (© Yann Arthus-Bertrand/Getty Images)",
"copyrightlink": "http://www.bing.com/search?q=L'Estartit&form=hpcapt&mkt=zh-cn",
"quiz": "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20170405_FreshSalt%22&FORM=HPQUIZ",
"wp": true,
"hsh": "800425f6986718006147107f1e622f39",
"drk": 1,
"top": 1,
"bot": 1,
"hs": []
}
],
"tooltips": {
"loading": "正在加载...",
"previous": "上一个图像",
"next": "下一个图像",
"walle": "此图片不能下载用作壁纸。",
"walls": "下载今日美图。仅限用作桌面壁纸。"
}
}

有这些信息,就可以每天抓取bing的背景图了

Read more »

1 REST的概念

Roy Thomas Fielding 博士在2000年 发表了论文 Architectural Styles and the Design of Network-based Software Architectures (《架构风格与基于网络的软件架构设计》) 。在这篇论文中,首次系统地阐述了 REST 的架构风格和设计思想。REST,全称为(Resource) Representational State Thransfer ,常见的翻译为"表现层状态转移",它省略了主语:资源。通俗来讲,即资源在网络中以某种表现形式进行状态转移。

  • Resource: 资源,即数据。泛指 Web 上一切可以识别、命名、可访问或处理的实体,如 HTTPPage,音频文件等。使用统一资源定位符指(URI)向资源.

  • Representational:某种表现形式。如 JSON,XML,二进制流等

  • State Transfer: 状态变化。通过 HTTP 动词实现。

在互联网发展的早期,网页的前后端是融合在一起的,如JSP,PHP.互联网发展的今天,客户端硬件设备层出不穷,而不同的Client又需要展示相同的商业逻辑。REST 接口可以为为不同的设备提供一套统一的接口。

简单来说, REST 即为: 使用URL定位资源,使用HTTP原语描述操作

HTTP原语,指 HTTP提供的原始方法,如 GET\POST\DELETE\HEAD etc

2 RESTful设计风格

RESTful 不是架构,而是一种架构风格,它提供了一些设计原则和约束条件。 REST 架构风格最重要的约束有:

  1. C/S架构。 Client/Server 架构形式提供了基本的分布式,客户端发走请求,服务端响应可拒绝请求。使用 HTTP Status Code 传递 Server 状态信息,如果出错则返回错误信息,则客户端处理异常。

  2. 无状态。通信的传话状态应该全部由客户端维护。即请求中应该包括全部的必要信息。

  3. 缓存。无状态不胜表示可能出现重复的请求。事实上有些请求只需要第一次完成执行,其余次请求皆可以享用这次请求的成果。缓存可以抵消一部分无状态带来的影响。

  4. 统一接口。这意味着每一个REST应用都共享一种能用架构。

  5. 分层系统:将系统划分为几个部分,每个部分负责单一职责。然后通过上层对下层的依赖和调用组成一个完成的系统。通常将系统划分为:应用层、服务层、数据访问层。

如果一个架构满足 REST 原则 ,那么就可以称它为 RESTful 架构。

API 发布之后,就很难再修改。所以在设计API时需要遵循一定规范,且又需要灵活友好。

Read more »

1 分屏启动VIM

  • 使用参数 O 垂直分屏:
1
vim -O<n> file1 file2 ...
  • 使用参数 o 水平分屏:
1
vim -o<n> file1 file2 ...

为数据,表示分成几屏

Read more »

在 C++11 之前 ,C++ 标准并没有提供统一的并发编程标准,也没有提供语言级别的支持。这导致我们在编写可移植的多线程程序时很不方便,往往需要面向不同的平台进行不同的实现,或者引入一些第三方平台,如Boost,pthread_win32 等。 从C++11开始 ,对并发编程进行了语言级别的支持,使用使用C++进行并发编程方便了很多。这里介绍C++11并发编程的相关特性。

1 线程

1.1 线程的创建

std::thread 的构造函数如下:

1
2
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );

我们只需要提供线程函数或函数对象,即可以创建线程,并可以同时指定线程函数的参数。

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
#include <iostream>
#include <thread>
using namespace std;

void func(const string& str){
cout<<str<<endl;
}

class FuncCls{
public:
void operator()(){
cout<<"Funcls called"<<endl;
}
};

int main(void){
thread t1(func,"Hello World");
FuncCls fcls;
thread t2(fcls);

if(t1.joinable()){
t1.join();
}
t2.join();
return 0;
}

join函数将会阻塞线程,直到线程函数执行完毕,主线程才会接着执行。如果线程函数有返回值,返回值将被忽略。 在使用线程对象的过程中,我们需要注意线程对象的生命周期。如果线程对象先于线程函数结束,那么将会出现不可预料的错误。可以通过线程阻塞的方式来等待线程函数执行完(join),或让线程在后台执行。 如果不希望线程被阻塞,可以调用线程的 detach() 函数,将线程与线程对象分离。但需要注意的是,detach 之后的线程无法再使用join来进行阻塞了,即detach之后的线程,我们无法控制了。当我们不确定一个线程是否可以join时,可以先使用 thead::joinable() 来进行判断。

另外,我们还可以通过 std::bind, lambda 来创建线程(其实就是使用函数对象创建线程)。

线程不可以被复制,但是可以被移动:

Read more »

1 引子

同事写的程序出现了点问题。调试发现,错误出现在一个 if 语句上:

1
2
3
4
// ...
// float a = 0.2, b=0.4;
if(a + b == 0.6)
// ...

这个 if 表达式被判定为 false, 程序没有按照预订执行下去。0.2 + 0.4 = 0.6,这还会有错吗?同事表示其小学数学还是很过关的。 是的,在人类的认知里,这毫无疑问是正确的,但是在计算机的认知里,就不一定了。这需要我们了解浮点数这种数据类型。

2 浮点数

2.1 用二进制表示小数

首先我们来想一下如何来表示一个十进制整数 d:

dmdm1...d1d0d1d2...d1,d[0,9]d_m d_{m-1} ... d_1 d_0 d_{-1} d_{-2} ... d_{-1}, d\in [0,9]

这个表示方法描述的数值 d 的定义如下:

d=i=1m10i×did = \sum_{i=-1}^m{10^i \times d_i}

同样,引申到小数,小数点左边的数字是 10 的非负幂,得到整数部分;小数点右面的数字是 10 的负幂,得到小数部分。

例如: (12.34_{10}) 所表示的字为: ( 1 \times 10^{1} + 2 \times 10^{0} + 3 \times 10^{-1}

  • 4 \times 10^{-2} =12 \frac{34}{100} )

类似地我们考虑一个二进制数 b 的表示:

bmbm1...b1b0b1...bn,b[0,1]b_{m} b_{m-1} ... b_{1} b_{0} b_{-1} ... b_{-n} ,b\in[0,1]

它的定义如下:

b=i=nm2i×dib = \sum_{i=-n}^{m}2^i \times d_i

如 (10.11_2) 表示数字: (1 \times 2^1 + 1 \times 2^{0} + 1 \times 2^{-1} + 1 \times 2^{-2} = 2 \frac{3}{4} )

Read more »

实际上这就是一个将文字转换为对应的图像并在前端显示出来的过程

目前所有的 emoji 与文件的对应关系定义如下:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
var emoji={"[大笑]":{file:"emoji_0.png"},
"[可爱]":{file:"emoji_01.png"},
"[色]":{file:"emoji_02.png"},
"[嘘]":{file:"emoji_03.png"},
"[亲]":{file:"emoji_04.png"},
"[呆]":{file:"emoji_05.png"},
"[口水]":{file:"emoji_06.png"},
"[汗]":{file:"emoji_145.png"},
"[呲牙]":{file:"emoji_07.png"},
"[鬼脸]":{file:"emoji_08.png"},
"[害羞]":{file:"emoji_09.png"},
"[偷笑]":{file:"emoji_10.png"},
"[调皮]":{file:"emoji_11.png"},
"[可怜]":{file:"emoji_12.png"},
"[敲]":{file:"emoji_13.png"},
"[惊讶]":{file:"emoji_14.png"},
"[流感]":{file:"emoji_15.png"},
"[委屈]":{file:"emoji_16.png"},
"[流泪]":{file:"emoji_17.png"},
"[嚎哭]":{file:"emoji_18.png"},
"[惊恐]":{file:"emoji_19.png"},
"[怒]":{file:"emoji_20.png"},
"[酷]":{file:"emoji_21.png"},
"[不说]":{file:"emoji_22.png"},
"[鄙视]":{file:"emoji_23.png"},
"[阿弥陀佛]":{file:"emoji_24.png"},
"[奸笑]":{file:"emoji_25.png"},
"[睡着]":{file:"emoji_26.png"},
"[口罩]":{file:"emoji_27.png"},
"[努力]":{file:"emoji_28.png"},
"[抠鼻孔]":{file:"emoji_29.png"},
"[疑问]":{file:"emoji_30.png"},
"[怒骂]":{file:"emoji_31.png"},
"[晕]":{file:"emoji_32.png"},
"[呕吐]":{file:"emoji_33.png"},
"[拜一拜]":{file:"emoji_160.png"},
"[惊喜]":{file:"emoji_161.png"},
"[流汗]":{file:"emoji_162.png"},
"[卖萌]":{file:"emoji_163.png"},
"[默契眨眼]":{file:"emoji_164.png"},
"[烧香拜佛]":{file:"emoji_165.png"},
"[晚安]":{file:"emoji_166.png"},
"[强]":{file:"emoji_34.png"},
"[弱]":{file:"emoji_35.png"},
"[OK]":{file:"emoji_36.png"},
"[拳头]":{file:"emoji_37.png"},
"[胜利]":{file:"emoji_38.png"},
"[鼓掌]":{file:"emoji_39.png"},
"[握手]":{file:"emoji_200.png"},
"[发怒]":{file:"emoji_40.png"},
"[骷髅]":{file:"emoji_41.png"},
"[便便]":{file:"emoji_42.png"},
"[火]":{file:"emoji_43.png"},
"[溜]":{file:"emoji_44.png"},
"[爱心]":{file:"emoji_45.png"},
"[心碎]":{file:"emoji_46.png"},
"[钟情]":{file:"emoji_47.png"},
"[唇]":{file:"emoji_48.png"},
"[戒指]":{file:"emoji_49.png"},
"[钻石]":{file:"emoji_50.png"},
"[太阳]":{file:"emoji_51.png"},
"[有时晴]":{file:"emoji_52.png"},
"[多云]":{file:"emoji_53.png"},
"[雷]":{file:"emoji_54.png"},
"[雨]":{file:"emoji_55.png"},
"[雪花]":{file:"emoji_56.png"},
"[爱人]":{file:"emoji_57.png"},
"[帽子]":{file:"emoji_58.png"},
"[皇冠]":{file:"emoji_59.png"},
"[篮球]":{file:"emoji_60.png"},
"[足球]":{file:"emoji_61.png"},
"[垒球]":{file:"emoji_62.png"},
"[网球]":{file:"emoji_63.png"},
"[台球]":{file:"emoji_64.png"},
"[咖啡]":{file:"emoji_65.png"},
"[啤酒]":{file:"emoji_66.png"},
"[干杯]":{file:"emoji_67.png"},
"[柠檬汁]":{file:"emoji_68.png"},
"[餐具]":{file:"emoji_69.png"},
"[汉堡]":{file:"emoji_70.png"},
"[鸡腿]":{file:"emoji_71.png"},
"[面条]":{file:"emoji_72.png"},
"[冰淇淋]":{file:"emoji_73.png"},
"[沙冰]":{file:"emoji_74.png"},
"[生日蛋糕]":{file:"emoji_75.png"},
"[蛋糕]":{file:"emoji_76.png"},
"[糖果]":{file:"emoji_77.png"},
"[葡萄]":{file:"emoji_78.png"},
"[西瓜]":{file:"emoji_79.png"},
"[光碟]":{file:"emoji_80.png"},
"[手机]":{file:"emoji_81.png"},
"[电话]":{file:"emoji_82.png"},
"[电视]":{file:"emoji_83.png"},
"[声音开启]":{file:"emoji_84.png"},
"[声音关闭]":{file:"emoji_85.png"},
"[铃铛]":{file:"emoji_86.png"},
"[锁头]":{file:"emoji_87.png"},
"[放大镜]":{file:"emoji_88.png"},
"[灯泡]":{file:"emoji_89.png"},
"[锤头]":{file:"emoji_90.png"},
"[烟]":{file:"emoji_91.png"},
"[炸弹]":{file:"emoji_92.png"},
"[枪]":{file:"emoji_93.png"},
"[刀]":{file:"emoji_94.png"},
"[药]":{file:"emoji_95.png"},
"[打针]":{file:"emoji_96.png"},
"[钱袋]":{file:"emoji_97.png"},
"[钞票]":{file:"emoji_98.png"},
"[银行卡]":{file:"emoji_99.png"},
"[手柄]":{file:"emoji_100.png"},
"[麻将]":{file:"emoji_101.png"},
"[调色板]":{file:"emoji_102.png"},
"[电影]":{file:"emoji_103.png"},
"[麦克风]":{file:"emoji_104.png"},
"[耳机]":{file:"emoji_105.png"},
"[音乐]":{file:"emoji_106.png"},
"[吉他]":{file:"emoji_107.png"},
"[火箭]":{file:"emoji_108.png"},
"[飞机]":{file:"emoji_109.png"},
"[火车]":{file:"emoji_110.png"},
"[公交]":{file:"emoji_111.png"},
"[轿车]":{file:"emoji_112.png"},
"[出租车]":{file:"emoji_113.png"},
"[警车]":{file:"emoji_114.png"},
"[自行车]":{file:"emoji_115.png"}};

使用正则表达式替换成文件,代码如下:

1
2
3
4
5
6
7
8
9
10
function buildEmoji(text) {
var re =  /\[([^\]\[]*)\]/g;
var matches = text.match(re) || [];
for (var j = 0, len = matches.length; j < len; ++j) {
if(emoji[matches[j]]){
text = text.replace(matches[j], '<img class="emoji" src="../static/modules/message/images/emoji/' + emoji[matches[j]].file + '" />');
}
}
return text;
}

并定义出相应的 style 即可。

用到的 emoji 图片与 js 文件可以在这里下载

0%