原生JavaScript的Ajax

原生JS的Ajax

[本程序可作为学习之用.使用:JSP进行调试.]

昨天看了看原生JS的Ajax,觉得还行,没有想象中难度那么大。其中最大的问题,依然是兼容性。比如在原生JS中,创建各种对象就需要考虑到N多浏览器的实现。而此时,Ajax同样如此,下面的代码貌似在Chrome里面无法运行,原因未知,而且对于中文的处理也并非完美.

所以,大家尽量使用各种JavaScript第三方库来处理Ajax吧,比如使用jQuery.

ajax原理图
image-1918

处理中文

1.发送路径的参数中包括中文,在服务器端接收参数值时产生乱码
  将数据提交到服务器有两种方法:一种是使用GET方法;一种是使用POST方法.使用不同的方法提交数据,在服务器端接收参数时解决中文乱码的方法是不同的,具体如下:
  1.1 当接收使用GET方法提交的数据时,要将编码转换为GBK或GB2312,例如:
  String selProvince = request.getParameter(“parProvince”);
  selProvince = new String (selProvince.getBytes(“ISO-8859-1”),”GBK”);

1.2由于应用POST方法提交数据时,默认的字符编码是UTF8,所以当接收使用POST方法提交的数据时,要将编码转换为UTF-8.例如,代码如下:
  String username= request.getParameter(“user”);
  username = new String(username.getBytes(“ISO-8859-1”),”UTF-8”);

2.返回到responseText或responseXML的值中包含中文时产生乱码
  由于Ajax在接受responseText或responseXML的值时是按照UTF-8的编码格式进行解码的,所以如果服务器端传递的数据不是UTF-8格式,在接收responseText或responseXML的值时就可能产生乱码.解决的方法是保证从服务器端传递的数据采用UTF-8的编码格式.

源代码

Fedora18安装tomcat

HTML部分代码:

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
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>注册信息</title>
</head>
<body>
<form action="RegisterTool.jsp" method="post" name="form1">
  <table>
    <tr>
      <td>用户名:</td>
      <td><input type="text" name="user" onBlur="createRequest(form1.user.value)" /></td>
    </tr>
    <tr>
      <td>密码:</td>
      <td><input type="password" name="pwd" /></td>
    </tr>
    <tr>
      <td>确认密码:</td>
      <td><input type="password" name="pwd1" /></td>
    </tr>
    <tr>
      <td>Email:</td>
      <td><input type="email" name="email" /></td>
    </tr>
    <tr>
      <td colspan="2"><input type="submit" value="提交" disabled /></td>
    </tr>
  </table>
