星期三, 四月 25, 2007

按文件日期排序


利用Collections的sort函数,实现一个Comparator

按时间倒序的Comparator

  1. import java.io.File;
  2. import java.util.Comparator;
  3.  
  4.  
  5. /**
  6. * 比较文件时间:倒序.
  7. *
  8. * @author scud http://www.javascud.org
  9. *
  10. */
  11. public class FileDateTimeComparator implements Comparator
  12. {
  13.  
  14. public int compare(Object fileA, Object fileB)
  15. {
  16. File realFileA = (File) fileA;
  17. File realFileB = (File) fileB;
  18. return (realFileA.lastModified()<realFileB.lastModified())?1:-1;
  19. }
  20.  
  21. }

其他方法的排序的自己参考编写吧

调用方法: 

Collections.sort(fileList,new FileDateTimeComparator());

其中fileList里面存的是File类型的对象.


比较,复制文件,移动文件,重命名文件,文件与String互转,打印文件相关信息

package hartech;

import java.io.*;

/**
*Title: Javas from hartech.cn
*
*Description: JTL's File ToolKit
*Copyright: Copyright (c) 2006 hartech.cn
* Website: www.hartech.cn
*
* @author JTL.zheng@gmail.com
* @version 1.0
*/
public class JFile {

/**
* print the informations of the file input
* @param f File
*/
public static void fileInfo(String name) {
fileInfo(new File(name));
}

public static void fileInfo(File f) {
J.p("--------- Attributes of the files ----------");
if (f == null) {
J.pw("a null reference!!");
}
else if (!f.exists()) {
J.pw(f.toString() + " file Not Found!");
}
else if (f.isFile()) {
StringBuffer length = new StringBuffer(String.valueOf(f.length()));
int i = length.length() - 3;
while (i > 0) {
length.insert(i, ",");
i -= 3;
}
J.p("\"" + f.toString() + "\" is a File.");
J.p("Name: \t\t" + f.getName());
J.p("Readable: \t" + f.canRead());
J.p("Writable: \t" + f.canWrite());
J.p("AbsolutePath:\t" + f.getAbsolutePath());
J.p("Parent:\t\t" + f.getAbsoluteFile().getParent());
J.p("Length:\t\t" + length + " bytes");
}
else {
J.p("\"" + f.toString() + "\" is a Directory");
J.p("Name: \t\t" + f.getName());
J.p("Readable: \t" + f.canRead());
J.p("Writable: \t" + f.canWrite());
J.p("AbsolutePath:\t" + f.getAbsolutePath());
J.p("Parent:\t\t" + f.getAbsoluteFile().getParent());
J.p("Subfiles:\t" + f.list().length);
}
J.p("-------------- fileInfo END ---------------");
}

/**
* compare file byte by byte

* can compare any file(binary file,text file...)
* @param file1 File
* @param file2 File
* @return boolean
*/
public static boolean compareFile(String file1, String file2) {
return compareFile(new File(file1), new File(file2));
}

public static boolean compareFile(File file1, File file2) {
BufferedInputStream in1 = null, in2 = null;
try {
in1 = new BufferedInputStream(new FileInputStream(
file1));
in2 = new BufferedInputStream(new FileInputStream(
file2));
int i;
while ( (i = in1.read()) != -1) {
if (i != in2.read()) {
return false;
}
}
if (in2.read() != -1) {
return false;
}
return true;
}
catch (FileNotFoundException ex) {
J.pw("File not found!");
}
catch (IOException ex) {
J.pw("IOException!");
}
finally {
try {
in1.close();
in2.close();
}
catch (IOException ex1) {
J.pw("IOException when closing!");
}
}
return false;
}

/**
* the java.io.File.renameTo(File,String newname)
* actually move the file to newname's path and rename it which is inconvenient.

* this method just rename the file and keep where it is,ignore the move

* and return the new File's reference


* eg. file = renameFile(file,"NewName.xxx");
* @param file File
* @param name String the newName
* @return File the new File's reference
*/
public static File renameFile(String file, String name) {
return renameFile(new File(file), name);
}

public static File renameFile(File file, String name) {
File newname;
if (file == null !file.exists()) {
J.pw("File not found!");
return null;
}
if (file.getParent() == null) {
newname = new File(name);
file.renameTo(newname);
}
else {
newname = new File(file.getParentFile(), name);
file.renameTo(newname);
}
J.p("Rename is done: " + file + " -> " + newname);
return newname;
}

/**
* use java.io.File.renameTo(File,String newname) to move file


* parameters must be a file and a directory

* return a reference point to the new file
* @param scr String
* @param dir String
* @return File a reference point to the new file
*/
public static File moveFile(String scr, String dir) {
return moveFile(new File(scr), new File(dir));
}

public static File moveFile(File scr, File dir) {
if (scr == null dir == null) {
J.pw("a null reference!");
return null;
}
if (!scr.exists() !dir.exists() scr.isDirectory() dir.isFile()) {
J.pw("not file or directory or not exist!");
return null;
}
File f = new File(dir, scr.getName());
if (f.exists()) {
J.pw("target file has existed!");
}
scr.renameTo(f);
J.p("move file done: " + scr + " -> " + f);
return f;
}

/**
* turn file to String


* maybe you can use it to access file randomly through the string

* but it maybe fault when trun a big file to string
* @param file String
* @return String
*/
public static String fileToString(String file) {
String lineStr = "", string = "";
int i;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
while ( (i = in.read()) != -1) {
string += (char) i;
}
string = string.trim();
return string;
}
catch (FileNotFoundException ex) {
J.pw("File Not Found!");
}
catch (IOException ex) {
J.pw("IO exception!");
}
finally {
try {
in.close();
}
catch (IOException ex1) {
J.pw("IOException when closing!");
}
}
return null;
}

