EdmondFrank's 时光足迹

この先は暗い夜道だけかもしれない それでも信じて進むんだ。星がその道を少しでも照らしてくれるのを。
或许前路永夜,即便如此我也要前进,因为星光即使微弱也会我为照亮前途。
——《四月は君の嘘》

Web正文提取(偏純文本类)

算法原理介绍

简介

本文主要采用基于标记窗的方法来提取网页的正文信息,不仅仅适合于处理一个网页中所有正文 信息均放在一个td的情况,也适合于处理网页正文放在多个td中的情况。即,它能够解决非Table 结构的网页正文提取问题。

定义

一般称HTML中成对出现的标记为标记对,称HTML格式的网页中出现在Title之后显示内容为非空的 标记对为标记窗

基本步骤

前提需求

满足规范化网页,即:

  • 除了网页标记tag外的地方出现"<“,”>“用<>替代
  • 所有标记的属性值放在引号中,如 a herf=“www.baidu.com"。
  • 所有标记都是匹配的,即每个开始标记均对应着一个结束,如
  • 所有标记都是正确嵌套的

提取网页正文

  1. 找出所有的标记窗Tw,对每一个标记窗Twi(i=1,…,N),去掉其中的HTML标记,得到不含任何HTML

标记的字符串。 2. 对每个标记窗Twi 内的字符串分词,得到字符串序列。取出Stwi 中的实词,得到Stwi ={

Wtw1,Wtw2,…,Wtwq}。使用Levenshtein Distance公式计算标题词序列Stitle与字符串 序列Stwi的距离(即主题相关性) 3. 比较计算结果和閥值大小,如果小于则为正文信息,将其提取,否则,舍弃。

算法实现

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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#encoding=utf-8
import jieba
from bs4 import BeautifulSoup
import re
import requests
#虚词列表
excludeWords = ()
with open('excludeWords','r+') as f:
    excludeWords=f.read()
    f.close()
excludeWords = eval(excludeWords)
class domNode:
    parentNode=None
    currNode=None
    innerText=None
    posi=0
    def __init__(self,parentNode,currNode,innerText,posi):
        self.parentNode=parentNode
        self.currNode=currNode
        self.innerText=innerText
        self.posi=posi
def levenshtein(a,b):
    "相似度计算."
    n, m = len(a), len(b)
    if n > m:
        # Make sure n <= m, to use O(min(n,m)) space
        a,b = b,a
        n,m = m,n

    current = range(n+1)
    for i in range(1,m+1):
        previous, current = current, [i]+[0]*n
        for j in range(1,n+1):
            add, delete = previous[j]+1, current[j-1]+1
            change = previous[j-1]
            if a[j-1] != b[i-1]:
                change = change + 1
            current[j] = min(add, delete, change)
    return current[n]

def filter_words(sourList,filterList):
    dest=[]
    if type(sourList)!="<type 'set'>":
        dest=list(sourList)
    else:
        dest=sourList
    if filterList==None or len(filterList)==0:
        return dest
    dest = [ i for i in dest if not(i in filterList) ]
    return dest

def getContextByNode(htmlSoup,titleKeyWordList,filterList):
    sentenceList=jieba.cut(htmlSoup.string)
    sentenceList=filter_words(sentenceList,excludeWords)
    sentenceList=filter_words(sentenceList,filterList)
    if levenshtein(sentenceList,titleKeyWordList)>=len(sentenceList):
        return None
    return sentenceList