</form>
</body>
<script type="text/javascript">
   
    /*
    整个过程是:创建XMLHttpRequest 对象 -> 指定发送地址及发送方法 -> 发送请求 -> 指定处理方法并处理返回结果。但是需要注意,我们正常的思路理解是这样的,可是onreadystatechange事件句柄指定处理方法需要在发送之前就指定好,否则无法处理状态变化事件。
   
   
    HttpRequest主要方法:
   
    ·open():建立到服务器的新请求。

  ·send():向服务器发送请求。

  ·abort():退出当前请求。

  ·readyState:提供当前 HTML 的就绪状态。

  ·responseText:服务器返回的请求响应文本。

    主要属性:
   
   
    属性:

        readyState 说明:HTTP 请求的状态。
       
        responseText 说明:目前为止为服务器接收到的响应体(不包括头部),或者如果还没有接收到数据的话,就是空字符串。
       
        responseXML 说明:对请求的响应,解析为 XML 并作为 Document 对象返回。
       
        status 说明:由服务器返回的 HTTP 状态代码。
       
        statusText 说明:这个属性用名称而不是数字指定了请求的 HTTP 的状态代码。
       
        onreadystatechange 是每次 readyState 属性改变的时候调用的事件句柄函数。
           
   
    */
   
   
   

    //创建对象.
        function createRequest(user) {
            http_request = false;
         try {
          http_request = new XMLHttpRequest();
         } catch (trymicrosoft) {
          try {
           http_request = new ActiveXObject("Msxml2.XMLHTTP");
          } catch (othermicrosoft) {
           try {
            http_request = new ActiveXObject("Microsoft.XMLHTTP");
           } catch (failed) {
            http_request = false;
           }
          }
         }
         if (!http_request){
          alert("Error initializing XMLHttpRequest!");
        }else{
            http_request.open("GET","http://localhost::8080/register.jsp?user="+user,true);
            http_request.onreadystatechange=getResult;
            http_request.send();
        }
        }

   
    //处理返回的结果.
    function getResult (){
       
                /*
                 打开请求
       
          有了要连接的 URL 后就可以配置请求了。可以用 XMLHttpRequest 对象的 open() 方法来完成。该方法有五个参数:
       
          ·request-type:发送请求的类型。典型的值是 GET 或 POST,但也可以发送 HEAD 请求。
       
          ·url:要连接的 URL。
       
          ·asynch:如果希望使用异步连接则为 true,否则为 false。该参数是可选的,默认为 true。
       
          ·username:如果需要身份验证,则可以在此指定用户名。该可选参数没有默认值。
       
          ·password:如果需要身份验证,则可以在此指定口令。该可选参数没有默认值。
       
          通常使用其中的前三个参数。事实上,即使需要异步连接,也应该指定第三个参数为 “true”。这是默认值,但坚持明确指定请求是异步的还是同步的更容易理解。
                */
           
        if (http_request.readyState==4){ //加載完成.
            if (http_request.status==200){ //如果執行成功.
                alert(http_request.responseText) ;
            }else{
                alert(http_request.responseText) ;
            }
        }
    }
   

</script>
</html>

JSP代码[全部,可以不用添加别的代码了.]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*" %>
    <%
    int result = 0 ;
    request.setCharacterEncoding("UTF8");
    String user = request.getParameter("user");
    user = new String(user.getBytes("ISO-8859-1"),"GB2312");
    String [] USERNAME = {"明日科技","mr","admin","sa"};
    Arrays.sort(USERNAME);
    result= Arrays.binarySearch(USERNAME, user);
    if(result > -1){
        out.println("用户名已经被注册!");
    }else{
        out.println("用户名没有被注册!");
    }
    %>

网络连接错误:651

针对人群

[方法来自网络,亲测可用.]

遇到此错误的朋友,关于此错误可以百度详细解释.

断网
image-1915

感觉我这个网,就有上面这个意思..

解决方案

此方法同样适用于Windows8
错误代码651意思为:
您的调制解调器(或其它连接设备)报告了一个错误。既未找到指定的端口。
解决办法:

  1. >>删除所有原来的连接.[我每次连接不上的时候,都会新建一个拨号连接,因此,修复的时候把原来的都删除了吧]
  2. >>试一下:打开 \windows\system32\logfiles\wmi,双击打开wmi再打开里面的RtBackup 会提示你需要管理员权限
  3. >>把wmi里的后缀为etl的文件[移动到桌面,备份数据!!!]包括RtBackup里的,然后重启

理论上说,执行完上面的步骤就可以了,如果还是不行就继续执行下面的步骤吧:

  1. 、远程访问记事簿文件和当前的“网络和拨号连接”配置可能不一致如果更改了通讯设备(例如:串行口或调制解调器),请确保重新配置“网络和拨号连接”。如果错误仍然存在,请删除并重新创建“网络和拨号连接”.
  2. 、win7 错误代码651,路由能上,拨号上不了
  3. 试一下:打开 \windows\system32\logfiles\wmi,双击打开wmi再打开里面的RtBackup 会提示你需要管理员权限 继续 重启,即可修复。
  4. 、不行的话把wmi里的后缀为etl的文件删除包括RtBackup里的 重启
  5. 、如果上面的都不管用的话那尝试一下,禁用本地连接-关机-检查猫的所有插头拔下重插-拔下猫的电源插头-等待2分钟 之后插上插头-开机。
  6. 、最后一招先从别的能上网的系统的机子里复制下面这个文件 c:\windows\system32\drivers\raspppoe.sys 来替换你电脑里的这个文件。