/**
* write the string to file

* if fail return false else return true
* @param src String
* @param file String
* @return boolean
*/
public static boolean stringToFile(String src, String file) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(file));
out.write(src);
return true;
}
catch (Exception ex) {
J.pw("IO exception!");
}
finally {
try {
out.close();
}
catch (IOException ex) {
J.pw("IOException when closing!");
}
}
return false;
}

/**
* only used to copy character files

* local char -> int -> unicode -> int -> local char
* @param src String
* @param dest String
*/
static public void copyFileByChar(String src, String dest) {
String lineStr;
BufferedReader in = null;
BufferedWriter out = null;
try {
in = new BufferedReader(new FileReader(src));
out = new BufferedWriter(new FileWriter(dest));
while ( (lineStr = in.
readLine()) != null) {
out.write(lineStr);
out.newLine();
}
J.p("copy is done !");
}
catch (FileNotFoundException ex) {
J.pw("File Not Found!");
}
catch (IOException ex) {
J.pw("IO exception!");
}
finally {
try {
in.close();
out.close();
}
catch (IOException ex1) {
J.pw("IOException when closing!");
}
}

}

/**
* copy file by byte

* can copy any file,because any file is made of bytes

* bytes -> int -> bytes
* @param src String
* @param dest String
*/
public static void copyFile(String src, String dest) {
copyFile(new File(src), new File(dest));
}

public static void copyFile(File src, File dest) {
int b;
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(src));
out = new BufferedOutputStream(new FileOutputStream(
dest));
while ( (b = in.read()) != -1) {
out.write(b);
}
J.p("CopyFile is done: " + src + " -> " + dest);
}
catch (FileNotFoundException ex) {
J.pw("File Not Found!");
}
catch (IOException ex) {
J.pw("IO exception!");
}
finally {
try {
in.close();
out.close();
}
catch (IOException ex1) {
J.pw("IOException when closing!");
}
}
}

static public void transformFile(String src, String dest) {
String lineStr;
int i = 0;
BufferedReader in = null;
BufferedWriter out = null;
try {
in = new BufferedReader(new FileReader(src));
out = new BufferedWriter(new FileWriter(dest));
while ( (lineStr = in.
readLine()) != null) {
// add transform codes here
// line by line
// eg. String lineStr=function(lineStr)
i = 0;
if (!lineStr.equals("")) {
while (lineStr.charAt(i) == J.SPACEC) {
lineStr = lineStr.replaceFirst(J.SPACES, J.TABS);
i++;
}
}
// code end
out.write(lineStr);
out.newLine();
}
J.p("Transform File is done: " + src + " -> " + dest);
}
catch (FileNotFoundException ex) {
J.pw("File Not Found!");
}
catch (IOException ex) {
J.pw("IO exception!");
}
finally {
try {
in.close();
out.close();
}
catch (IOException ex) {
J.pw("IOException when closing!");
}
}
}

public static String getIO() {
java.io.BufferedReader br;
br = new BufferedReader(new InputStreamReader(System.in));
try {
return br.readLine();
}
catch (IOException ex) {
return null;
}
}

public static void main(String[] arg) {
transformFile("scr/hartech/J.java", "dest.txt");
}
}

java 下复制,移动文件

import java.io.*;