def getHTMLContext(html,filterList):
    "获取html的正文内容,不包含tag标签"
    if html==None:
        return ""
    if len(html)==0:
        return ""
    encodeHtml=encodeSpecialTag(html)
    htmlSoup=BeautifulSoup(encodeHtml)

    #去除script和style内容
    if  htmlSoup.script!=None:
        htmlSoup.script.replaceWith("")
    if htmlSoup.style!=None:
        htmlSoup.style.replaceWith("")

    if htmlSoup==None or htmlSoup.html==None or htmlSoup.html.head==None or htmlSoup.html.head.title==None:
        return ""

    #提取标题
    title=htmlSoup.html.head.title.string
    #print(title)
    if title==None:
        return ""

    titleKeyWordList=jieba.cut(title)
    titleKeyWordList = list(filter(lambda w:w.strip(),titleKeyWordList))
    titleKeyWordList=filter_words(titleKeyWordList,excludeWords)
    titleKeyWordList=filter_words(titleKeyWordList,filterList)
    #print(titleKeyWordList)
    # 实词是否为空
    if len(titleKeyWordList)==0:
        return ""

    markWindowsList=getMarkWindowsList(htmlSoup.html.body)
    #print(type(markWindowsList))
    #print(markWindowsList)
    markWindowsList = sorted(markWindowsList,key=cmp_to_key(lambda x,y: cmp(x.posi, y.posi)))

    #print(titleKeyWordList)
    context=""
    for item in markWindowsList:
        innerText=item.innerText
        markWindowKeyWordList=jieba.cut(innerText)
        markWindowKeyWordList=filter_words(markWindowKeyWordList,excludeWords)
        markWindowKeyWordList=filter_words(markWindowKeyWordList,filterList)
        kl=len(markWindowKeyWordList)
        if kl==0:
            continue

        l=levenshtein(titleKeyWordList,markWindowKeyWordList)
        #print(markWindowsList,l)
        if l<kl:
            context+=innerText.strip()+'.\n'

    return  decodeSpecialTag(context)
def cmp(x,y):
    return x-y
def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K


def repl(m):
    contents = m.group(3)
    if contents == '</p>':
        return '[[p]]'
    return contents
def encodeSpecialTag(html):
    "这里的规则有助于正文内容的识别,还可以保留部分标签,如下面就保留了P,br等标签"
    comment=re.compile(r"<!--[.\s\S]*?-->")
    html=comment.sub('', html)


    #h tag
    hPre=re.compile(r"<[\t ]*?[hH][1-6][^<>]*>")
    html=hPre.sub('[[h4]]', html)
    hAfter=re.compile(r"<[\t ]*?/[hH][1-6][^<>]*>")
    html=hAfter.sub('[[/h4]]', html)

    br=re.compile(r"<[ \t]*?br[^<>]*>", re.IGNORECASE)
    html=br.sub('[[br /]]', html)

    hr=re.compile(r"<[ \t]*?hr[^<>]*>", re.IGNORECASE)
    html=hr.sub('[[hr /]]', html)


    strongPre=re.compile(r"<[\t ]*?strong[^<>]*>", re.IGNORECASE)
    html=strongPre.sub('[[strong]]', html)
    strongAfter=re.compile(r"<[\t ]*?/strong[^<>]*>", re.IGNORECASE)
    html=strongAfter.sub('[[/strong]]', html)

#    labelPre=re.compile(r"<[\t ]*?label[^<>]*>", re.IGNORECASE)
#    html=labelPre.sub('[[label]]', html)
#    labelAfter=re.compile(r"<[\t ]*?/label[^<>]*>", re.IGNORECASE)
#    html=labelAfter.sub('[[/label]]', html)
#
#    spanPre=re.compile(r"<[\t ]*?span[^<>]*>", re.IGNORECASE)
#    html=spanPre.sub('[[span]]', html)
#    spanAfter=re.compile(r"<[\t ]*?/span[^<>]*>", re.IGNORECASE)
#    html=spanAfter.sub('[[/span]]', html)
    pPre=re.compile(r"<[\t ]*?/p[^<>]*>[^<>]*<[\t ]*?p[^<>]*>", re.IGNORECASE)
    html=pPre.sub('[[/p]][[p]]', html)
    pAfter=re.compile(r"<[\t ]*?p[^<>]*>(?=[^(\[\[)]*\[\[/p\]\]\[\[p\]\])", re.IGNORECASE)
    html=pAfter.sub('[[p]]', html)
#
#    pAfter=re.compile(r"(\[\[p\]\].*>)([^<>]*)(</p>)", re.IGNORECASE)
#    html=pAfter.sub(repl,html)



    aPre=re.compile(r"<[\t ]*?a[^<>]*>", re.IGNORECASE)
    html=aPre.sub('', html)
    aAfter=re.compile(r"<[\t ]*?/a[^<>]*>", re.IGNORECASE)
    html=aAfter.sub('', html)

#    chardet.detect(html)['encoding']

    js=re.compile(r"<(script)[\w\s\S.\u4e00-\u9fa5\uac00-\ud7ff\u30a0-\u30ff\u3040-\u309f]*?</\1>(?s)", re.IGNORECASE)
    html=js.sub('', html)
    css=re.compile(r"<(style)[\w\s\S.\u4e00-\u9fa5\uac00-\ud7ff\u30a0-\u30ff\u3040-\u309f]*?</\1>(?s)", re.IGNORECASE)
    html=css.sub('', html)


    return html