比较简单的实现”返回顶部”

设置CSS

更新:margin-left的值过大,导致内容挡住了按钮.已更新.
[具体样式可以参考我的.]

下面是CSS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#returnTop { /* 默认样式 */
    width:51px;
    height:51px;
    margin-top:220px;
    margin-left:20px;/*原来:margin-left:200px;更新:此值尽量小一些,否则在一些小的屏幕上无法完整显示按钮*/
    position:absolute;
    position:fixed
}
.returnMout { /*移除后的样式*/
    background:url(images/returntop.gif);
    position:0px 0px;
    position:absolute;
    position:fixed
}
.returnMover { /*移到元素上的样式*/
    width:51px;
    height:51px;
    background:url(images/returntop.gif);
    position:absolute;
    position:fixed;
    background-position:-51px -51px
}

按钮图片

按钮图片
image-1912

JS部分

使用jQuery(1.10.*)版本,不过可以把下面的JS代码直接放到你的网站本身的JS里面.

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
$(window).scroll(function() {

    var bt = $('#returnTop');

    bt.removeClass("returnMover");

    bt.addClass("returnMout");

    var st = $(window).scrollTop();

    if (st > 50) {

        bt.show();

        //
        bt.mouseover(function(e) {

            bt.removeClass("returnMout");

            bt.addClass("returnMover");

        });

        bt.mouseout(function(e) {

            bt.removeClass("returnMover");

            bt.addClass("returnMout");

        });

        bt.click(

        function() {

            window.scroll(0, 0);

        }

        );

    } else {

        bt.hide();

    }

});

返回顶部 效果图
image-1913

HTML元素

1
<div id="returnTop">&nbsp;</div>

放在页面顶部,我放在底部没效果,不知道为什么.

JSTL获取数据库中的信息

运行环境

  • Tomcat-7.0.35[请先保持关闭状态]
  • MySQL5.5.29[请先保持关闭状态]
  • [Eclipse]Version: Juno Service Release 1
  • Build id: 20120920-0800
  • Fedora 18 /32

此程序在我的平台上可以运行,因此系统安装在虚拟机上,所以也需要保证虚拟机外的系统也可访问.此操作涉及到防火墙操作[文章]

配置数据库

请确认你已经安装Mysql数据库,并已经初始化完成[MySQL启动]

之后,可以执行下面的命令:

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
//[SHELL]-->表示在Linux终端执行的命令
//[MySQL]-->表示在MySQL中执行的命令

//[SHELL] vim shop.sql
//复制下面的内容,粘贴到里面
DROP DATABASE DEMO;
CREATE DATABASE DEMO;
USE DEMO;
create table SHOP1063 (
       id int not null auto_increment,
      name varchar(20) not null ,
       age int not null ,
      primary key(id)
      );
//[SHELL]:wq!+ENTER
[SHELL]mysql -u root -p
[SHELL]password:[键入你的密码,之后回车]
[MySQL]:source shop.sql
//如下提示表示创建成功.
//Query OK, 1 row affected (0.03 sec)

//Query OK, 1 row affected (0.00 sec)

//Database changed
//Query OK, 0 rows affected (0.15 sec)

利用Java向MySQL插入数据

在Eclipse中新建Java项目,名为:InsertData,然后复制下面的代码,并执行[返回1表示插入成功]:

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
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 *  insert Data
 *
 * @author puruidong
 * @version 2013.7.29
 *
 */
public class InsertData {
   
   
    /*
     * !!!CREATE DATABASE AND CREATE TABLE.
     *  MYSQL.
     *
     
         DROP DATABASE DEMO;
            CREATE DATABASE DEMO;
            USE DEMO;
                create table SHOP1063 (
                    id int not null auto_increment,
                  name varchar(20) not null ,
                   age int not null ,
                  primary key(id)
                  );
     */
   
    private static final String DRIVER = "com.mysql.jdbc.Driver";
    private static String URL = "jdbc:mysql://localhost:3306/DEMO";
    private static String USER = "root";
    private static String PASSWORD = "dcxy5201314";
    private static Statement st = null ;
   