public class FileOperate {
public FileOperate() {
}

/**
* 新建目录
* @param folderPath String 如 c:/fqf
* @return boolean
*/
public void newFolder(String folderPath) {
try {
String filePath = folderPath;
filePath = filePath.toString();
java.io.File myFilePath = new java.io.File(filePath);
if (!myFilePath.exists()) {
myFilePath.mkdir();
}
}
catch (Exception e) {
System.out.println("新建目录操作出错");
e.printStackTrace();
}
}

/**
* 新建文件
* @param filePathAndName String 文件路径及名称 如c:/fqf.txt
* @param fileContent String 文件内容
* @return boolean
*/
public void newFile(String filePathAndName, String fileContent) {

try {
String filePath = filePathAndName;
filePath = filePath.toString();
File myFilePath = new File(filePath);
if (!myFilePath.exists()) {
myFilePath.createNewFile();
}
FileWriter resultFile = new FileWriter(myFilePath);
PrintWriter myFile = new PrintWriter(resultFile);
String strContent = fileContent;
myFile.println(strContent);
resultFile.close();

}
catch (Exception e) {
System.out.println("新建目录操作出错");
e.printStackTrace();

}

}

/**
* 删除文件
* @param filePathAndName String 文件路径及名称 如c:/fqf.txt
* @param fileContent String
* @return boolean
*/
public void delFile(String filePathAndName) {
try {
String filePath = filePathAndName;
filePath = filePath.toString();
java.io.File myDelFile = new java.io.File(filePath);
myDelFile.delete();

}
catch (Exception e) {
System.out.println("删除文件操作出错");
e.printStackTrace();

}

}

/**
* 删除文件夹
* @param filePathAndName String 文件夹路径及名称 如c:/fqf
* @param fileContent String
* @return boolean
*/
public void delFolder(String folderPath) {
try {
delAllFile(folderPath); //删除完里面所有内容
String filePath = folderPath;
filePath = filePath.toString();
java.io.File myFilePath = new java.io.File(filePath);
myFilePath.delete(); //删除空文件夹

}
catch (Exception e) {
System.out.println("删除文件夹操作出错");
e.printStackTrace();

}

}

/**
* 删除文件夹里面的所有文件
* @param path String 文件夹路径 如 c:/fqf
*/
public void delAllFile(String path) {
File file = new File(path);
if (!file.exists()) {
return;
}
if (!file.isDirectory()) {
return;
}
String[] tempList = file.list();
File temp = null;
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
}
else {
temp = new File(path + File.separator + tempList[i]);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
delAllFile(path+"/"+ tempList[i]);//先删除文件夹里面的文件
delFolder(path+"/"+ tempList[i]);//再删除空文件夹
}
}
}

/**
* 复制单个文件
* @param oldPath String 原文件路径 如:c:/fqf.txt
* @param newPath String 复制后路径 如:f:/fqf.txt
* @return boolean
*/
public void copyFile(String oldPath, String newPath) {
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
if (oldfile.exists()) { //文件存在时
InputStream inStream = new FileInputStream(oldPath); //读入原文件
FileOutputStream fs = new FileOutputStream(newPath);
byte[] buffer = new byte[1444];
int length;
while ( (byteread = inStream.read(buffer)) != -1) {
bytesum += byteread; //字节数 文件大小
System.out.println(bytesum);
fs.write(buffer, 0, byteread);
}
inStream.close();
}
}
catch (Exception e) {
System.out.println("复制单个文件操作出错");
e.printStackTrace();

}

}

/**
* 复制整个文件夹内容
* @param oldPath String 原文件路径 如:c:/fqf
* @param newPath String 复制后路径 如:f:/fqf/ff
* @return boolean
*/
public void copyFolder(String oldPath, String newPath) {

try {
(new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
File a=new File(oldPath);
String[] file=a.list();
File temp=null;
for (int i = 0; i < file.length; i++) {
if(oldPath.endsWith(File.separator)){
temp=new File(oldPath+file[i]);
}
else{
temp=new File(oldPath+File.separator+file[i]);
}

if(temp.isFile()){
FileInputStream input = new FileInputStream(temp);
FileOutputStream output = new FileOutputStream(newPath + "/" +
(temp.getName()).toString());
byte[] b = new byte[1024 * 5];
int len;
while ( (len = input.read(b)) != -1) {
output.write(b, 0, len);
}
output.flush();
output.close();
input.close();
}
if(temp.isDirectory()){//如果是子文件夹
copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);
}
}
}
catch (Exception e) {
System.out.println("复制整个文件夹内容操作出错");
e.printStackTrace();

}

}

/**
* 移动文件到指定目录
* @param oldPath String 如:c:/fqf.txt
* @param newPath String 如:d:/fqf.txt
*/
public void moveFile(String oldPath, String newPath) {
copyFile(oldPath, newPath);
delFile(oldPath);

}

/**
* 移动文件到指定目录
* @param oldPath String 如:c:/fqf.txt
* @param newPath String 如:d:/fqf.txt
*/
public void moveFolder(String oldPath, String newPath) {
copyFolder(oldPath, newPath);
delFolder(oldPath);

}
}

星期二, 四月 24, 2007

列表排序(转)