def decodeSpecialTag(html):
    html=html.replace("[[strong]]","<strong>")
    html=html.replace("[[/strong]]","</strong>")
    html=html.replace("[[hr /]]","<hr />")
    html=html.replace("[[br /]]","<br />")
    html=html.replace("[[h4]]","<h4>")
    html=html.replace("[[/h4]]","</h4>")
    html=html.replace("[[label]]","<label>")
    html=html.replace("[[/label]]","</label>")
    html=html.replace("[[span]]","<span>")
    html=html.replace("[[/span]]","</span>")
    html=html.replace("[[p]]","<p>")
    html=html.replace("[[/p]]","</p>")
    return html

def getMarkWindowsList(bodySoup):
    #过滤a标签
    nodeQueue=[]
    innerTextNodeList=[]
    nodeQueue.append(bodySoup)
    i=0
    while len(nodeQueue)>0:
        currNode=nodeQueue[0]
        del nodeQueue[0]
        for childNode  in currNode:
            if childNode.string!=None:
                innerText=childNode.string
                innerText=innerText.replace('\r\n','').replace('\r','').replace('\n','')
                tmp=innerText.replace('\t','').replace('  ',' ').replace('  ',' ')
                tmpInt=len(tmp.replace(" ",""))
                if tmpInt==0:continue
                if len(tmp.split(' '))<=2 and len(innerText)-tmpInt>tmpInt:continue
                if len(tmp)>1  and innerText.find("©")==-1 and innerText.find("&copy;")==-1 :
                    dn=domNode(childNode.parent,childNode,innerText,i)
                    innerTextNodeList.append(dn)
                    i+=1
#                    print "i",
#                    print i,
#                    print "=",
#                    print childNode
            else:
                #这里的规则可能有助于垃圾信息的排除
                if childNode.name!=None and childNode.name=='style':continue
                if childNode.name!=None and childNode.name=='script':continue
                if childNode.name!=None and childNode.name=="a" and len(childNode.text)<=2:continue
                if childNode.name!=None and childNode.parent.name!=None   and  childNode.name=="span" and ( childNode.parent.name=="li" )and len(childNode.text)<=3:
                    continue
                nodeQueue.append(childNode)
    return innerTextNodeList

#soup=BeautifulSoup(html)
def test():
    headers={
    'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
    }
    c=requests.get('http://www.xfocus.net/articles/200808/984.html',headers=headers)
    content=c.content.decode('gbk')
    #content=c.text
    # #print(content)
    print(getHTMLContext(content,['']))
if __name__ == '__main__':
    test()

效果测试

比较网页结构相似度

总体介绍

网页网页结构相似度计算通常是网页自动分类的基础,在一般的网页信息提取中,判断网页片断是“噪声”还是“有效信息”通常是个两类分类问题。 简单地,我们可以把一般网页分为三个类,即:

  • 目录导航式页面(List\Index Page)
  • 详细页面(Detail Page)
  • 未知页面(Unknown Page)

由于网页本身就可以抽象成串行的节点或者是DOM树,那么对于串行序列,就可以常用最长公共子序列来衡量相似度

最长公共子序列

最长公共子序列是动态规划的基本问题:

序列a共有m个元素,序列b共有n个元素,如果a[m-1]==b[n-1],

那么a[:m]和b[:n]的最长公共子序列长度就是a[:m-1]和b[:n-1]的最长公共子序列长度+1;

如果a[m-1]!=b[n-1],那么a[:m]和b[:n]的最长公共子序列长度就是

MAX(a[:m-1]和b[:n]的最长公共子序列长度,a[:m]和b[:n-1]的最长公共子序列长度)

递归式展示

 