    private InsertData(){
        getStat();
    }
   
   
    /**
     * GET Statement Object.
     * @return Statement Object.
     */
   
    private Statement getStat(){
        try{
            Class.forName(DRIVER);
            Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             st = conn.createStatement();
           
        }catch(SQLException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return st;
    }
   
    /**
     * inser Data.
     * @return
     * @throws SQLException
     */
    private int InsertDatas(int i)throws SQLException{
        int result = 0 ;
        StringBuffer sql = new StringBuffer();
        sql.append(" INSERT INTO SHOP1063  ");
        sql.append(" (name,age) ");
        sql.append(" VALUES ");
        sql.append(" ( 'test"+i+"',");
        sql.append(i+");");
        result = st.executeUpdate(sql.toString());
        return  result;
    }
   
    public static void main (String [] args){
        try{
            InsertData id = new InsertData();
            int result=0;
            for (int i=0;i<30;i++){
                result=id.InsertDatas(i);
            }
            System.out.println(result);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
   
}

一旦数据插入完成,就可以进行下面的步骤了.

tomcat
image-1908

JSTL处理数据

新建一个动态WEB项目,名称:Test1063

从数据库中获取数据

首先创建一个bean,名称:ShopTest.
然后粘贴以下代码:

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
package bean;

public class ShopTest {
   
    private int id ;
    private String name;
    private int age ;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

在bean包下创建一个名称为:ConnDB.Properties的文件[连接字符串,用户名,密码改成你自己的.]:

1
2
3
CONN_DB_URL=jdbc:mysql://localhost:3306/DEMO
CONN_DB_USER=root
CONN_DB_PASSWORD=password

保存.
创建类:CONNDB用来对数据库的连接进行处理.

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
package bean;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;

public class CONNDB {
   
    private final static String DRIVER = "com.mysql.jdbc.Driver";
    private static String URL = null ;
    private static String USER = null;
    private static String PASSWORD = null ;
    private static Statement st = null ;
    private static Properties prop = new Properties(); 
    private static String PropFiles = "ConnDB.Properties";  //read Properties
   
    public CONNDB(){
        try{
        InputStream is = getClass().getResourceAsStream(PropFiles);
        prop.load(is);
        URL=prop.getProperty("CONN_DB_URL", URL);
        USER=prop.getProperty("CONN_DB_USER", USER);
        PASSWORD = prop.getProperty("CONN_DB_PASSWORD", PASSWORD);
        getStat();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
   
    public Statement getStat(){
        try{
        Class.forName(DRIVER);
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        st = conn.createStatement();
        }catch(Exception e){
            e.printStackTrace();
        }
        return st ;
    }

}

Servlet逻辑处理

在Java Resources–>src–>创建bean包.
然后创建Servlet,名称为:ShopServlet
主要代码如下[注意:导入相关的包]:

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
//已经省略部分代码

        /**
     * The doGet method of the servlet. <br>
     *
     * This method is called when a form has its tag value method equals to get.
     *
     * @param request the request send by the client to the server
     * @param response the response send by the server to the client
     * @throws ServletException if an error occurred
     * @throws IOException if an error occurred
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String query = request.getParameter("action");
        if (query.equals("query")){
            this.query(request,response);
        }

    }

        /**
     * List.
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void query(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
                List <Object> list = new ArrayList<Object>();
                CONNDB co = new CONNDB();
                ResultSet rs = null ;  
                Statement sts = co.getStat();
                try{
                rs = sts.executeQuery(" SELECT * FROM SHOP1063 ");
                while (rs.next()){
                    ShopTest st = new ShopTest();
                    st.setId(rs.getInt("id"));
                    st.setName(rs.getString("name"));
                    st.setAge(rs.getInt("age"));
                    list.add(st);
                }
                }catch(SQLException e){
                    e.printStackTrace();
                }
                request.setAttribute("list", list);
                request.getRequestDispatcher("query.jsp").forward(request, response);
    }

之后开始配置tomcat的web.xml,代码如下[MyEclipse会自动生成下面的数据,在当前项目的下面]:

1
2
3
4
5
6
7
8
<servlet>
    <servlet-name>ShopServlet</servlet-name>
    <servlet-class>bean.ShopServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ShopServlet</servlet-name>
    <url-pattern>/servlet/ShopServlet</url-pattern>
  </servlet-mapping>

JSTL转发数据

新建一个动态WEB项目,之后在WebContext[或者WebRoot]中创建index.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>首页</title>
  </head>
  <body>
    <c:redirect url="./ShopServlet" >
    <c:param name="action" value="query" />
    </c:redirect>
  </body>
</html>

展示数据

创建query.jsp用来展示数据,代码如下:

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
<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>所得数据</title>
</head>
<body>
    <table border="1">
        <tr>
            <td>ID</td>
            <td>姓名</td>
            <td>年龄</td>
        </tr>
        <c:forEach items="${requestScope.list}" var="goods">
            <tr>
                <td>${goods.id}</td>
                <td>${goods.name}</td>
                <td>${goods.age}</td>
                </tr>
        </c:forEach>
    </table>
</body>
</html>

完成工作

  • 启动Tomcat
  • 在终端运行ifconfig获取ip
  • 然后可以在虚拟机外面的系统,访问:[ip:8080/Test1063/index.jsp
  • 页面会跳转到query.jsp并展示从数据库中查询出来的数据.

高效率编辑器 VIM-操作篇,非常适合 VIM 新手

转.此文来自LinuxToy:[原文]

虽然从很久前就开始用 VIM 了,但一直都是半调吊子,翻来覆去只用自己会的命令。最近为了提高书写代码的效率,还有 coding 时候的乐趣,又重新钻研了一下 VIM,发现了一篇很好的 VIM 入门的文章,原文是英文版的,我觉得非常适合 VIM 使用入门,所以翻译了过来。这里是简单的介绍了 VIM 的操作方式,并没有说为什么要用 VIM,如果你想知道答案可以去 Google,VIM 被誉为编辑器之神。
这篇教程写了在不同工作模式下使用 VIM 的一些基本技巧——即插入模式(insert mode), 命令模式(command mode), 存取文件等。目的是帮助刚刚接触 VIM 的新手更加有效率的使用这个出色的编辑器。
说明:在这篇文章里面, 代表 Ctrl + X——就是按住 Ctrl 键然后再按 X。而且你可以在很多情况下使用 :help command 来获得大部分命令的帮助,这个是 VIM 的内部帮助文件命令。

vim logo
image-1900

高效率移动

在插入模式之外
基本上来说,你应该尽可能少的呆在插入模式里面,因为在插入模式里面 VIM 就像一个“哑巴”编辑器一样。很多新手都会一直呆在插入模式里面,因为这样易于使用。但 VIM 的强大之处在于他的命令行模式!你会发现,在你越来越了解 VIM 之后,你就会花越来越少的时间使用插入模式了。
使用 h、j、k、l
使用 VIM 高效率编辑的第一步,就是放弃使用箭头键。使用 VIM,你就不用频繁的在箭头键和字母键之间移来移去了,这会节省你很多时间。当你在命令模式时,你可以用 h、j、k、l 来分别实现左、下、上、右箭头的功能。一开始可能需要适应一下,但一旦习惯这种方式,你就会发现这样操作的高效之处了。
在你编辑你的电子邮件或者其他有段落的文本时,你可能会发现使用方向键和你预期的效果不一样,有时候可能会一次跳过了很多行。这是因为你的段落在 VIM 看来是一个大的长长的行。这时你可以在按 h、j、k 或者 l 之前键入一个 g,这样 VIM 就会按屏幕上面的行如你所愿的移动了。
在当前行里面有效的移动光标
很多编辑器只提供了简单的命令来控制光标的移动(比如左、上、右、下、到行首/尾等)。VIM 则提供了很多强大的命令来满足你控制光标的欲望。当光标从一点移动到另外一点,在这两点之间的文本(包括这两个点)称作被“跨过”,这里的命令也被称作是 motion。(简单说明一下,后面会用到这个重要的概念)
这里是常用到的一些命令(motion):

  • fx:移动光标到当前行的下一个 x 处。很明显,x 可以是任意一个字母,而且你可以使用 ; 来重复你的上一个 f 命令。
  • tx:和上面的命令类似,但是是移动到 x 的左边一个位置。(这真的很有用)
  • Fx:和 fx 类似,不过是往回找。
  • w:光标往前移动一个词。
  • b:光标往后移动一个词。
  • 0:移动光标到当前行首。
  • ^:移动光标到当前行的第一个字母位置。
  • $:移动光标到行尾。
  • ):移动光标到下一个句子。
  • ( :移动光标到上一个句子。

在整个文件里面有效移动光标

VIM 有很多命令,可以用来到达文件里面你想到达的地方。下面是一些在文件里面移动的命令:

  • :向下移动一屏。
  • :向上移动一屏。
  • G:到文件尾
  • numG:移动光标到指定的行(num)。(比如 10G 就是到第 10 行)
  • gg:到文件首
  • H:移动光标到屏幕上面
  • M:移动光标到屏幕中间
  • L:移动光标到屏幕下面
  • *:读取光标处的字符串,并且移动光标到它再次出现的地方。
  • #:和上面的类似,但是是往反方向寻找。
  • /text:从当前光标处开始搜索字符串 text,并且到达 text 出现的地方。必须使用回车来开始这个搜索命令。如果想重复上次的搜索的话,按 n。
  • ?text:和上面类似,但是是反方向。
  • ma:在当前光标的位置标记一个书签,名字为 a。书签名只能是小写字母。你看不见书签的存在,但它确实已经在那里了。
  • `a:到书签 a 处。注意这个不是单引号,它一般位于大部分键盘的 1 的左边。
  • `.:到你上次编辑文件的地方。这个命令很有用,而且你不用自己去标记它。

高效的输入

使用关键词自动完成

VIM 有一个非常漂亮的关键词自动完成系统。这表示,你可以输入一个长词的一部分,然后按一下某个键,然后 VIM 就替你完成了这个长词的输入了。举个例子:你有一个变量名为 iAmALongAndAwkwardVarName 在你写的代码的某个地方。也许你不想每回都自己一个一个字母的去输入它。
使用关键词自动完成功能,你只需要输入开始几个字母(比如 iAmAL),然后按 (按住 Ctrl,再按 N)或者 。如果 VIM 没有给出你想要的词,继续按,直到你满意为止,VIM 会一直循环它找到的匹配的字符串。

聪明的进入插入模式

很多新手进入插入模式都只是用 i。这样当然可以进入插入模式,但通常不是那么合适,因为 VIM 提供了很多进入插入模式的命令。下面是最常用的一些:

  • i:在当前字符的左边插入
  • I:在当前行首插入
  • a:在当前字符的右边插入
  • A:在当前行尾插入
  • o:在当前行下面插入一个新行
  • O:在当前行上面插入一个新行
  • c{motion}:删除 motion 命令跨过的字符,并且进入插入模式。比如:c$,这将会删除从光标位置到行尾的字符并且进入插入模式。ct!,这会删除从光标位置到下一个叹号(但不包括),然后进入插入模式。被删除的字符被存在了剪贴板里面,并且可以再粘贴出来。
  • d{motion}:和上面差不多,但是不进入插入模式。

有效的移动大段的文本

使用可视选择(visual selections)和合适的选择模式

不像最初的 VI,VIM 允许你高亮(选择)一些文本,并且进行操作。这里有三种可视选择模式:

  • v:按字符选择。经常使用的模式,所以亲自尝试一下它。
  • V:按行选择。这在你想拷贝或者移动很多行的文本的时候特别有用。
  • :按块选择。非常强大,只在很少的编辑器中才有这样的功能。你可以选择一个矩形块,并且在这个矩形里面的文本会被高亮。

在选择模式的时候使用上面所述的方向键和命令(motion)。比如,vwww,会高亮光标前面的三个词。Vjj 将会高亮当前行以及下面两行。

在可视选择模式下剪切和拷贝

一旦你高亮了选区,你或许想进行一些操作:

  • d:剪贴选择的内容到剪贴板。
  • y:拷贝选择的内容到剪贴板。
  • c:剪贴选择的内容到剪贴板并且进入插入模式。

在非可视选择模式下剪切和拷贝

如果你很清楚的知道你想拷贝或者剪切什么,那你根本就不需要进入可视选择模式。这样也会节省时间:

  • d{motion}:剪切 motion 命令跨过的字符到剪贴板。比如,dw 会剪切一个词而 dfS 会将从当前光标到下一个 S 之间的字符剪切至剪贴板。
  • y{motion}:和上面类似,不过是拷贝。
  • c{motion}:和 d{motion} 类似,不过最后进入插入模式。
  • dd:剪切当前行。
  • yy:拷贝当前行。
  • cc:剪切当前行并且进入插入模式。
  • D:剪切从光标位置到行尾到剪贴板.
  • Y:拷贝当前行。
  • C:和 D 类似,最后进入插入模式。
  • x:剪切当前字符到剪贴板。
  • s:和x类似,不过最后进入插入模式。

粘贴

粘贴很简单,按 p。

使用多重剪贴板

很多编辑器都只提供了一个剪贴板。VIM 有很多。剪贴板在 VIM 里面被称为寄存器(Registers)。你可以列出当前定义的所有寄存器名和它们的内容,命令为“:reg”。最好使用小写字母来作为寄存器的名称,因为大写的有些被 VIM 占用了。
使用寄存器的命令为双引号 “。
比如:我们要拷贝当前行到寄存器 k。你应该按 “kyy。(你也可以使用 V”ky。为什么这样也可以呢?)现在当前行应该已经存在了寄存器 k 里面直到你又拷贝了一些东西进入寄存器 k。现在你可以使用命令 “kp 来粘贴寄存器 k 里面的内容到你想要的位置。

避免重复

令人惊奇的 . 命令

在 VI 里面,输入 . (小数点符号),将会重复你输入的上一个命令。比如,你上个命令为“dw”(删除一个词),VI 将会接着再删除一个词。

使用数字

使用数字也是 VIM 强大的而且很节省时间的重要特性之一。在很多 VIM 的命令之前都可以使用一个数字,这个数字将会告诉 VIM 这个命令需要执行几次。比如:

  • 3j 将会把光标向下移动三行。
  • 10dd 将会删除十行。
  • y3″ 将会拷贝从当前光标到第三个出现的引号之间的内容到剪贴板。

数字是扩展 motion 命令作用域非常有效的方法。

记录宏

有时候,你会发现你自己在文章的每段或者每行都重复相同的一系列动作。VIM 允许你记录一个宏来完成你的特殊需要。

  • qregister:记录宏到寄存器 register,这里 register 是任意的你的寄存器的名字。比如 qa,将会记录并且把宏存在寄存器 a 里面。
  • q:结束宏的记录。
  • @register:使用存在寄存器 register 的宏。比如 @a,将会使用存在寄存器 a 里面的宏。

必须要记住的是,宏只记录了你的系列按键并且重复执行它们。它们不是魔法。因为在 VIM 里面完成目的的方法有很多,所以有时候你要小心选择命令来记录你的宏。因为它们会在所有你要执行它的地方执行。

用 VIM 写代码

VIM 是一个用来写代码的绝好编辑器,因为它有一些特性是专门为程序员而设计的。这里是一些常用的:

  • ]p:和 p 的功能差不多,但是它会自动调整被粘贴的文本的缩进去适应当前代码的位置。试一下!
  • %:匹配花括号、方括号、括号等。在一个括号的上面,然后按 %,鼠标就会出现在匹配的另外一半括号处。
  • >>:缩进所有选择的代码
  • <<:和上面类似,但是反缩进
  • gd:到达光标所在处函数或者变量的定义处。
  • K:在 Man 里面查找光标当前所在处的词。