在Java Collection Framework中定义的List实现有Vector,ArrayList和LinkedList。这些集合提供了对对象组的索引访问。他们提供了元素的添加与删除支持。然而,它们并没有内置的元素排序支持.

你能够使用java.util.Collections类中的sort()方法对List元素进行排序。你既可以给方法传递一个List对象,也可以传递一个List和一个Comparator。如果列表中的元素全都是相同类型的类,并且这个类实现了Comparable接口,你可以简单的调用Collections.sort()。如果这个类没有实现Comparator,你也可以传递一个Comparator到方法sort()中,进行排序。如果你不想使用缺省的分类顺序进行排序,你同样可以传递一个Comparator到方法sort()中来进行排序。如果列表中的元素并不都是相同类型的类,你在进行排序的时候就不是这样幸运了。除非你编写一个专用的跨类的Comparator。

  排序的顺序怎么样呢?如果元素是String对象,却省的排序顺序是按照字符编码进行的,基本上是每个字符的ASCII/Unicode值。如果严格的限制在处理英文,却省的排序顺序通常是足够的,因为它首先排A-Z,然后是小写字母a-z。然而如果你处理非英文字,或者你只是想使用不同的排序顺序,这样Collections.sort()就出现了第二种变化。例如,你想使用字符串的反序进行排序。为了实现这个功能,你可以在Collections类中通过reverseOrder()来获取一个反序Comparator。然后,你将反序Comparator传递给sort()方法。换句话说,你作如下工作:

List list = ...;
Comparator comp = Collections.reverseOrder();
Collections.sort(list, comp);


  如果列表包含项目:Man, man, Woman, 和woman,排序好的列表将是Man, Woman, man, woman。这里没有什么复杂的。需要注意的非常重要的一点是Collections.sort()是进行原位排序。如果你需要保留原序,需要先对原集合进行复制,在排序,就像这样:

List list = ...;
List copyOfList = new ArrayList(list);
Collections.sort(copyOfList);


  这里,排好序的列表是:Man, Woman, man, woman,但是原始列表(Man, man, Woman, woman)被保留了。

  到目前为止,排序是区分大小写的。你如何进行不去分大小写的排序呢?一种实现方式是象这样实现Comparator:

public static class CaseInsensitiveComparator
implements Comparator {
public int compare(Object element1,
Object element2) {
String lower1 =
element1.toString().toLowerCase();
String lower2 =
element2.toString().toLowerCase();
return lower1.compareTo(lower2);
}
}


  你确实不需要手工的创建这个类。而是,你可以是用以存在的Comparator,CASE_INSENSIVTIVE_ORDER,它是在String类中定义的。

  这种实现方式有一点小小的问题。Sort()算法提供稳定的排序,并保持与原有序列相同的元素。这意味着一个包含两个元素”woman”和”Woman”的列表将有不同的排序,而这种不同是根据两个元素在列表中出现的先后次序决定的。

  语言的不同又会怎么样呢?java.text包提供了Collector和CollectionKey类来进行区分语言的排序。这里是例子:

  注意,如果你的文本是本地语言,而不是缺省语言,你需要传递一个本地语种给getInstance()方法,就象:

public static class CollatorComparator
implements Comparator {
Collator collator = Collator.getInstance();
public int compare(Object element1,
Object element2) {
CollationKey key1 = collator.getCollationKey(
element1.toString());
CollationKey key2 = collator.getCollationKey(
element2.toString());
return key1.compareTo(key2);
}
}


  你是在对集合关键字进行排序,而不是实际的字符串。这不仅提供固定的不区分大小写的排序,而且它是跨语种的排序。换句话说,如果你对西班牙文和非西班牙文的混合词进行排序,词ma?ana (tomorrow)将排在mantra的前面。如果你不使用Collector,ma?ana将排在mantra的后面。

  下面这个程序对一个列表进行不同类型的排序(缺省的、区分大小写的、区分语种的):

import java.awt.BorderLayout;
import java.awt.Container;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