算法实现(python实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#params:
# - a : str
# - b : str
#return
# - c : 过程处理矩阵
# - c[x][y] : the lcs-length(最长公共子序列长度)
def lcs(a,b):
    lena=len(a)
    lenb=len(b)
    c=[[0 for i in range(lenb+1)] for j in range(lena+1)]
    for i in range(lena):
        for j in range(lenb):
            if a[i]==b[j]:
                c[i+1][j+1]=c[i][j]+1
            elif c[i+1][j]>c[i][j+1]:
                c[i+1][j+1]=c[i+1][j]
            else:
                c[i+1][j+1]=c[i][j+1]
    return c,c[lena][lenb]

网页相似度计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#-*-coding:utf-8-*-
import lxml.html.soupparser as soupparser
import requests
headers = {
    "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
}
def get_domtree(html):
    dom = soupparser.fromstring(html)
    for child in dom.iter():
        yield child.tag

def similar_web(a_url,b_url):
    html1 = requests.get(a_url,headers=headers).text
    html2 = requests.get(b_url,headers=headers).text
    dom_tree1 = ">".join(list(filter(lambda e: isinstance(e,str),list(get_domtree(html1)))))
    dom_tree2 = ">".join(list(filter(lambda e: isinstance(e,str),list(get_domtree(html2)))))
    c,flag,length = lcs(dom_tree1,dom_tree2)
    return 2.0*length/(len(dom_tree1)+len(dom_tree2))

percent = similar_web(
'http://edmondfrank.github.io/blog/2017/04/05/qian-tan-mongodb/',
'http://edmondfrank.github.io/blog/2017/03/27/emacsshi-yong-zhi-nan/')
print(percent) #相似度(百分比)

浅谈MongoDB

Mongodb 简介

MongoDB 是一款强大,灵活,且易于扩展的通用型数据库。

它能够扩展出非常多的功能,如二级索引,范围查询,排序,聚合以及本地空间索引的功能

基本概念

Mongodb 是一个面向文档的NoSQL型数据库,不采用关系模型。 其中mongodb 不再有“行”的概念,而是以文档取而代之。

另外也不存在预定义模式,即文档的键(key)和值(value)不再是固定的类型和大小

数据库(database)

Mongodb 的一个实例可以拥有多个相互独立的数据库,

在Mongodb中,多个文档组合成集合,而多个集合即组成数据库。

文档(document)

Mongodb 中的数据的基本单元,类似于关系型数据库管理系统中的行

表示形式:

{“greeting”:“Hello World”, …. }

集合(collection)

类似的,可以看作是一个拥有动态模式的表

基本设计

Mongodb 的设计采用横向扩展。同时面向文档的数据模型

使它能够很容易的在多台服务器之间进行数据分割。

MongoDB 启动

通常,MongoDB做为网络服务守护进程运行,客户端可连接到该服务器并执行操作。

  1. 下载MongoDB(http://www.mongodb.org/downloads

  2. 解压,并运行 mongod 命令

启动后,在默认情况下服务器会打印版本和系统信息,并监听27017端口。

除此之外 mongod 还会启动一个非常基本的HTTP服务器,监听比主端口号高1000的端口

这意味着你可以访问 http://localhost:28017 来获取数据库管理信息

MongoDB shell 简介

MongoDB 自带JavaScript Shell,可以在shell中用命令行与Mongodb实例交互

运行shell

mongo

MongoDB Shell version: 2.4.0

connecting to :test

>

shell 是一个功能完备的JavaScript解释器,可以运行任意JavaScript程序,另外还可以充分使用JavaScript的标准库

MongoDB 客户端

在客户端的shell启动后,如果要查看db当前执行哪个数据库,可以使用 db 命令

> db

test

MongoDB 基本操作

创建

insert 函数可以将一个文档添加到集合中。举一个存储文章的例子:首先,

创建一个名为post的局部变量,这是一个JavaScript对象,用于表示我们的文档

他会有几个键:“ title ”、“ content ”和 “ date ”(发布时间)

> post = {
"title":"hello",
"content":"Here's my blog post",
"date":new Date()
}

> db.blog.insert(post)

此时,这篇文章已经存到数据库中了,要查看它可用调用集合的 find 方法

> db.blog.find()

读取

findfindOne 可以接收一个 查询文档 为限定条件,并返回符合一定条件的文件,使用

find 的默认自动显示最多20个匹配的文档。

更新

使用 update 修改文章:

update接收(至少)两个参数: - 第一个是限定条件(用于匹配待更新的文档) - 第二个是新的文档

例如我们为之前的文章增加评论功能,就需要新增一个键,用于保存评论数组。

> post.comments = []
[ ]

> db.blog.update({ title : "hello"} , post)

再用 find 查看一下,就可以看见新的键。

> db.blog.find()

删除

使用remove方法可以将文档从数据库中永久删除。如果没用任何参数传入,他将删除集合内的全部文档。

同时,它可以接收一个做为限定条件的文档做为参数。例如:

> db.blog.reomve({ title : "hello" })

有关Mongodb的简单描述到此结束,更多Mongdb使用操作内容,欢迎参考下一篇文章

Emacs使用指南

Emacs快速指南

传说中的神级编辑器??

基础介绍

vim 和 emacs 分别被称为「 编辑器之神」与「 神之编辑器」,自有其独到之处。

快捷键的无差别延续

vim 和 emacs 诞生于 30 年前,快捷键基本没什么变化。这意味着,一旦你学会使用这两个编辑器,无论以后软件怎么更新,都不需要学习别的快捷键了。因为历史较长,加上快捷键变化不大,新兴编辑器大多提供模拟 vim/emacs 操作的插件。这也方便了用户迁移到其他编辑器,无需学习更多的同质快捷键。

基础和适用的默认配置

vim 和 emacs 都可以运行在终端,也有图形化的软件,非常适合快速编辑文件。当需要在无法运行图形界面的服务器上编辑代码的时候,二者也足以胜任。虽然在终端也有 nano 这样的编辑器,但毕竟过于简洁,无法支撑较为复杂的编辑工作。这两个编辑器的默认配置的功能就已经很强大了,语法着色、补全、缩进等功能都很不错。

emacs有别于vim的地方

为了实现强大的功能,vim 选择了多模式编辑(Normal, Insert, Visual 模式),而 emacs 没有输入上的 mode 差别,所以需要依赖复杂的快捷键来实现强大的编辑功能,正如上图所示。emacs 插件想象力更加丰富,有「伪装成编辑器的操作系统」之称。插件的 major mode 和 minor mode 的设计很出彩,对一个文件,只有一个 major mode,但是可以有多个 minor mode,这样一个文件一个主插件,多个附加插件,可以实现很多有趣的效果。

快捷键说明

在网上的Emacs文档或手册中,总有一些快捷键说明如下所示:

C-v : 其中C 代表 Ctrl,因此这项快捷键代表 Ctrl + C M-v : 其中M 代表 Alt,这项快捷键代表 Alt + V

基本光标控制

1
2
3
4
5
6
7
8
9
                    上一行 C-p
    向左移 C-b::: 目前光标位置 ::: 向右移 C-f
                    下一行 C-n

                 亦可使用小键盘方向键

M-f 移动到词的末尾 M-b 移动到词的首部

请注意 C-f 、C-b 和 M-f 、M-b 两对之间的类比关系。通常的惯例是:META 系 列组合键用来操作“由语言定义的单位(比如词、句子、段落)”,而 CONTROL 系列组合键用来操作“与语言无关的基本单位(比如字符、行等等)”。

这里对简单的光标移动命令做一个总结,其中也包括了整词和整句的移动:

    C-f     向右移动一个字符
    C-b     向左移动一个字符

    M-f     向右移动一个词【对中文是移动到下一个标点符号】
    M-b     向左移动一个词【对中文是移动到上一个标点符号】

    C-n     移动到下一行
    C-p     移动到上一行

    C-a     移动到行首
    C-e     移动到行尾

    M-a     移动到句首
    M-e     移动到句尾

窗格(WINDOWS)

Emacs 可以有多个“窗格”,每个窗格显示不同的文字。后面会介绍怎么对付多个窗 格,现在我们先学会如何关掉多余的窗格。其实也很简单:

    C-x 1   只保留一个窗格(也就是关掉其它所有窗格)。

也就是先按 CONTROL-x 然后再按 1。C-x 1 会保留光标所在的窗格,并将其扩大 到整个屏幕,同时关掉所有其它的窗格。

把光标移到本行然后输入 C-u 0 C-l。

输入 C-h k C-f。观察当一个新窗格出现时当前窗格(用来显示 C-f 命令的文档)是如何缩小的。

输入 C-x 1 关掉文档窗格。

Emacs中的删除

在Emacs删除文字有很多种方法。在说明相应的操作方法前,我们需要先弄明白几个概念:“删除(Delete)”、“移除(Kill)”、“召回(Yank)”还有“移除环(Kill-Ring)”

好吧,“删除”和“移除”在文本编辑的过程中实现的效果非常类似——对应的字符或内容在文档中“消失”了。存在细微差别的地方在与取回这些“消失”的内容的方法上:

被“删除”的内容,只能使用“撤销(Undo)”的办法来找回这些“消失的字符”。和大多数人预想的一样,这些“消失的字符”只会重新出现在原来它们所在的地方。

Emacs的剪贴板以及Undo/Redo

被“移除”的内容,除了可以使用“撤销”的方法了找回内容以外,还可以使用“召回”的方法让这些“消失”的内容出现在当前光标之后。这样的操作效果和使用了“剪贴板”的效果类似了。

“召回”,是将移除的内容插入到光标所在位置的一种操作。使用召回操作,让人有一种使用“剪贴板”的感觉。不过Emacs会记录多次“移除”操作的内容,这一点可要比windows操作系统提供的“剪贴板”功能要强大多了。这种记录多次“移除”操作内容的机制在Emacs中叫做“移除环”——Yank Ring。

Emacs中的查找和替换

任何编辑器都有查找和替换的功能,Emacs也不例外。在Emacs中最常用的查找方法是“增量查找”。除此之外,Emacs还提供了“正则查找”、“词组查找”、“简单查找”三种方法。这些查找方法,一般来说,熟练掌握其中的一至两类就足够应付日常使用的需要了。

Emacs中的文件操作

基础的Emacs文件操作是创建、保存、另存。

创建新文件: 使用组合键 Ctrl-x Ctrl-f 打开“find file” 在提示符处输入一个不存在的文件名 回车确认后,Emacs即创建了一个空的Buffer供输入使用。 保存文件 Emac的保存文件非常简单,只需要按下组合键Ctrl-x Ctrl-s即可。 另存文件 要另存一个文件,按下组合键Ctrl-x Ctrl-w,按提示输入新的文件名即可。

常用快捷键

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
C-v : 向前移动一屏(PageUp)
M-v : 向后移动一屏(PageDn)

导航操作:
c-p            上一行
c-n            下一行
c-f            下一个字符
c-b            上一个字符
c-a            行首
c-e            行尾

M-p            下一段
M-b            上一段
M-f            下一个单词
M-b            上一个单词
M-a            句首
M-e            句尾

c-v            下一页
M-v            上一页

c-l            以本行为中间显示

M-<            文本头
M->            文本尾部

c-M-v            不移动光标让另一个窗口翻下一页
c-S-M-v(S:shift)    不移动光标让另一个窗口翻上一页

撤销操作:
c-_ / c-x c-u        撤销
c-h l            列出最近操作
M-x revert-buffer    重新读入buffer(撤销本次全部更改)

文件操作:
c-x c-f            打开文件(没有就创建一个新的)
c-x c-s            保存文件
c-x c-w            另存为文件
M-x recover-file 文件名    从错误中恢复(读取自动保存的文件)

buffers:
c-x b 输入buffer名字    切换buffer
    方向键可以切换buffer
c-x k 输入buffer名字    终止buffer
c-x c-b            列出所有buffer
    之后按下:
    1            让选中的个buffer全屏
    o            让选中的buffer在另外的窗口显示并且切换到该窗口
    c-o         让选中的buffer在另外的窗口显示并且不切换到该窗口
    s            标记当前buffer为保存
    d/c-d        标记一个要删除的buffer并且光标下/上移动
    x            按下x后执行标记了要删除的buffer或者标记要保存的buffer

fram&windows:
c-x 2            在frame内横向分割两个windows
c-x 3            在frame内纵向分割两个windows
c-x 1            删除其他windows
c-x 0            删除当前windows
c-x o            切换到另外的窗口
#c-x {            水平缩小当前窗口(分两个窗口的时候)
#c-x }            水平扩大当前窗口
c-x 5 2            水平创建一个新的frame
c-x 5 0         杀死当前的fram(最后一个不可以哦)
c-x 5 o            切换fram

Searching:
c-s            向前搜索
c-r            向后搜索
搜索时按下:
    M-p        搜索历史向上
    M-n        搜索历史向后
M-%            搜索并替换,替换时询问每一个是否需要被替换(y表示是n表示否!表示全部y)
M-x replace-string    搜索并替换,不询问
c-r             进入紧急编辑(保存当前的查询替换的状态)
c-M-c            退出紧急编辑
c-[            取消紧急编辑(无视原来保护的现场Orz)
M-x occur 匹配串    找到并列出所有匹配串

helping:
c-h            帮助首菜单
c-h c-h            帮助buffer
c-h k    按键        显示绑定这个键位的功能
c-h a    输入功能    显示对应的按键(正则匹配)
c-x c-h            显示全部c-x的命令
c-x w    输入功能    显示对应的按键(非正则匹配)
c-x h    输入功能    显示这个功能的具体功能