public class SortIt {

public static class CollatorComparator
implements Comparator {
Collator collator = Collator.getInstance();
public int compare(Object element1,
Object element2) {
CollationKey key1 = collator.getCollationKey(
element1.toString());
CollationKey key2 = collator.getCollationKey(
element2.toString());
return key1.compareTo(key2);
}
}

public static class CaseInsensitiveComparator
implements Comparator {
public int compare(Object element1,
Object element2) {
String lower1 = element1.toString().
toLowerCase();
String lower2 = element2.toString().
toLowerCase();
return lower1.compareTo(lower2);
}
}

public static void main(String args[]) {
String words[] =
{"man", "Man", "Woman", "woman",
"Manana", "manana", "ma?ana", "Ma?ana",
"Mantra", "mantra", "mantel", "Mantel"
};

// Create frame to display sortings
JFrame frame = new JFrame("Sorting");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
JTextArea textArea = new JTextArea();
JScrollPane pane = new JScrollPane(textArea);
contentPane.add(pane, BorderLayout.CENTER);

// Create buffer for output
StringWriter buffer = new StringWriter();
PrintWriter out = new PrintWriter(buffer);

// Create initial list to sort
List list = new ArrayList(Arrays.asList(words));
out.println("Original list:");
out.println(list);
out.println();

// Perform default sort
Collections.sort(list);
out.println("Default sorting:");
out.println(list);
out.println();

// Reset list
list = new ArrayList(Arrays.asList(words));

// Perform case insensitive sort
Comparator comp = new CaseInsensitiveComparator();
Collections.sort(list, comp);
out.println("Case insensitive sorting:");
out.println(list);
out.println();

// Reset list
list = new ArrayList(Arrays.asList(words));

// Perform collation sort
comp = new CollatorComparator();
Collections.sort(list, comp);
out.println("Collator sorting:");
out.println(list);
out.println();

// Fill text area and display
textArea.setText(buffer.toString());
frame.pack();
frame.show();
}
}


  如果你的主要问题是顺序访问,可能列表不是你的好的数据结构选择。只要你的集合没有重复,你可以在树(TreeSet)中保存你的元素(提供或不提供Comparator)。这样,元素将总是排序形式的。



作者Blog:http://blog.csdn.net/chensheng913/

VIM 使用简介(转)

来源: http://elephant.linux.net.cn/articles/vim.php?lang=zh
Vi IMproved (VIM) 是 Bram Moolenaar 开发的与 UNIX 下的通用文本编辑器 vi 兼容并且更加强大的文本编辑器。它支持语法变色、正规表达式匹配与替换、插入补全、自定义键等等功能,为编辑文本尤其是编写程序提供了极大方便。VIM 可以运行在“任何”操作系统上,包括我们常用的 Windows 和 UNIX/Linux。一旦掌握了 VIM,你就掌握了一项跨平台的利器。

尽管 VIM 功能十分强大,但对于刚接触它的人尤其是用惯类似 Windows 的 notepad 的人来说,VIM 并不十分易于掌握,毕竟它兼容的是 vi 而不是 notepad。本文旨在介绍 VIM 中我所了解的用法,希望有更多的人喜欢 VIM。应该指出的是,VIM 中有太多的功能和命令,有许多你并不用的着,因此没有记的必要,有些我也不知道,这要靠你来发现,关键是多看 :help ,多试。另外,取决于你的 VIM 的版本以及编译安装时的设置,文中讲述的某些功能或命令在你的 VIM 中也许并不存在,如有需要请升级。
1. vi 的基本用法

vi 的屏幕区域分为两个部分:最下面一行是命令行,一般用于提示信息或命令行输入;除此之外为正文显示区域。跟 notepad 不同的是,vi 中存在两种模式:普通(Normal)模式和插入(Insert)模式。

进入 vi 后默认即为普通模式。新手一般初次进入 vi 后就想输入一串字符,结果发现 vi 一连串莫名其妙的反应。其实,在 vi 的普通模式下,任何按键包括普通字符都表示某个命令,并不表示在当前光标处插入字符。常用的命令有:(注意区分大小写)
: 进入命令行
i 或 a 进入插入模式。区别是:i 进入插入模式后,光标在当前字符前面; a 进入插入模式后,光标在当前字符后面
h j k l 分别是光标左移、下移、上移、右移(一般来说你不会用到它们来移动光标,按方向键就可以了)
x 删除一个字符
dd 删除一行
J 删除本行的回车符,把下一行并入本行末尾
r字符 替换光标所在字符为新字符
^ $ 分别是光标移到行首和行末
数字G 移动光标到第若干行,如果直接按 G 则移动到最后一行

在普通模式中,命令以按键形式输入。而在命令行中,命令以字符串形式输入。下面是常用的命令行:
:q 退出! (更确切的说应该是关闭当前文件)
:w 文件名 存盘。如果还是保存为当前文件,不必写文件名
:wq 存盘退出
:new 文件名 打开或新建文件(同时关闭当前文件)。如果不指定文件名或者文件名不存在则是新建文件
:help 帮助! 看完后用 :q 关掉窗口。可以在 help 后面加某个帮助主题的名称,如 :help dd 或 :help help

还有一点是,如果某个命令得到警告(拒绝执行),则要在命令的命令词后加叹号表示强制执行。比如你修改过文件,但又想放弃存盘并退出,如果输入 :q, vi 会告诉你文件已修改,这时,你只能输入 :q! 退出。又如用 :w! a.txt 表示把当前文件存为 a.txt 而不管 a.txt 是否已经存在。

插入模式就不用多说了,添加你的新内容吧。不过,你也许会发现 BackSpace 键和 Delete 键的运用很受限制,也不能像 notepad 那样选择、复制和粘贴。这些都是由于这是 vi 的标准,后面我们将看到 VIM 扩展的功能可以解决这些问题。记住按 ESC 退出插入模式,回到普通模式。
2. 复制和粘贴

为了便于选取文本,VIM 引入了可视(Visual)模式。要选取一段文本,首先将光标移到段首,在普通模式下按 v 进入可视模式,然后把光标移到段末。需要注意,光标所在字符是包含在选区中的。这时可以对所选的文本进行一些操作,常用的(可视模式)命令有:
x 或 d 剪切(即删除,同时所选的文本进入剪贴板)
y 复制
r字符 所有字符替换为新字符
u U ~ 分别是所有字母变小写、变大写、反转大小写
> < 分别是缩进和反缩进 当输入了命令以后,VIM 将回到普通模式,这时可以按 p 或 P 进行粘贴。普通模式下有关复制和粘贴的命令: v 进入可视模式 p 或 P 在当前位置粘贴剪贴板的内容,p 粘在光标所在字符后面,P 粘在前面 不得不承认,虽然引入了可视模式,复制和粘贴在 VIM 中仍然是比较麻烦的操作,这也许是 VIM 唯一的缺点。:-) 此外,VIM 还引入了选择 (Select)模式,跟可视模式类似。结合一些键的定义和选项设置,可以实现跟 notepad 相同的复制和粘贴的使用习惯。限于篇幅和水平,在此不提。 3. VIM 的定制 VIM 在 vi 的基础上扩展了许多功能和命令,提供了许多选项。但是有些功能默认是关闭的,有些选项可能也不符合个人的使用习惯。为此,我们需要编写一个 vimrc 文件。在 DOS/Windows 版本的 VIM 中,这个文件应放在 VIM 的目录下,文件名为“_vimrc”。在 UNIX 版本 的 VIM 中,这个文件一般可以放在用户的个人主目录下,文件名为“.vimrc”。VIM 启动时将会把 vimrc 文件中的每一行作为命令行依次执行,我们可以在该文件中加入若干命令,使 VIM 启动时自动开启一些有用的功能,定义一些常用的快捷键等。 下面是一个 vimrc 文件的示例:(请注意区分浏览器的换行与实际的换行。) " 设置 Backspace 和 Delete 的灵活程度,backspace=2 则没有任何限制 set backspace=2 " 设置在哪些模式下使用鼠标功能,mouse=a 表示所有模式 set mouse=a " 设置路径,在 f 等命令中涉及此参数 " 对于 Windows 编程,path 可设为如 " set path=.,"C:\Program Files\Microsoft Visual Studio\vc98\Include",, " 对于 UNIX 编程,path 可设为如下 set path=.,/usr/include,/usr/include/qt,, " 打开光标的行列位置显示功能 set ruler " 设置跳格距离 set tabstop=4 " 设置自动缩进格数 set shiftwidth=4 " 打开自动缩进功能 set autoindent " 设置哪些键可以行间绕转,如下设置则 Backspace 和方向键等均可行间绕转 set whichwrap=b,s,h,l,<,>,[,]
" 根据当前文件语法自动变色。VIM 识别上百种文本文件的语法,如 html、c++、java 等
syntax on
" 以下是个人习惯,定义 等键,便于插入模式和可视模式之间的切换
" (1) 使 x d y 自动返回插入模式
vnoremap y "ryi
vnoremap x "rxi
vnoremap d di
" (2) 普通模式和插入模式下均可按 粘贴
imap :if col(".")!=1 exe 'normal "rp'elseexe 'normal "rP'endif`[i
nmap "rP
" (3) 普通模式和插入模式下均可按 进入可视模式
imap :if col(".")!=1 exe 'normal lv'elseexe 'normal v'endif
nmap v

下面给出我常用的 vimrc 文件。另外,你的 VIM 安装目录下很可能也带有一个默认的 vimrc 文件,你都可以参考。如果遇到没有见过的命令或者参数,可以自己看看 :help 。

* vimrc

4. VIM 的其它命令

要真正使用 VIM,光靠 vi 的基本命令当然不行,下面就来介绍更多的命令。以下的命令,有些是 VIM 特有的,有些在 vi 中也存在,我就不加区分了。其中,以“:”开头表示该命令在命令行输入,以“i”开头表示这是插入模式下的命令,其它则是普通模式下的命令。 表示按 Ctrl+X。
4.1 使用帮助

在 :help 中,遇到超连接可以按 Ctrl+] 跳转
在 :help 中,按 Ctrl+T 往回跳转

4.2 打开多个文件

:split 文件名 切分出一个新窗口,打开指定文件。如果省略文件名,则仍显示当前文件,可用于同时观察文件的不同部分。(注意跟 :new 的区别)
f 切分显示光标所指的文件名,VIM 会在 path 中搜索该文件名,比如常用它打开 #include 语句中的文件
当同时打开几个文件时,按 在各窗口之间切换
_ 当同时打开几个文件时,按 _ 使当前窗口最大化
:set scrollbind 设置卷动绑定属性。所有设置了卷动绑定属性的窗口将一起卷动。可以用 :set noscrollbind 解除绑定

4.3 撤销和恢复

编辑过程中出现错误在所难免,不过没有关系,VIM 允许无限次的撤销。只要你没有关闭文件,你甚至可以一直撤销下去,回到几个小时以前刚打开这个文件开始工作时的状态。
u 撤销(Undo)上次所做的修改
恢复(Redo)上次撤销的内容

4.4 字符串搜索替换

/字符串 向下搜索字符串
?字符串 向上搜索字符串
* # 分别是向下和向上搜索光标所指的词
n 重复上一次搜索
:起始行,结束行s/搜索串/替换串/g 从起始行到结束行,把所有的搜索串替换为替换串
:set ignorecase 设置忽略字母大小写。可以用 :set noignorecase 取消忽略字母大小写

例如 /hello 从当前光标位置开始向下搜索 hello,不带字符串的命令 / 可重复上一次搜索,相当于 n。又如 :1,$ s/hello/hi/g 把全文中的 hello 改为 hi,其中 $ 表示最后一行。另外,你还可以先进入可视模式选择一段文本,按 :进入命令行并输入 s/hello/hi/g ,VIM 将在选区中进行替换操作。

搜索字符串用的是正规表达式(Regular expression),其中许多字符都有特殊含义:
\ 取消后面所跟字符的特殊含义。比如 \[vim\] 匹配字符串“[vim]”
[] 匹配其中之一。比如 [vim] 匹配字母“v”、“i”或者“m”,[a-zA-Z] 匹配任意字母
[^] 匹配非其中之一。比如 [^vim] 匹配除字母“v”、“i”和“m”之外的所有字符
. 匹配任意字符
* 匹配前一字符大于等于零遍。比如 vi*m 匹配“vm”、“vim”、“viim”……
\+ 匹配前一字符大于等于一遍。比如 vi\+m 匹配“vim”、“viim”、“viiim”……
\? 匹配前一字符零遍或者一遍。比如 vi\?m 匹配“vm”或者“vim”
^ 匹配行首。例如 /^hello 查找出现在行首的单词 hello
$ 匹配行末。例如 /hello$ 查找出现在行末的单词 hello
\(\) 括住某段正规表达式
\数字 重复匹配前面某段括住的表达式。例如 \(hello\).*\1 匹配一个开始和末尾都是“hello”,中间是任意字符串的字符串

对于替换字符串,可以用“&”代表整个搜索字符串,或者用“\数字”代表搜索字符串中的某段括住的表达式。

举一个复杂的例子,把文中的所有字符串“abc……xyz”替换为“xyz……abc”可以有下列写法:
:%s/abc\(.*\)xyz/xyz\1abc/g
:%s/\(abc\)\(.*\)\(xyz\)/\3\2\1/g
其它关于正规表达式搜索替换的更详细准确的说明请看 :help pattern 。
4.5 插入补全

在插入模式下,为了减少重复的击键输入,VIM 提供了若干快捷键,当你要输入某个上下文曾经输入过的字符串时,你只要输入开头若干字符,使用快捷键, VIM 将搜索上下文,找到匹配字符串,把剩下的字符补全,你就不必敲了。这样,编程序时你起多长的变量名都没关系了,:-) 而且还可以减少输入错误。我认为,插入补全是 VIM 最为突出的一项功能。
i 向上搜索,补全一个词。例如,上文中出现过 filename 这个词,当你想再输入 filename 时,只要按 f 即可。假如 VIM 向上搜索,找到以 f 开头的第一个匹配不是 filename,你可以继续按 搜索下一个匹配进行补全。当然,如果你想一次 就成功,你可以多输入几个字符比如 filen 再按 补全
i 向下搜索,补全一个词
i 补全一行。比如你写过一行 for (int i = 0; i <> 即可。如果补全出来的不是你想要的那一行,你可以按 或 选择上一个或下一个匹配行
i 在文件系统中搜索,补全一个文件名

如果按 或 补全一个词,在当前文件中没有找到匹配,VIM 将搜索 #include 语句中的文件,而文件的位置将在 path 中搜索。
4.6 键的定义

在 VIM 中你可以定义一个键,按了这个键等于按了某一串预定的键。比如
:map! ddi
表示如果你在插入模式下按 就相当于连续按了 ddi,这将会使 VIM 退回到普通模式,删除一行,再进入插入模式。

map 命令有许多变化形式,每种变化形式所定义的键只在某些模式下有效,而在其它模式下无效。你需要根据情况使用正确的变化形式:
:nmap 键只对普通模式有效
:imap 键只对插入模式有效
:vmap 键只对可视模式有效
:cmap 键只在命令行下有效
:map 键在普通模式和可视模式都有效
:map! 键在插入模式和命令行下都有效

还要注意,如果你定义 :map d di 这将引起循环定义错误。这时,你需要使用 :noremap d di 来定义。同样,noremap 也有对不同模式的变化形式。

比如,你想在文件的每一行的倒数第二个字符处插入字符串“abc”,你可以定义
:nmap $hiabcj
在普通模式下按一次 将会:光标移到行末,光标左移一格,进入插入模式,输入“abc”,退回到普通模式,光标下移一行。不停地按 将解决问题。这是我临时处理多行重复操作的常用手段,当然,这是一种笨办法。:-) 而上文写到的对 的定义则是 map 更复杂的用法。你还可以定义
:map :w:!gcc -o %< -I/usr/include/qt -lqt %
:map :!./%<
实现按 编译当前文件,按 执行。

在插入补全当中提到的补全一行需要按 ,如果你觉得麻烦,你可以定义
:inoremap
减少击键次数。同样,对 也可以作类似定义。
4.7 其它命令

:!命令行 执行一条外部命令
. 在光标当前位置处重复上一次操作
i后续字符 输入特殊的 ASCII 字符或键。除了插入模式外,也适用于命令行。后续字符可以是键盘上的任意键,也可以是三位的十进制数字表示字符的 ASCII 码
i命令 执行一个普通模式的命令,执行完毕后回到插入模式
i 跳转到光标所指标识符的定义行。比如你在编程时遇到一个函数 CreateWindow,想看它的定义语句,你就可以在它上面按 i,VIM 将打开新窗口,把光标移到它定义的地方。当然,前提是在当前文件或它的 #include 文件中存在 CreateWindow 的定义。这也牵涉到 path 的设置。不过,VIM 找得不一定很准
K 看光标所指标识符的 man 帮助页
i 把上一行对应列的字符抄下来
i 把下一行对应列的字符抄上来(写这一行时我就用了 )
光标所指整数加一
光标所指整数减一
光标返回到以前的位置。相当于光标移动的“撤销”
光标返回到后来的位置。相当于光标移动的“恢复”
进入可视模式,选取一个矩形区域。该命令通常用于对多行进行列操作。接着按 I 可以在块前的每一行同时插入字符;按 A 可以在块后的每一行同时插入字符;按 x、d 或 y 可以剪切或复制;等等
:X 对当前文件加密

5. VIM 6.0 的一些新特性

2001 年 9 月 26 日发布的 VIM 6.0 增加了一些以往没有的新特性。下面仅仅是一些简单的介绍,更详细的描述请自己看 :help 。

* 打开目录

在 VIM 6.0 中,:new 和 :split 等命令不但可以打开普通文件,还可以打开目录。一个目录打开以后将列出里面的文件信息,可以按回车继续打开相应的文件或者子目录,也可以按 ? 得到其它目录操作(修改文件名,删除文件等)的帮助。
* 折叠

当一个文本太长而你又对其中很长一大段内容不关心的话,可以把你不关心的那些行折叠起来,让它们从你的视线中消失。被折叠的行将以一行显示代替,例如:
+--217 行:2. VIM 的定制---------------------
折叠可以有多种方式控制,可以通过设置 foldmethod 选项的值来改变。默认情况下 foldmethod=manual 为手工折叠。下面介绍几个使用折叠的最简单的命令:
(可视模式下) zf 手工创建折叠。在可视模式下选择一段文本,然后按 zf 可以手工创建一个折叠
方向键左或右 打开折叠。普通或插入模式下,在折叠行上横向移动光标将打开被折叠的行
zc 关闭折叠

* 垂直切分窗口

:vsplit 文件名 垂直切分窗口。跟 :split 命令相似,但新窗口与原窗口左右并列。

* 更多的正规表达式

VIM 6.0 比以前增加了许多新的正规表达式,其中我认为最有用的是换行匹配符 \n。以前的版本中,正规表达式只能在同一行上匹配。现在,只要显式地给出 \n,正规表达式可以跨多行。
* diff 模式

专门用于比较编辑两个或多个内容相近的文件的模式。一般来说,比如你要比较编辑 A 跟 B 两个文件,你可以命令行启动 vim - d A B 或者这样:先打开文件 A,然后 :vsplit 打开文件 B,对文件 A 和 B 都输入命令 :diffthis。这时 VIM 将非常清晰的对比显示出两个文件的不同之处,编辑起来十分方便。