|
2007年8月29日
package cc.dynasoft.struts.action;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.Region;
import cc.dynasoft.bean.Department;
///import org.apache.poi.hssf.record.HeaderRecorder;
public class OutputExcel {
public static boolean outputExcel(ExcelArgs args, List title, List list) {
try {
int cellNum = args.getCellNum(); // workbook
int rowNum = args.getRowNum();
/**
* 建立表格设置。
*/
HSSFWorkbook wb = new HSSFWorkbook(); // create the new Workbook
HSSFSheet sheet = wb.createSheet(args.getSheetName()); // create
/**
* 打印设置
*/
HSSFPrintSetup hps = sheet.getPrintSetup();
hps.setPaperSize((short) 9); // 设置A4纸
// hps.setLandscape(true); // 将页面设置为横向打印模式
sheet.setHorizontallyCenter(true); // 设置打印页面为水平居中
// sheet.setVerticallyCenter(true); // 设置打印页面为垂直居中
wb.setPrintArea(0, "$A$2:$e$" + rowNum + 2);// 打印区域设置.
/**
* 设置表的Footer
*/
HSSFFooter footer = sheet.getFooter();
// 设置footer的位置和显示的内容
footer.setCenter("Time:" + HSSFFooter.date());
footer.setRight("Page " + HSSFFooter.page() + " of "
+ HSSFFooter.numPages());
/**
* 设置表的Header
*/
// 设置header的位置,共有三种位置和相应的显示设置
HSSFHeader header = sheet.getHeader();
// header.setRight("Center Header");
// header.setLeft("Left Header");
header.setCenter(HSSFHeader.font("Stencil-Normal", "Italic")
+ HSSFHeader.fontSize((short) 30) + args.getHeaderTitle());
// header.endDoubleUnderline();
header.startUnderline();
/**
* 设置列的宽度
*/
sheet.setColumnWidth((short) 2,
(short) ((30 * 8) / ((double) 1 / 10)));
sheet.setColumnWidth((short) 3,
(short) ((40 * 8) / ((double) 1 / 10)));
sheet.setColumnWidth((short) 4,
(short) ((50 * 8) / ((double) 1 / 20)));
/**
* 创建第一行,也就是显示的标题, 可以高置的高度,单元格的格式,颜色,字体等设置. 同时可以合并单元格.
*/
HSSFRow row0 = sheet.createRow(0); // 创建0行
row0.setHeight((short) 0x300); // 设直行的高度.
HSSFFont font2 = wb.createFont(); // 创建字体格式
font2.setColor(HSSFFont.SS_NONE); // 设置单元格字体的颜色.
font2.setFontHeight((short) 700); // 设置字体大小
font2.setFontName("Courier New"); // 设置单元格字体
HSSFCell cell0 = row0.createCell((short) 0); // 创建0行0列.
HSSFCellStyle style3 = wb.createCellStyle(); // 创建单元格风格.
style3.setAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
style3.setAlignment(HSSFCellStyle.ALIGN_CENTER); // /水平居中
style3.setFont(font2); // 将字体格式加入到单元格风格当中
// cell0.setCellType()
cell0.setCellStyle(style3); // 设置单元格的风格.
cell0.setCellValue(args.getHeaderTitle()); // 设置单元的内容.
sheet.addMergedRegion(new Region(0, (short) 0, 0,
(short) (cellNum - 1)));// 指定合并区域,前二个参数为开始处X,Y坐标.后二个为结束的坐标.
/**
* 设置其它数据 设置风格
*/
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_THIN); // 设置单无格的边框为粗体
style.setBottomBorderColor(HSSFColor.BLACK.index); // 设置单元格的边框颜色.
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.BLACK.index);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setRightBorderColor(HSSFColor.BLACK.index);
style.setBorderTop(HSSFCellStyle.BORDER_THIN);
style.setTopBorderColor(HSSFColor.BLACK.index);
// style.setWrapText(true);//文本区域随内容多少自动调整
// style.setFillForegroundColor(HSSFColor.LIME.index);
// style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
/**
* 设置风格1
*/
HSSFCellStyle style1 = wb.createCellStyle();
style1.setBorderBottom(HSSFCellStyle.BORDER_THIN); // 设置单无格的边框为粗体
style1.setBottomBorderColor(HSSFColor.BLACK.index); // 设置单元格的边框颜色.
style1.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style1.setLeftBorderColor(HSSFColor.BLACK.index);
style1.setBorderRight(HSSFCellStyle.BORDER_THIN);
style1.setRightBorderColor(HSSFColor.BLACK.index);
style1.setBorderTop(HSSFCellStyle.BORDER_MEDIUM);
style1.setTopBorderColor(HSSFColor.BLACK.index);
style1.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);// 最好的设置Pattern
// 单元格背景的显示模式.
style1.setFillForegroundColor(new HSSFColor.RED().getIndex()); // 设置单元格背景色;
style1.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平对齐方式
// style1.setWrapText(true);//文本区域随内容多少自动调整
// style.setFillPattern(HSSFCellStyle.//);
// 设置字体Color,首先创建Font对象,后对font设置,然后做为参数传给style
HSSFFont font = wb.createFont();
font.setColor(HSSFFont.SS_NONE);
// font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
// font.setItalic(true);
// font.setStrikeout(true);//给字体加上删除线
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
style1.setFont(font);
/**
*
* 设置第零行表格说明行
*
*
*
*/
HSSFRow row1 = sheet.createRow((short) 1);
for (int j = 0; j < cellNum; j++) {
HSSFCell cell = row1.createCell((short) j);
cell.setCellValue((String) title.get(j));
cell.setCellStyle(style1);
}
// style.setFillPattern(HSSFCellStyle.NO_FILL);
/**
* 设置表的内容主体
*/
Iterator iter = list.iterator();
for (int i = 2; iter.hasNext(); i++) {
Department dep = (Department) iter.next();
HSSFRow row = sheet.createRow((short) i);
HSSFCell cell5 = row.createCell((short) 0);
HSSFCell cell1 = row.createCell((short) 1);
HSSFCell cell2 = row.createCell((short) 2);
HSSFCell cell3 = row.createCell((short) 3);
HSSFCell cell4 = row.createCell((short) 4);
cell5.setCellValue(dep.getId());
cell5.setCellStyle(style);
cell1.setCellValue(dep.getParentId());
cell1.setCellStyle(style);
cell2.setCellValue(dep.getName());
cell2.setCellStyle(style);
cell3.setCellValue(dep.getDescription());
cell3.setCellStyle(style);
cell4.setCellValue(dep.getImagePath());
cell4.setCellStyle(style);
}
// Write the output to a file}
// FileOutputStream fileOut = new
// FileOutputStream(args.getPath()+args.getFileName());
/**
* 对文件进行输出操作。
*/
FileOutputStream fileOut = new FileOutputStream(args
.getPathAndName());
wb.write(fileOut);
// fileOut.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
}
mkdir /home/u1 创建文件夹/home/u1
chown oracle /home/u1 表示改变目录所有者为oracle账户;
chgrp dba /home/u1 改变/home/u1目录为dba所有;
chmod 755 /home/u1 表示oracle账户对/home/u1目录有755权限;
rmdir /home/u1 表示删除/home/u1目录
hostname可以查看linux的计算机名;
whoami可以查看当前用户;
pwd显示当前路径;
df查看系统的硬件信息
ls -lrt l表示显示详细列表,-t表示按时间排序,-r反向排序
cat orcl_ora_3436.trc|grep bucket
以下查看相关文件内容:
more /etc/oratab
cat /etc/passwd
cat /etc/group
copy命令
该命令的功能是将给出的文件或目录拷贝到另一文件或目录中,同MSDOS下的copy命令一样,功能十分强大。
语法: cp [选项] 源文件或目录 目标文件或目录
说明:该命令把指定的源文件复制到目标文件或把多个源文件复制到目标目录中。
该命令的各选项含义如下:
- a 该选项通常在拷贝目录时使用。它保留链接、文件属性,并递归地拷贝目录,其作用等于dpR选项的组合。
- d 拷贝时保留链接。
- f 删除已经存在的目标文件而不提示。
- i 和f选项相反,在覆盖目标文件之前将给出提示要求用户确认。回答y时目标文件将被覆盖,是交互式拷贝。
- p 此时cp除复制源文件的内容外,还将把其修改时间和访问权限也复制到新文件中。
- r 若给出的源文件是一目录文件,此时cp将递归复制该目录下所有的子目录和文件。此时目标文件必须为一个目录名。
- l 不作拷贝,只是链接文件。
需要说明的是,为防止用户在不经意的情况下用cp命令破坏另一个文件,如用户指定的目标文件名已存在,用cp命令拷贝文件后,这个文件就会被新源文件覆盖,因此,建议用户在使用cp命令拷贝文件时,最好使用i选项。
对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,而且rownum不能以任何表的名称作为前缀。
举例说明:
例如表:student(学生)表,表结构为:
ID char(6) --学号
name VARCHAR2(10) --姓名
create table student (ID char(6), name VARCHAR2(100));
insert into sale values('200001',‘张一’);
insert into sale values('200002',‘王二’);
insert into sale values('200003',‘李三’);
insert into sale values('200004',‘赵四’);
commit;
(1) rownum 对于等于某值的查询条件
如 果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因 为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标)
SQL> select rownum,id,name from student where rownum=1;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
1 200001 张一
SQL> select rownum,id,name from student where rownum =2;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
(2)rownum对于大于某值的查询条件
如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录
SQL> select rownum,id,name from student where rownum >2;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
那如何才能找到第二行以后的记录呀。可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
SQL>select * from(select rownum no ,id,name from student) where no>2;
NO ID NAME
---------- ------ ---------------------------------------------------
3 200003 李三
4 200004 赵四
SQL> select * from(select rownum,id,name from student)where rownum>2;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
(3)rownum对于小于某值的查询条件
如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。
SQL> select rownum,id,name from student where rownum <3;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
1 200001 张一
2 200002 王二
综 上几种情况,可能有时候需要查询rownum在某区间的数据,那怎么办呀从上可以看出rownum对小于某值的查询条件是人为true的,rownum对 于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之 间的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记 录行。但是这样的操作会在大数据集中影响速度。
SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2;
NO ID NAME
---------- ------ ---------------------------------------------------
2 200002 王二
3 200003 李三
(4)rownum和排序
Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。
SQL> select rownum ,id,name from student order by name;
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
3 200003 李三
2 200002 王二
1 200001 张一
4 200004 赵四
可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询
SQL> select rownum ,id,name from (select * from student order by name);
ROWNUM ID NAME
---------- ------ ---------------------------------------------------
1 200003 李三
2 200002 王二
3 200001 张一
4 200004 赵四
这样就成了按name排序,并且用rownum标出正确序号(有小到大)
//ArrayList
{
ArrayList arraylist=new ArrayList();
arraylist.add(0,"end");//指定索引加入值
//需注意的是,如果现有2个值,我加入索引为5的那么就会出现异常
for(int i=0;i<2;i++){
arraylist.add(i,String.valueOf(i));
}
System.out.println("ArrayList:");
for(int i=0;i<arraylist.size();i++){
System.out.print(arraylist.get(i)+";");
}
arraylist.add("0");//直接加入值到ArrayList的最后
arraylist.add("0");
System.out.print("\nArrayList\'s lastIndexOf(\"0\") is "+arraylist.lastIndexOf("0"));
}
//Arrays
{
String []array=new String[]{"a","b","c"};
List list=Arrays.asList(array);
System.out.println("\nArrays:");
for(int i=0;i<list.size();i++){
System.out.print(list.get(i)+";");
}
System.out.print("\nArrays\'s length is "+array.length);//打印数组的长度
}
//Collections
{
String []array=new String[]{"a","b","c"};
List list=Arrays.asList(array);
Collections.fill(list,"Fill");//用Fill填充全部元素
System.out.println("\nCollections:");
for(int i=0;i<list.size();i++){
System.out.print(list.get(i)+";");
}
array=new String[]{"1","2","3"};
List list2=Arrays.asList(array);
Collections.copy(list,list2);//拷贝list2的数据进list
System.out.println("\n"+list);
Collections.swap(list,2,1);//调换索引为1和2的元素的位置
System.out.println(list);
}
//EventObject
{
String s="hello";
String s2=s;
EventObject eventobject=new EventObject(s);//一个准容器类型,确切的归类它不是容器
System.out.println("EventObject:");
System.out.println(eventobject.getSource());
System.out.println(eventobject.equals(s2));
}
//HashMap
{
HashMap hashmap=new HashMap();//一个速度最快的容器
hashmap.put("0","c");
hashmap.put("1","a");
hashmap.put("2","b");
hashmap.put("3","a");
System.out.println("HashMap:");
System.out.println(hashmap);//该容器有其内部的排序方式
Set set=hashmap.keySet();//获取全部键
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.print(hashmap.get(iterator.next())+";");
}
}
//HashSet
{
HashSet hashset=new HashSet();//一个绝对不能重复的类型
hashset.add("c");
hashset.add("b");
hashset.add("a");
hashset.add("a");
hashset.add("b");
System.out.println("\nHashSet:");
System.out.println(hashset);
Iterator iterator=hashset.iterator();//取出元素
while(iterator.hasNext()){
System.out.print(iterator.next()+";");
}
}
//Hashtable
{
Hashtable hashtable=new Hashtable();//一个完全可以由其他容器替换的老容器类型
hashtable.put("0","c");
hashtable.put("1","a");
hashtable.put("3","c");
hashtable.put("2","b");
System.out.println("\nHashtable:");
Enumeration enumeration=hashtable.elements();//获取元素,Enumeration已经不是主流,Iterator是它的下一代替代品
while(enumeration.hasMoreElements()){
System.out.print(enumeration.nextElement()+";");
}
}
//IdentityHashMap
{
IdentityHashMap identityhashmap=new IdentityHashMap();
identityhashmap.put("0","c");
identityhashmap.put("1","a");
identityhashmap.put("3","b");
identityhashmap.put("2","a");
System.out.println("\nIdentityHashMap:");
System.out.println(identityhashmap);
System.out.println(identityhashmap.containsKey("3"));//是否包含这个键
System.out.println(identityhashmap.containsValue("a"));//是否包含值
Set set=identityhashmap.entrySet();//传为Set类型
System.out.println(set);
set=identityhashmap.keySet();//全部键
System.out.println(set);
}
//LinkedHashMap
{
LinkedHashMap linkedhashmap=new LinkedHashMap();
linkedhashmap.put("0","b");
linkedhashmap.put("2","a");
linkedhashmap.put("1","c");
linkedhashmap.put("3","b");
System.out.println("LinkedHashMap:");
System.out.println(linkedhashmap);
System.out.println(linkedhashmap.containsKey("2"));//是否包含这个键
System.out.println(linkedhashmap.containsValue("c"));//是否包含值
Set set=linkedhashmap.keySet();
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.print(linkedhashmap.get(iterator.next())+";");
}
}
//LinkedHashSet
{
LinkedHashSet linkedhashset=new LinkedHashSet();//它包含了几种Set的属性但却没有自己的特色
linkedhashset.add("c");
linkedhashset.add("a");
linkedhashset.add("a");
linkedhashset.add("b");
System.out.println("\nLinkedHashSet:");
System.out.println(linkedhashset);
System.out.println(linkedhashset.contains("a"));//是否包含对象
Iterator iterator=linkedhashset.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next()+";");
}
}
//LinkedList
{
LinkedList linkedlist=new LinkedList();//自由使用是它的特色
linkedlist.add("a");
linkedlist.add(1,"c");
linkedlist.addLast("b");
linkedlist.addFirst("d");
System.out.println("\nLinkedList:");
System.out.println(linkedlist);
//linkedlist.clear();//该方法清空容器
//linkedlist.remove(0);//删除索引为0的元素
//linkedlist.remove("d");//删除值为d的元素
//linkedlist.removeFirst();//删除第一个元素
//linkedlist.removeLast();//删除最后一个元素
for(int i=0;i<linkedlist.size();i++){
System.out.print(linkedlist.get(i)+";");
}
}
//Stack
{
Stack stack=new Stack();//堆栈
stack.add("b");
stack.add(0,"c");
stack.push("d");
stack.add("e");
stack.push("a");
Enumeration enumeration=stack.elements();
System.out.println("\nStack:");
while(enumeration.hasMoreElements()){
System.out.print(enumeration.nextElement()+";");
}
//后进先出
System.out.println("\n"+stack.peek());
System.out.println(stack.pop());
System.out.println(stack.contains("d")+";"+stack.contains("a"));//是否包含该元素,有趣的事情发生了
System.out.println(stack.search("c"));//非常有用的属性,检索,但是由后向前的排列
}
//TreeMap
{
TreeMap treemap=new TreeMap();
treemap.put("0","d");
treemap.put("2","a");
treemap.put("1","b");
treemap.put("3","c");
System.out.println("\nTreeMap:");//可以对键排序
System.out.println(treemap);
System.out.println(treemap.firstKey());//返回第一个键
Set set=treemap.keySet();
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.print(treemap.get(iterator.next())+";");
}
}
//TreeSet
{
TreeSet treeset=new TreeSet();//自动排序内容
treeset.add("b");
treeset.add("a");
treeset.add("c");
treeset.add("d");
System.out.println("\nTreeSet:");
System.out.println(treeset);
System.out.println(treeset.first());//返回第一个元素
Iterator iterator=treeset.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next()+";");
}
}
//Vector
{
Vector vector=new Vector();
vector.add(0,"b");
vector.add("a");
vector.addElement("d");
vector.add("c");
System.out.println("\nVector:");
System.out.println(vector);
vector.set(2,"h");//替换掉指定索引的元素
System.out.println(vector);
Object []str=vector.toArray();
for(int i=0;i<str.length;i++){
System.out.print(str[i]+";");
}
vector.setSize(2);//重新设置大小为2
System.out.println("\n"+vector);
}
//WeakHashMap
{
WeakHashMap weakhashmap=new WeakHashMap();
weakhashmap.put("1","b");
weakhashmap.put("2","c");
weakhashmap.put("0","d");
weakhashmap.put("3","a");
System.out.println("\nWeakHashMap:");
System.out.println(weakhashmap);
System.out.println(weakhashmap.containsKey("3"));//是否包含键
System.out.println(weakhashmap.containsValue("d"));//是否包含值
Set set=weakhashmap.entrySet();
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next()+";");
}
//weakhashmap.remove("2");//删除该键对应的值
//weakhashmap.get("1");//获取指定键的值
}
}
在JAVA的util包中有两个所有集合的父接口Collection和Map,它们的父子关系:
java.util
+Collection 这个接口extends自 --java.lang.Iterable接口
+List 接口
-ArrayList 类
-LinkedList 类
-Vector 类 此类是实现同步的
+Queue 接口
+不常用,在此不表.
+Set 接口
+SortedSet 接口
-TreeSet 类
-HashSet
+Map 接口
-HashMap 类 (除了不同步和允许使用 null 键/值之外,与 Hashtable 大致相同.)
-Hashtable 类 此类是实现同步的,不允许使用 null 键值
+SortedMap 接口
-TreeMap 类
以下对众多接口和类的简单说明:首先不能不先说一下数组(Array)
一、Array , Arrays
Java所有“存储及随机访问一连串对象”的做法,array是最有效率的一种。
1、
效率高,但容量固定且无法动态改变。
array还有一个缺点是,无法判断其中实际存有多少元素,length只是告诉我们array的容量。
2、Java中有一个Arrays类,专门用来操作array。
arrays中拥有一组static函数,
equals():比较两个array是否相等。array拥有相同元素个数,且所有对应元素两两相等。
fill():将值填入array中。
sort():用来对array进行排序。
binarySearch():在排好序的array中寻找元素。
System.arraycopy():array的复制。
二、Collection , Map
若撰写程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用容器类库,array不适用。
1、Collection 和 Map 的区别
容器内每个为之所存储的元素个数不同。
Collection类型者,每个位置只有一个元素。
Map类型者,持有 key-value pair,像个小型数据库。
2、Java2容器类类库的用途是“保存对象”,它分为两类,各自旗下的子类关系
Collection
--List:将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。
--ArrayList / LinkedList / Vector
--Set : 不能含有重复的元素
--HashSet /TreeSet
Map
--HashMap
--HashTable
--TreeMap
Map----一组成对的“键值对”对象,即其元素是成对的对象,最典型的应用就是数据字典,并且还有其它广泛的应用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且还可以像数组一样扩展多维Map,只要让Map中键值对的每个“值”是一个Map即可。
Collection下 1.迭代器
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
2.List的功能方法
List(interface): 次序是List最重要的特点;它确保维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(只推荐LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和删除元素。
ArrayList: 由数组实现的List。它允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历ArrayList,而不是用来插入和删除元素,因为这比LinkedList开销要大很多。
LinkedList: 由列表实现的List。对顺序访问进行了优化,向List中间插入与删除得开销不大,随机访问则相对较慢(可用ArrayList代替)。它具有方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast(),这些方法(没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。
3.Set的功能方法
Set(interface): 存入Set的每个元素必须是唯一的,这也是与List不同的,因为Set不保存重复元素。加入Set的Object必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet: HashSet能快速定位一个元素,存入HashSet的对象必须定义hashCode()。
TreeSet: 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。
LinkedHashSet: 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
HashSet采用散列函数对元素进行排序,这是专门为快速查询而设计的;TreeSet采用红黑树的数据结构进行排序元素;LinkedHashSet内部使用散列以加快查询速度,同时使用链表维护元素的次序,使得看起来元素是以插入的顺序保存的。需要注意的是,生成自己的类时,Set需要维护元素的存储顺序,因此要实现Comparable接口并定义compareTo()方法。
3、其他特征
* List,Set,Map将持有对象一律视为Object型别。
* Collection、List、Set、Map都是接口,不能实例化。
继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。
* vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。
三、Collections
Collections是针对集合类的一个帮助类。提供了一系列静态方法实现对各种集合的搜索、排序、线程完全化等操作。
相当于对Array进行类似操作的类——Arrays。
如,Collections.max(Collection coll); 取coll中最大的元素。
Collections.sort(List list); 对list中元素排序
四、如何选择?
1、容器类和Array的区别、择取
* 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。
* 一旦将对象置入容器内,便损失了该对象的型别信息。
2、
* 在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList();
Vector总是比ArrayList慢,所以要尽量避免使用。
* 在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。
HashTree存在的唯一理由:能够维护其内元素的排序状态。
* 在各种Maps中
HashMap用于快速查找。
* 当元素个数固定,用Array,因为Array效率是最高的。
结论:最常用的是ArrayList,HashSet,HashMap,Array。而且,我们也会发现一个规律,用TreeXXX都是排序的。
注意:
1、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
2、Set和Collection拥有一模一样的接口。
3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。
5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。
HashMap会利用对象的hashCode来快速找到key。
* hashing
哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。
我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。
发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。
6、Map中元素,可以将key序列、value序列单独抽取出来。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。
为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。
1、项目经理必须关注项目成功的三个标准
简单地说,一是准时;二是预算控制在既定的范围内;三是质量得到经理和用户们的赞许。项目经理必须保证项目小组的每一位成员都能对照上面三个标准来进行工作。
2、任何事都应当先规划再执行
就项目管理而言,很多专家和实践人员都同意这样一个观点:需要项目经理投入的最重要的一件事就是规划。只有详细而系统的由项目小组成员参与的规划才是项目成功的唯一基础。当现实的世界出现了一种不适于计划生存的环境时,项目经理应制定一个新的计划来反映环境的变化。规划、规划、再规划就是项目经理的一种生活方式。
3、项目经理必须以自己的实际行动向项目小组成员传递一种紧迫感
由于项目在时间、资源和经费上都是有限的,项目最终必须完成。但项目小组成员大多有自己的爱好,项目经理应让项目小组成员始终关注项目的目标和截止期限。例如,可以定期检查,可以召开例会,可以制作一些提醒的标志置于项目的场所。
4、成功的项目应使用一种可以度量且被证实的项目生命周期
标准的信息系统开发模型可以保证专业标准和成功的经验能够融入项目计划。这类模型不仅可以保证质量,还可以使重复劳动降到最低程度。因此,当遇到时间和预算压力需要削减项目时,项目经理应确定一种最佳的项目生命周期。
5、所有项目目标和项目活动必须生动形象地得以交流和沟通
项目经理和项目小组在项目开始时就应当形象化地描述项目的最终目标,以确保与项目有关的每一个人都能记住。项目成本的各个细节都应当清楚、明确、毫不含糊,并确保每个人对此都达成了一致的意见。
6、采用渐进的方式逐步实现目标
如果试图同时完成所有的项目目标,只会造成重复劳动,既浪费时间又浪费钱。俗话说,一口吃不成个胖子。项目目标只能一点一点地去实现,并且每实现一个目标就进行一次评估,确保整个项目能得以控制。
7、项目应得到明确的许可,并由投资方签字实施
在实现项目目标的过程中获得明确的许可是非常重要的。应将投资方的签字批准视为项目的一个出发点。道理很简单:任何有权拒绝或有权修改项目目标的人都应当在项目启动时审查和批准这些项目目标。
8、要想获得项目成功必须对项目目标进行透彻的分析
研究表明,如果按照众所周知记录在案的业务需求来设计项目的目标,则该项目多半会成功。所以,项目经理应当坚持这样一个原则,即在组织机构启动项目之前,就应当为该项目在业务需求中找到充分的依据。
9、项目经理应当责权对等
项目经理应当对项目的结果负责,这一点并不过分。但与此相对应,项目经理也应被授予足够的权利以承担相应的责任。在某些时候,权利显得特别重要,如获取或协调资源,要求得到有关的中小企业的配合,做相应的对项目成功有价值的决策等等。
10、项目投资方和用户应当主动介入,不能被动地坐享其成
多数项目投资方和用户都能正确地要求和行使批准(全部或部分)项目目标的权力。但伴随这个权力的是相应的责任——主动地介入项目的各个阶段。例如,在项目早期要帮助确定项目目标;在项目进行中,要对完成的阶段性目标进行评估,以确保项目能顺利进行。项目投资方应帮助项目经理去访问有关的中小企业和目标顾客的成员,并帮助项目经理获得必要的文件资料。
11、项目的实施应当采用市场运作机制
在多数情况下,项目经理应将自己看成是卖主,以督促自己完成投资方和用户交付的任务。项目计划一旦批准项目经理应当定期提醒项目小组成员该项目必须满足的业务需求是什么,以及该怎样工作才能满足这些业务需求。
12、项目经理应当获得项目小组成员的最佳人选
最佳人选是指受过相应的技能培训,有经验,素质高。对于项目来说,获得最佳人选往往能弥补时间、经费或其它方面的不足。项目经理应当为这些最佳的项目成员创造良好的工作环境,如帮助他们免受外部干扰,帮助他们获得必要的工具和条件以发挥他们的才能。
在开始正文之前,我想先讲两个故事——关于软件项目的故事。
故事一
有两个软件项目(姑且称之为“项目 A”和“项目 B”),它们在开始时的预算都是 50 个人月,时间是 5 个月。
项目 A 在 5 个月后完工,耗费成本 50 人月
项目 B 在 6 个月后完工,耗费成本 70 人月
在软件圈子里摸爬滚打多年的读者们对这个故事一定有自己的判断——而且我可以大致猜出是什么样的判断。不过先别着急,我们还有另一个故事。
故事二
有两个软件项目(仍然姑且称之为“项目 A”和“项目 B”),它们在开始时的计划都是交付 200 项功能。
项目 A 在项目结束时一次性交付了最初计划的 200 项功能,但客户发现其中大约 30 项功能没有太大用处,而另外 30 项有用的功能要等到下一个项目才能实现。
项目 B 在第一个月结束时交付了第一个版本,此后每两周交付一个新的版本。在项目进行的过程中,客户进行了一次业务调整,加入了 90 项新的功能,并搁置了 50 项用处不大的功能。最终该项目交付了 240 项功能。
聪明的读者大概注意到了,前后两个故事讲的是同一回事,同样的两个项目。现在我的问题来了:请问哪个项目是更成功的项目?
这个问题并不容易回答——实际上它没有标准答案。站在很多软件企业的立场上,项目 A 是一个理想的成功项目:按时间、按成本完成预先约定的任务。请注意,我用了“理想的”这个词,稍后我还会解释这个有趣的词,因为实际上的软件项目往往没有那么理想。
而如果换一个角度,站在客户的立场上呢?也许付钱购买软件的客户会有一些不同的想法。项目 B 从开始之后一个月便交付了第一个可工作的版本,从那时起客户就开始使用这个软件的部分功能,并且不断地把自己使用的感受反馈给开发团队。在真实的业务运营过程中,客户甚至发现了一种新的盈利模式,并进行了一次大规模的业务调整,这次调整的结果也直观地体现在软件项目中。虽然项目B的整体交付速率低于项目 A,但它提供的所有功能都是客户真正需要的,它们为客户提供实实在在的价值——更不用说,客户提前好几个月就开始使用这个软件。
实际上,这是一篇关于软件价值的文章。和“成功项目”一样,对于“软件的价值”,不同的人也会有不同的定义。不过作为付钱购买软件的客户,他对于软件价值的定义是一目了然的:他能够从使用软件中创造多少价值,软件能够为他的业务提供多少价值,这就是软件的价值。或者说得更简明一点:
软件价值源自使用
这正是为什么很多客户青睐“项目 B”的原因——我并不打算声称所有客户都有同样的观点,稍后我也会举出反例,但至少支持这一观点的客户不在少数。因为他们处在一个残酷而快速变化的商业环境中:他们的供应商在变化,他们的客户在变化,他们所处的经济环境和政策环境也在变化。这一切的变化迫使他们的业务也要随之变化。更要命的是,今天这个经济全球化的时代是一个“快鱼吃慢鱼”的时代,客户迫切希望新的软件系统为他们带来竞争优势——哪怕这个软件系统尚未完成,只要能够投入使用。最后,客户对于新的软件系统究竟应该是什么样子并没有百分之百的把握,他们的想法往往要在真正使用软件之后才会浮现成型。几方面的因素加在一起,使得这些客户更愿意尽快开始使用软件、提出反馈、并不断完善软件,而不是提出一组需求、然后坐等几个月之后原封不动地拿到这些功能。
一个真实的案例
在 ThoughtWorks 的一个项目中,开发者们在项目开始之后一个月内就发布了第一个版本——只有一些简单的数据采集功能。在发布展示会上,发生了这样的对话……
开发者:这是我们的第一个功能。我们从文本文件、Excel 数据表和遗留数据库采集数据,现在我们的数据库中有这些数据……(展示数据库结构)
客户:唔……有意思。要是你能做这样一个查询(写出查询要求),得到的结果可能会有用。
开发者:可是我们的界面上没有地方做这样的查询操作。
客户:啊,我不需要操作界面,只要每天深夜做一次查询,把报表发到我的信箱就可以了。
开发者:这样吗……另一个问题是,这需要花我们几天时间。
客户:不要紧,把别的任务往后放几天好了,我很想看到这份报表。
开发者:那好吧,下周我们就会开始提供这个报表。
猜猜结果怎么样?一周之后客户就开始每天接收这份报表,并根据报表内容做一些分析和决策。仅仅几个月之后,这份报表给客户带来的收益就已经超过了整个项目的投资——这时项目其他部分的开发甚至还没有完成。
想想这个客户会怎么定义一个“成功的软件项目”?好吧,也许这个项目超过了预期的时间,也许投入了更多的人力,但这些并不意味着“项目失败”——只是付出更高的成本。关键在于,他投入的这些成本能够带来多大的收益,他的投资回报率是否划算。对于这个客户而言,如果项目能够随时给他提供可用的、能够创造最大价值的软件,能够随时让——就像故事中提到的——这种有价值的想法得以实现,这就是一个成功的项目。
所以,亲爱的读者,请你忘记本文标题上出现的“敏捷”二字,我们在这里所说的不是别的,就是一种为客户创造最大化价值的软件开发方法。这样的方法有很多种,但它们有一个共同的特点:尽快、尽可能频繁地交付可以工作的软件,让客户尽快开始使用软件,从使用中创造价值、厘清思路、提出反馈。仍然以 ThoughtWorks 的项目为例,这些项目通常在启动开发阶段之后一个月内就会发布第一个版本,随后每一周或每两周发布一个新版本——每个版本都是一个可以工作的软件,每个版本都比前一个版本具有更丰富的功能,并且每个版本都包含客户认为迄今为止最有价值的那些功能。用软件开发的“黑话”,“开发下一个版本”的过程叫做“迭代”,这些开发方法最大的共同点就是“迭代式开发”——不是一股脑地交付全部功能,而是每次增加一点、渐进地交付最有价值的功能。
软件开发的梦想与真实
回到文章开始处的两个故事。我曾经说过,对于很多软件企业而言,项目 A 是一个“理想的”成功项目。那么,是什么让情况变得不那么理想?
答案是一个所有软件开发者耳熟能详的词:需求变更。在真实的项目中,客户通常不会等到最后一天再照单全收整个项目,因为他知道自己的业务正在发生变化。这时需求变更就出现了,伴随着来回的扯皮和讨价还价。更糟的是,大量的需求变更发生在项目晚期——因为直到这时客户才真正看到、使用到这个软件,他的很多想法才真正浮现成型。随着这种“最后一分钟的需求变更”,项目超期、超出预算也就成了家常便饭。能够像项目A这样完工交付的,实在是凤毛麟角的幸运儿。
为了对付需求变更这个噩梦,软件开发者们还发明了另一个词:变更控制。这个有趣的词暗示着:需求变更是一种“不好”的东西,是需要“控制”的东西。然而站在客户的角度上想想,他在亲身使用了软件之后提出的要求,难道不是最有价值的东西吗?把这种真正创造业务价值的要求“控制”起来,难道是合理的吗?
在前面我也暗示过,并非所有的客户都一定青睐迭代式开发。那么,哪些软件项目不一定需要迭代式开发呢?从整篇文章的内容不难看出,如果客户的业务绝对不会变化,如果客户的需求巨细靡遗非常明确,如果客户不需要尽快开始使用软件以便收回成本,那么迭代式开发对他的帮助就会小得多。不过,如果读者认真思考的话,这样的例子也许并不多——也许比你最初认为的要少得多。一个很好的例子是“神州六号”火箭使用的计算机控制系统。还有多少这样的例子?读者不妨试着自己想想。
如果我足够幸运的话,也许一些读者已经被这篇文章吊起了胃口:既然有这么好的软件开发方法,既然它能够为我们创造更大的价值,那还等什么呢,我们马上就动手吧。事情不会那么简单。为了让迭代式开发能够成为现实,为了确保尽快、尽可能频繁地交付,为了确保每次交付的都是最有价值的功能,我们——包括软件开发者、软件企业和客户——需要很多的改变。这里既有职责与权利的划分,也有开发过程和团队的重组,还有技术层面的实践指导。这些正是敏捷方法学所涵盖的内容。缺少了这些东西,“为客户创造最大价值”就只能成为一句空话。在后续的文章里,我们将结合 ThoughtWorks 的实践经验,逐步介绍敏捷方法的方方面面。
最开始时微软公司将Java当做一种能解决C和C++中存在的问题的语言,并不在意,并继续维持和培训着其C和C++技术和编程人员。接下来不幸的是,正当微软尽力在Visual J++基础上拓展Java功能,并使之与Windows操作系统紧密结合在一起的时候,Sun公司对微软提出法律诉讼说其违反了许可证协议中的条款,最终的结果是微软公司不得不停止其Visual J++产品的开发。(微软公司仍然销售Visual J++,但是从1998年10月以来就没有新的版本发布,并且在.Net平台上也没有Visual J++的位置了)接下来的事情就很清楚了,微软公司开发了C#语言。接下来的一部分将讨论C#与Java的相似性。
C#与Java的区别
C#最引人的地方是它与Java的区别而不是其相似性。下面主要来介绍C#区别于Java的不同的运行特点及Java完全没有的特点。
中间语言
当MSIL被编译成最终的机器码时,微软公司在如何选择上是非常灵活的。微软公司很谨慎的对外宣称说MSIL不是解释型的,而是被编译成机器码。因为开发人员都有这样一个观念:Java程序天生就比C程序运行慢,所以这暗示着基于MSIL的程序优于解释型的Java字节码。当然,既然C#和其它MSIL产品编译器还未发布,那么这一点就还未证明,但是Java无处不在的即时编译器使得C#和Java在效能上是一样的。象“C#是编译型的,Java是解释型的”这样话只是销售中的技巧。Java的字节码和MSIL码都是的类似汇编的中间语言,在运行时执行这些中间码。
与COM的整合
对于基于Windows的C#开发人员来说,最大的收获是对COM的无损整合,COM是微软Win32的组件技术。实际上,任何一种.Net体系结构上的语言最终都可能去写COM的客户端和服务器端程序。用C#编写的类也会作为COM组件的子类;结果类(resulting class)也能作为COM组件使用,并作为COM组件的子类。
微软公司的目标是使越来越多的语言都能访问组件,并使这些组件能整合到.Net体系结构中。已有几个厂商开始着手开发支持.Net功能的编程语言,如COBOL和Haskell。开发人员能选择不同的语言解决不同问题,更重要的是,开发人员不必为采用.Net体系结构而必须学习新语言,可以选择一种他们熟悉的语言。
总结
本文的第一、二部分对C#做了一肤浅的总体介绍,主要是其产生动机及与Java的相似性。第三部分则涵概了C#的语言特点。用范例说明了C#与Java两者在相似性外,它们又是非常不同的,有许多细微的语义和设计区别,适合不同的技术和市场环境,又谈到了微软公司对C#进行标准化方面的尝试,及其对Java的影响。
c#与java的区别
1.属性:
java中定义和访问均要用get和set方法,可以不成对出现。
c#中是真正的属性,定义时get和set必须同时出现,房问时用.号即可。不用get,set
2.对象索引
就是对象数组
public Story this [int index] {
3.C#中,不用任何范围修饰符时,默认的是protect,因而不能在类外被访问.
4.因为JAVA规定,在一个文件中只能有一个public类,而且这个类的名称必须与文件名一模一样,这是一个区别
5.在C#中,它是以Main方法来定位入口的.如果一个程序中没有一个名为Main的方法,就会出"找不到入口的错误".不要把Main写成main哟
6.C#预定义的简单数据类型比Java多。例如,C#有unit,即无符号整数
7.忘掉Java中的static final修饰符。在C#中,常量可以用const关键词声明
C#的设计者还增加了readonly关键词,readonly域只能通过初始化器或类的构造函数设置
8.公用类的入口点:c#是可以对Main进行重载(java中是main),允许有int返回值和空参数的Main
9.在Java中,switch语句只能处理整数。但C#中的switch语句不同,它还能够处理字符变量。请考虑下面用switch语句处理字符串变量的C#代码
10.C#没有>>>移位操作符
11.goto关键词:
Java不用goto关键词。在C#中,goto允许你转到指定的标签。不过,C#以特别谨慎的态度对待goto,比如它不允许goto转入到语句块的内部。在Java中,你可以用带标签的语句加上break或continue取代C#中的goto。
12.int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };
但在C#中,只有第一行代码合法,[]不能放到变量名字之后。
13.与Java不同的是,C#允许为名称空间或者名称空间中的类指定别名:
using TheConsole = System.Console;
14.在Java中,包的名字同时也是实际存在的实体,它决定了放置.java文件的目录结构。在C#中,物理的包和逻辑的名称之间是完全分离的
.NET中包的实体称为程序集(Assembly)。每一个程序集包含一个manifest结构。manifest列举程序集所包含的文件,控制哪些类型和资源被显露到程序集之外,并把对这些类型和资源的引用映射到包含这些类型与资源的文件。程序集是自包含的,一个程序集可以放置到单一的文件之内,也可以分割成多个文件。.NET的这种封装机制解决了DLL文件所面临的问题,即臭名昭著的DLL Hell问题。
15.在Java中,java.lang包是默认的包,C#中不存在默认的包
16.C#中的访问修饰符与Java中的基本对应,但多出了一个internal。简而言之,C#有5种类型的可访问性,如下所示:
public:成员可以从任何代码访问。
protected:成员只能从派生类访问。
internal:成员只能从同一程序集的内部访问。
protected internal:成员只能从同一程序集内的派生类访问。
private:成员只能在当前类的内部访问。
17.由于C#中不存在final关键词,如果想要某个类不再被派生,你可以使用sealed关键词
18.与Java不同,C#中的接口不能包含域(Field)。
另外还要注意,在C#中,接口内的所有方法默认都是公用方法。在Java中,方法声明可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。例如,下面的C#接口将产生一个编译错误。
19.C#中的is操作符与Java中的instanceof操作符一样,两者都可以用来测试某个对象的实例是否属于特定的类型。在Java中没有与C#中的as操作符等价的操作符。as操作符与is操作符非常相似,但它更富有“进取心”:如果类型正确的话,as操作符会尝试把被测试的对象引用转换成目标类型;否则,它把变量引用设置成null。
20.C#仍旧保留了C++的内存手工管理方法,它适合在速度极端重要的场合使用,而在Java中这是不允许的
21.在C#中,所有的异常都从一个名为Exception的类派生
22.枚举器即enum类型(java无),把它作为一个变量值的类型使用,从而把变量可能的取值范围限制为枚举器中出现的值。
23.结构(Struct)与类很相似,而结构是一种值类型,它存储在栈中或者是嵌入式的,结构可以实现接口,可以象类一样拥有成员,但结构不支持继承
24.属性声明语法的第一部分与域声明很相似,第二部分包括一个set过程和/或一个get过程
25.传值方式:
在java中简单数据类型的值传参时,都以传值方式;
在c#中如果加ref则会以引用的方式传值(方法内部改变该参数,则外部变量一起跟着变);
加out与ref基本相同,但out不要求参数一定要初始化.
26.c#保留了指针。unsafe
27.代理:代理(delegate)可以看作C++或者其他语言中的函数指针
代理用来封装可调用方法。你可以在类里面编写方法并在该方法上创建代理,此后这个代理就可以被传递到第二个方法。这样,第二个方法就可以调用第一个方法。
代理是从公共基类System.Delegate派生的引用类型。定义和使用代理包括三个步骤:声明,创建实例,调用。代理用delegate声明语法声明。
初学 Java 有段时间了,感觉似乎开始入了门,有了点儿感觉
但是发现很多困惑和疑问而且均来自于最基础的知识
折腾了一阵子又查了查书,终于对 String 这个特殊的对象有了点感悟
大家先来看看一段奇怪的程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
}
}
这个程序真是简单啊!可是有什么问题呢?
1. 来自 String 的忧虑
上面这段程序中,到底有几个对象呢?
可能很多人脱口而出:两个,s1 和 s2
为什么?
String 是 final 类,它的值不可变。
看起来似乎很有道理,那么来检测一下吧,稍微改动一下程序
就可以看到结果了:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
}
}
呵呵,很多人都会说已经不止两个对象了
编译并运行程序,输出:s1 == s2
啊!
为什么 s1 == s2 ?
== 分明是在说:s1 与 s2 引用同一个 String 对象 -- "Monday"!
2. 千变万化的 String
再稍微改动一下程序,会有更奇怪的发现:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
我们将 s2 用 new 操作符创建
程序输出:
s1 != s2
s1 equals s2
嗯,很明显嘛
s1 s2分别引用了两个"Monday"String对象
可是为什么两段程序不一样呢?
3. 在 String 的游泳池中游泳
哈哈,翻了翻书终于找到了答案:
原来,程序在运行的时候会创建一个字符串缓冲池
当使用 s2 = "Monday" 这样的表达是创建字符串的时候,程序首先会
在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被
放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1
将 s2 引用 s1 所引用的对象"Monday"
第二段程序中,使用了 new 操作符,他明白的告诉程序:
“我要一个新的!不要旧的!”与是一个新的"Monday"Sting对象被创
建在内存中。他们的值相同,但是位置不同,一个在池中游泳
一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?
4. 继续潜水
再次更改程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
这次加入:s2 = s2.intern();
哇!程序输出:
s1 == s2
s1 equals s2
原来,程序新建了 s2 之后,又用intern()把他打翻在了池里
哈哈,这次 s2 和 s1 有引用了同样的对象了
我们成功的减少了内存的占用
5. == 与 equals() 的争斗
String 是个对象,要对比两个不同的String对象的值是否相同
明显的要用到 equals() 这个方法
可是如果程序里面有那么多的String对象,有那么多次的要用到 equals ,
哦,天哪,真慢啊
更好的办法:
把所有的String都intern()到缓冲池去吧
最好在用到new的时候就进行这个操作
String s2 = new String("Monday").intern();
嗯,大家都在水池里泡着了吗?哈哈
现在我可以无所顾忌的用 == 来比较 String 对象的值了
真是爽啊,又快又方便!
关于String :)
String 啊 String ,让我说你什么好呢?
你为我们 Java 程序员带来所有的困扰还不够吗?
看看 String 这一次又怎么闹事儿吧
1. 回顾一下坏脾气的 String 老弟
例程1:
class Str {
public static void main(String[] args) {
String s = "Hi!";
String t = "Hi!";
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
}
程序输出什么呢?
如果看客们看过我的《来自 String 的困惑》之一
相信你很快会做出正确的判断:
程序输出:equals
2. 哦,天哪,它又在搅混水了
例程2:
class Str {
public static void main(String[] args) {
String s = "HELLO";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
}
那么这个程序有输出什么呢?
慎重!再慎重!不要被 String 这个迷乱的家伙所迷惑!
它输出:equals
WHY!!!
把程序简单的更改一下:
class Str2 {
public static void main(String[] args) {
String s = "Hello";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
}
你可能会说:不是一样吗?
不!千真万确的,不一样!这一次输出:not equals
Oh MyGOD!!!
谁来教训一下这个 String 啊!
3. 你了解你的马吗?
“要驯服脱缰的野马,就要了解它的秉性”牛仔们说道。
你了解 String 吗?
解读 String 的 API ,可以看到:
toUpperCase() 和 toLowerCase() 方法返回一个新的String对象,
它将原字符串表示字符串的大写或小写形势;
但是要注意:如果原字符串本身就是大写形式或小写形式,那么返回原始对象。
这就是为什么第二个程序中 s 和 t 纠缠不清的缘故
对待这个淘气的、屡教不改的 String ,似乎没有更好的办法了
让我们解剖它,看看它到底有什么结构吧:
(1) charAt(int n) 返回字符串内n位置的字符,第一个字符位置为0,
最后一个字符的位置为length()-1,访问错误的位置会扔出一块大砖头:
StringIndexOutOfBoundsException 真够大的
(2) concat(String str) 在原对象之后连接一个 str ,但是返回一个新的 String 对象
(3) EqualsIgnoreCase(String str) 忽略大小写的 equals 方法
这个方法的实质是首先调用静态字符方法toUpperCase() 或者 toLowerCase()
将对比的两个字符转换,然后进行 == 运算
(4) trim() 返回一个新的对象,它将原对象的开头和结尾的空白字符切掉
同样的,如果结果与原对象没有差别,则返回原对象
(5) toString() String 类也有 toString() 方法吗?
真是一个有趣的问题,可是如果没有它,你的 String 对象说不定真的不能用在
System.out.println() 里面啊
小心,它返回对象自己
String 类还有很多其他方法,掌握他们会带来很多方便
也会有很多困惑,所以坚持原则,是最关键的
4. 我想买一匹更好的马
来购买更驯服温和的 String 的小弟 StringBuffer 吧
这时候会有人反对:它很好用,它效率很高,它怎么能够是小弟呢?
很简单,它的交互功能要比 String 少,如果你要编辑字符串
它并不方便,你会对它失望
但这不意味着它不强大
public final class String implements Serializable, Comparable, CharSequence
public final class StringBuffer implements Serializable, CharSequence
很明显的,小弟少了一些东东,不过这不会干扰它的前途
StringBuffer 不是由 String 继承来的
不过要注意兄弟它也是 final 啊,本是同根生
看看他的方法吧,这么多稳定可靠的方法,用起来比顽皮的 String 要有效率的多
?br /> Java 为需要改变的字符串对象提供了独立的 StringBuffer 类
它的实例不可变(final),之所以要把他们分开
是因为,字符串的修改要求系统的开销量增大,
占用更多的空间也更复杂,相信当有10000人挤在一个狭小的游泳池里游泳
而岸边又有10000人等待进入游泳池而焦急上火
又有10000人在旁边看热闹的时候,你这个 String 游泳池的管理员也会焦头烂额
在你无需改变字符串的情况下,简单的 String 类就足够你使唤的了,
而当要频繁的更改字符串的内容的时候,就要借助于宰相肚里能撑船的
StringBuffer 了
5. 宰相肚里能撑船
(1) length() 与 capacity()
String 中的 length() 返回字符串的长度
兄弟 StringBuffer 也是如此,他们都由对象包含的字符长度决定
capacity()呢?
public class TestCapacity {
public static void main(String[] args){
StringBuffer buf = new StringBuffer("it was the age of wisdom,");
System.out.println("buf = " + buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());
String str = buf.toString();
System.out.println("str = " + str);
System.out.println("str.length() = " + str.length());
buf.append(" " + str.substring(0,18)).append("foolishness,");
System.out.println("buf = " + buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());
System.out.println("str = " + str);
}
}
程序输出:
buf = it was the age of wisdom.
buf.length() = 25
buf.capacity() = 41
str = it was the age of wisdom
str.length() = 25
buf = it was the age of wisdom, it was the age of foolishness,
buf.length() = 56
buf.capacity() = 84
str = it was the age of wisdom,
可以看到,在内容更改之后,capacity也随之改变了
长度随着向字符串添加字符而增加
而容量只是在新的长度超过了现在的容量之后才增加
StringBuffer 的容量在操作系统需要的时候是自动改变的
程序员们对capacity所能够做的仅仅是可以在初始化 StringBuffer对象的时候
以上片断引用自http://bbs.blueidea.com/viewthread.php?tid=945875&page=###
解释得比较形象和经典。具体的比较,要亲自动手运行一下程序才行,如下为网上找到的专门研究equals和==的关系的程序,相信可从中体会出他们的深刻不同:
String s1 = null;
String s2 = null;
System.out.println(s1==s2);//true
//System.out.println(s1.equals(s2));//NullPointerException
s1 = s2;
System.out.println(s1==s2);//true
//System.out.println(s1.equals(s2));//NullPointerException
System.out.println("***1***");
s1 = null;
s2 = "";
System.out.println(s1==s2);//false
//System.out.println(s1.equals(s2));//NullPointerException
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***2***");
s1 = "";
s2 = null;
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//false
s1 = s2;
System.out.println(s1==s2);//true
//System.out.println(s1.equals(s2));//NullPointerException
System.out.println("***3***");
s1 = "";
s2 = "";
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***4***");
s1 = new String("");
s2 = new String("");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***5***");
s1 = "null";
s2 = "null";
System.out.println(s1==s2);//ture
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***6***");
s1 = new String("null");
s2 = new String("null");
System.out.println(s1==s2);//flase
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***7***");
s1 = "abc";
s2 = "abc";
System.out.println(s1==s2);//ture
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***8***");
s1 = new String("abc");
s2 = new String("abc");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
s1 = s2;
System.out.println(s1==s2);//true
System.out.println(s1.equals(s2));//true
System.out.println("***9***");
总结: 多数情况下这两者的区别就是究竟是对对象的引用进行比较还是对对象的值进行比较(其他特殊情况此处不予考虑)。==操作符是比较的对象的引用而不是对象的值。
但在最初的Object对象中的equals方法与==操作符完成功能是相同的。
源码:
java.lang.Object.equals()方法:
-------------------------------------------------------------
public boolean equalss(Object obj) {
return (this = = obj);
}
-------------------------------------------------------------
jdk文档中给出如下解释:
-------------------------------------------------------------
The equalss method implements an equivalence relation:
· It is reflexive: for any reference value x, x.equalss(x) should return true.
· It is symmetric: for any reference values x and y, x.equalss(y) should return true if and only if y.equalss(x) returns true.
· It is transitive: for any reference values x, y, and z, if x.equalss(y) returns true and y.equalss(z) returns true, then x.equalss(z) should return true.
· It is consistent: for any reference values x and y, multiple invocations of x.equalss(y) consistently return true or consistently return false, provided no information used in equalss comparisons on the object is modified.
· For any non-null reference value x, x.equalss(null) should return false.
The equalss method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any reference values x and y, this method returns true if and only if x and y refer to the same object (x==y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equals objects must have equals hash codes.
-------------------------------------------------------------
对于String类的equals方法是对什么内容进行比较的呢?下面我们来看它的代码和注释:
源代码:
-------------------------------------------------------------
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
-------------------------------------------------------------
此方法的注释为:
-------------------------------------------------------------
Compares this string to the specified object. The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object.
-------------------------------------------------------------
由上面的代码和注释可以得到String类的equal方法是对对象的值进行比较。
根据以上的讨论可以得出结论:equal方法和==操作符是否存在区别要个别对待,要根据equal的每个实现情况来具体判断。
tomcat5.5.9 jdbc 连接池配置时常会出现两个错误:
1.***not bound
2.Cannot create JDBC driver of class '' for connect URL 'null'
可以通过以下的方法达到正确的配置:
1.现在%tomcat home%/conf/catalina/localhost 下面添加一段独立的context xml段,命名为jasper.xml,例如
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/jasper" docBase="jasper" debug="1" reloadable="true" crossContext="true">
</Context>
2.通过admin的界面(或手动在server.xml界面里进行配置)数据源,以sql2k为例,配置后的server.xml应该为
<?xml version="1.0" encoding="UTF-8"?>
<Server>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"/>
<GlobalNamingResources>
<Environment
name="simpleValue"
type="java.lang.Integer"
value="30"/>
<Resource
auth="Container"
description="User database that can be updated and saved"
name="UserDatabase"
type="org.apache.catalina.UserDatabase"
pathname="conf/tomcat-users.xml"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"/>
<Resource
name="jdbc/Northwind"
type="javax.sql.DataSource"
password="*****"
driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
maxIdle="2"
maxWait="5000"
username="sa"
url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=Northwind"
maxActive="20"/>
</GlobalNamingResources>
<Service
name="Catalina">
<Connector
port="80"
redirectPort="8443"
minSpareThreads="25"
connectionTimeout="20000"
maxSpareThreads="75"
maxThreads="150"
maxHttpHeaderSize="8192">
</Connector>
<Connector
port="8009"
redirectPort="8443"
protocol="AJP/1.3">
</Connector>
<Engine
defaultHost="localhost"
name="Catalina">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
<Host
appBase="webapps"
name="localhost">
</Host>
</Engine>
</Service>
</Server>
3.在conf下面的context.xml文件中,</Context>之前加入
<ResourceLink name="jdbc/Northwind" global="jdbc/Northwind" type="javax.sql.DataSourcer"/>
4.在你的web文件夹下面的WEB-INF中找到web.xml,在最后</web-app>之前加入
<resource-ref>
<description>Northwind Connection</description>
<res-ref-name>jdbc/Northwind</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
5.保存,重启tomcat,这样,通过测试页面,就可以发现已经可以通过数据库连接池连上sql2k
test1.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<%@page import="javax.naming.*"%>
<%@page import="javax.sql.*"%>
<%
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/Northwind");
Connection conn = ds.getConnection();
int counts=0;
String sql="select count(*) from orders";
PreparedStatement pst=conn.prepareStatement(sql);
ResultSet rst=pst.executeQuery();
while(rst.next()){
counts=rst.getInt(1);
}
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>JNDI 测试</title>
</head>
<body>
<%=sql%><br>
pnrreg talbe's count is:<%=counts%>
</body>
</html>
摘要: JS的正则表达式
//校验是否全由数字组成
function isDigit(s)
{
var patrn=/^[0-9]{1,20}$/;
if (!patrn.exec(s)) return false
return true
}
//校验登录名:只能输入5-20个以字母开头、可带数字、“_”... 阅读全文
原著:Steve Mansour
sman@scruznet.com
Revised: June 5, 1999
(copied by jm /at/ jmason.org from http://www.scruz.net/%7esman/regexp.htm, after the original disappeared! )
翻译:Neo Lee
neo.lee@gmail.com
2004年10月16日
英文版原文
译者按:原文因为年代久远,文中很多链接早已过期(主要是关于vi、sed等工具的介绍和手册),本译文中已将此类链接删除,如需检查这些链接可以查看上面链接的原文。除此之外基本照原文直译,括号中有“译者按”的部分是译者补充的说明。如有内容方面的问题请直接和Steve Mansor联系,当然,如果你只写中文,也可以和我联系。
目 录
什么是正则表达式
范例
简单
中级(神奇的咒语)
困难(不可思议的象形文字)
不同工具中的正则表达式
什么是正则表达式
一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。很多人因为它们看上去比较古怪而且复杂所以不敢去使用——很不幸,这篇文章也不能够改变这一点,不过,经过一点点练习之后我就开始觉得这些复杂的表达式其实写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作压缩在几分钟(甚至几秒钟)内完成。正则表达式被各种文本编辑软件、类库(例如Rogue Wave的tools.h++)、脚本工具(像awk/grep/sed)广泛的支持,而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。
我们将在如下的章节中利用一些例子来解释正则表达式的用法,绝大部分的例子是基于vi中的文本替换命令和grep文件搜索命令来书写的,不过它们都是比较典型的例子,其中的概念可以在sed、awk、perl和其他支持正则表达式的编程语言中使用。你可以看看不同工具中的正则表达式这一节,其中有一些在别的工具中使用正则表达式的例子。还有一个关于vi中文本替换命令(s)的简单说明附在文后供参考。
正则表达式基础
正则表达式由一些普通字符和一些 元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,我们下面会给予解释。
在最简单的情况下,一个正则表达式看上去就是一个普通的查找串。例如,正则表达式"testing"中没有包含任何元字符,,它可以匹配"testing"和"123testing"等字符串,但是不能匹配"Testing"。
要想真正的用好正则表达式,正确的理解元字符是最重要的事情。下表列出了所有的元字符和对它们的一个简短的描述。
元字符 |
|
描述 |
|
|
|
. |
|
匹配任何单个字符。例如正则表达式r.t匹配这些字符串:rat、rut、r t,但是不匹配root。 |
$ |
|
匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾,但是不能匹配字符串"They are a bunch of weasels."。 |
^ |
|
匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the"。 |
* |
|
匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。 |
\ |
|
这是引用府,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。 |
[ ]
[c1-c2]
[^c1-c2] |
|
匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配rat、rot和rut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。 |
\< \> |
|
匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。 |
\( \) |
|
将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。 |
| |
|
将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。 |
+ |
|
匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。 |
? |
|
匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。 |
\{i\}
\{i,j\} |
|
匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]\{3\} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]\{4,6\} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。 |
最简单的元字符是点,它能够匹配任何单个字符(注意不包括新行符)。假定有个文件test.txt包含以下几行内容:
he is a rat
he is in a rut
the food is Rotten
I like root beer
我们可以使用grep命令来测试我们的正则表达式,grep命令使用正则表达式去尝试匹配指定文件的每一行,并将至少有一处匹配表达式的所有行显示出来。命令
在test.txt文件中的每一行中搜索正则表达式 r.t,并打印输出匹配的行。正则表达式 r.t匹配一个 r接着任何一个字符再接着一个 t。所以它将匹配文件中的 rat和 rut,而不能匹配 Rotten中的 Rot,因为正则表达式是大小写敏感的。要想同时匹配大写和小写字母,应该使用字符区间元字符(方括号)。正则表达式 [Rr]能够同时匹配 R和 r。所以,要想匹配一个大写或者小写的 r接着任何一个字符再接着一个 t就要使用这个表达式: [Rr].t。
要想匹配行首的字符要使用抑扬字符(^)——又是也被叫做插入符。例如,想找到text.txt中行首"he"打头的行,你可能会先用简单表达式he,但是这会匹配第三行的the,所以要使用正则表达式^he,它只匹配在行首出现的h。
有时候指定“除了×××都匹配”会比较容易达到目的,当抑扬字符(^)出现在方括号中是,它表示“排除”,例如要匹配he ,但是排除前面是t or s的情性(也就是the和she),可以使用:[^st]he。
可以使用方括号来指定多个字符区间。例如正则表达式[A-Za-z]匹配任何字母,包括大写和小写的;正则表达式[A-Za-z][A-Za-z]* 匹配一个字母后面接着0或者多个字母(大写或者小写)。当然我们也可以用元字符+做到同样的事情,也就是:[A-Za-z]+ ,和[A-Za-z][A-Za-z]*完全等价。但是要注意元字符+ 并不是所有支持正则表达式的程序都支持的。关于这一点可以参考后面的正则表达式语法支持情况。
要指定特定数量的匹配,要使用大括号(注意必须使用反斜杠来转义)。想匹配所有100和1000的实例而排除10和10000,可以使用:10\{2,3\},这个正则表达式匹配数字1后面跟着2或者3个0的模式。在这个元字符的使用中一个有用的变化是忽略第二个数字,例如正则表达式0\{3,\} 将匹配至少3个连续的0。
简单的例子
这里有一些有代表性的、比较简单的例子。
vi 命令 |
作用 |
|
|
:%s/ */ /g |
把一个或者多个空格替换为一个空格。 |
:%s/ *$// |
去掉行尾的所有空格。 |
:%s/^/ / |
在每一行头上加入一个空格。 |
:%s/^[0-9][0-9]* // |
去掉行首的所有数字字符。 |
:%s/b[aeio]g/bug/g |
将所有的bag、beg、big和bog改为bug。 |
:%s/t\([aou]\)g/h\1t/g |
将所有tag、tog和tug分别改为hat、hot和hug(注意用group的用法和使用\1引用前面被匹配的字符)。 |
|
|
中级的例子(神奇的咒语)
例1
将所有方法foo(a,b,c)的实例改为foo(b,a,c)。这里a、b和c可以是任何提供给方法foo()的参数。也就是说我们要实现这样的转换:
之前 |
|
之后 |
foo(10,7,2) |
|
foo(7,10,2) |
foo(x+13,y-2,10) |
|
foo(y-2,x+13,10) |
foo( bar(8), x+y+z, 5) |
|
foo( x+y+z, bar(8), 5) |
下面这条替换命令能够实现这一魔法:
:%s/foo(\([^,]*\),\([^,]*\),\([^)]*\))/foo(\2,\1,\3)/g
现在让我们把它打散来加以分析。写出这个表达式的基本思路是找出foo()和它的括号中的三个参数的位置。第一个参数是用这个表达式来识别的::\([^,]*\),我们可以从里向外来分析它:
[^,] |
|
除了逗号之外的任何字符 |
[^,]* |
|
0或者多个非逗号字符 |
\([^,]*\) |
|
将这些非逗号字符标记为\1,这样可以在之后的替换模式表达式中引用它 |
\([^,]*\), |
|
我们必须找到0或者多个非逗号字符后面跟着一个逗号,并且非逗号字符那部分要标记出来以备后用。 |
现在正是指出一个使用正则表达式常见错误的最佳时机。为什么我们要使用[^,]*这样的一个表达式,而不是更加简单直接的写法,例如:.*,来匹配第一个参数呢?设想我们使用模式.*来匹配字符串"10,7,2",它应该匹配"10,"还是"10,7,"?为了解决这个两义性(ambiguity),正则表达式规定一律按照最长的串来,在上面的例子中就是"10,7,",显然这样就找出了两个参数而不是我们期望的一个。所以,我们要使用[^,]*来强制取出第一个逗号之前的部分。
这个表达式我们已经分析到了:foo(\([^,]*\),这一段可以简单的翻译为“当你找到foo(就把其后直到第一个逗号之前的部分标记为\1”。然后我们使用同样的办法标记第二个参数为\2。对第三个参数的标记方法也是一样,只是我们要搜索所有的字符直到右括号。我们并没有必要去搜索第三个参数,因为我们不需要调整它的位置,但是这样的模式能够保证我们只去替换那些有三个参数的foo()方法调用,在foo()是一个重载(overoading)方法时这种明确的模式往往是比较保险的。然后,在替换部分,我们找到foo()的对应实例,然后利用标记好的部分进行替换,是的第一和第二个参数交换位置。
例2
假设有一个CSV(comma separated value)文件,里面有一些我们需要的信息,但是格式却有问题,目前数据的列顺序是:姓名,公司名,州名缩写,邮政编码,现在我们希望讲这些数据重新组织,以便在我们的某个软件中使用,需要的格式为:姓名,州名缩写-邮政编码,公司名。也就是说,我们要调整列顺序,还要合并两个列来构成一个新列。另外,我们的软件不能接受逗号前后面有任何空格(包括空格和制表符)所以我们还必须要去掉逗号前后的所有空格。
这里有几行我们现在的数据:
Bill Jones, HI-TEK Corporation , CA, 95011
Sharon Lee Smith, Design Works Incorporated, CA, 95012
B. Amos , Hill Street Cafe, CA, 95013
Alexander Weatherworth, The Crafts Store, CA, 95014
...
我们希望把它变成这个样子:
Bill Jones,CA 95011,HI-TEK Corporation
Sharon Lee Smith,CA 95012,Design Works Incorporated
B. Amos,CA 95013,Hill Street Cafe
Alexander Weatherworth,CA 95014,The Crafts Store
...
我们将用两个正则表达式来解决这个问题。第一个移动列和合并列,第二个用来去掉空格。
下面就是第一个替换命令:
:%s/\([^,]*\),\([^,]*\),\([^,]*\),\(.*\)/\1,\3 \4,\2/
这里的方法跟例1基本一样,第一个列(姓名)用这个表达式来匹配: \([^,]*\),即第一个逗号之前的所有字符,而姓名内容被用 \1标记下来。公司名和州名缩写字段用同样的方法标记为 \2和 \3,而最后一个字段用 \(.*\)来匹配("匹配所有字符直到行末")。替换部分则引用上面标记的那些内容来进行构造。
下面这个替换命令则用来去除空格:
我们还是分解来看: [ \t]匹配空格/制表符, [ \t]* 匹配0或多个空格/制表符, [ \t]*,匹配0或多个空格/制表符后面再加一个逗号,最后, [ \t]*,[ \t]*匹配0或多个空格/制表符接着一个逗号再接着0或多个空格/制表符。在替换部分,我们简单的我们找到的所有东西替换成一个逗号。这里我们使用了结尾的可选的 g参数,这表示在每行中对所有匹配的串执行替换(而不是缺省的只替换第一个匹配串)。
例3
假设有一个多字符的片断重复出现,例如:
Billy tried really hard
Sally tried really really hard
Timmy tried really really really hard
Johnny tried really really really really hard 而你想把"really"、"really really",以及任意数量连续出现的"really"字符串换成一个简单的"very"(simple is good!),那么以下命令:
:%s/\(really \)\(really \)*/very / 就会把上述的文本变成:
Billy tried very hard
Sally tried very hard
Timmy tried very hard
Johnny tried very hard 表达式 \(really \)*匹配0或多个连续的"really "(注意结尾有个空格),而 \(really \)\(really \)* 匹配1个或多个连续的"really "实例。
困难的例子(不可思议的象形文字)
Coming soon.
不同工具中的正则表达式
OK,你已经准备使用RE(regular expressions,正则表达式),但是你并准备使用vi。所以,在这里我们给出一些在其他工具中使用RE的例子。另外,我还会总结一下你在不同程序之间使用RE可能发现的区别。
当然,你也可以在Visual C++编辑器中使用RE。选择Edit->Replace,然后选择"Regular expression"选择框,Find What输入框对应上面介绍的vi命令:%s/pat1/pat2/g中的pat1部分,而Replace输入框对应pat2部分。但是,为了得到vi的执行范围和g选项,你要使用Replace All或者适当的手工Find Next and Replace(译者按:知道为啥有人骂微软弱智了吧,虽然VC中可以选中一个范围的文本,然后在其中执行替换,但是总之不够vi那么灵活和典雅)。
sed
Sed是Stream EDitor的缩写,是Unix下常用的基于文件和管道的编辑工具,可以在手册中得到关于sed的详细信息。
这里是一些有趣的sed脚本,假定我们正在处理一个叫做price.txt的文件。注意这些编辑并不会改变源文件,sed只是处理源文件的每一行并把结果显示在标准输出中(当然很容易使用重定向来定制):
sed脚本 |
|
描述 |
|
|
|
sed 's/^$/d' price.txt |
|
删除所有空行 |
sed 's/^[ \t]*$/d' price.txt |
|
删除所有只包含空格或者制表符的行 |
sed 's/"//g' price.txt |
|
删除所有引号 |
awk
awk是一种编程语言,可以用来对文本数据进行复杂的分析和处理。可以在手册中得到关于awk的详细信息。这个古怪的名字是它作者们的姓的缩写(Aho,Weinberger和Kernighan)。
在Aho,Weinberger和Kernighan的书The AWK Programming Language中有很多很好的awk的例子,请不要让下面这些微不足道的脚本例子限制你对awk强大能力的理解。我们同样假定我们针对price.txt文件进行处理,跟sed一样,awk也只是把结果显示在终端上。
awk脚本 |
|
描述 |
|
|
|
awk '$0 !~ /^$/' price.txt |
|
删除所有空行 |
awk 'NF > 0' price.txt |
|
awk中一个更好的删除所有行的办法 |
awk '$2 ~ /^[JT]/ {print $3}' price.txt |
|
打印所有第二个字段是'J'或者'T'打头的行中的第三个字段 |
awk '$2 !~ /[Mm]isc/ {print $3 + $4}' price.txt |
|
针对所有第二个字段不包含'Misc'或者'misc'的行,打印第3和第4列的和(假定为数字) |
awk '$3 !~ /^[0-9]+\.[0-9]*$/ {print $0}' price.txt |
|
打印所有第三个字段不是数字的行,这里数字是指d.d或者d这样的形式,其中d是0到9的任何数字 |
awk '$2 ~ /John|Fred/ {print $0}' price.txt |
|
如果第二个字段包含'John'或者'Fred'则打印整行 |
grep
grep是一个用来在一个或者多个文件或者输入流中使用RE进行查找的程序。它的name编程语言可以用来针对文件和管道进行处理。可以在手册中得到关于grep的完整信息。这个同样古怪的名字来源于vi的一个命令, g/re/p,意思是 global regular expression print。
下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:
Francis, John 5-3871
Wong, Fred 4-4123
Jones, Thomas 1-4122
Salazar, Richard 5-2522
grep命令 |
|
描述 |
|
|
|
grep '\t5-...1' phone.txt |
|
把所有电话号码以5开头以1结束的行打印出来,注意制表符是用\t表示的 |
grep '^S[^ ]* R' phone.txt |
|
打印所有姓以S打头和名以R打头的行 |
grep '^[JW]' phone.txt |
|
打印所有姓开头是J或者W的行 |
grep ', ....\t' phone.txt |
|
打印所有姓是4个字符的行,注意制表符是用\t表示的 |
grep -v '^[JW]' phone.txt |
|
打印所有不以J或者W开头的行 |
grep '^[M-Z]' phone.txt |
|
打印所有姓的开头是M到Z之间任一字符的行 |
grep '^[M-Z].*[12]' phone.txt |
|
打印所有姓的开头是M到Z之间任一字符,并且点号号码结尾是1或者2的行 |
egrep
egrep是grep的一个扩展版本,它在它的正则表达式中支持更多的元字符。下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:
Francis, John 5-3871
Wong, Fred 4-4123
Jones, Thomas 1-4122
Salazar, Richard 5-2522
egrep command |
|
Description |
|
|
|
egrep '(John|Fred)' phone.txt |
|
打印所有包含名字John或者Fred的行 |
egrep 'John|22$|^W' phone.txt |
|
打印所有包含John 或者以22结束或者以W的行 |
egrep 'net(work)?s' report.txt |
|
从report.txt中找到所有包含networks或者nets的行 |
正则表达式语法支持情况
命令或环境 |
. |
[ ] |
^ |
$ |
\( \) |
\{ \} |
? |
+ |
| |
( ) |
vi |
X |
X |
X |
X |
X |
|
|
|
|
|
Visual C++ |
X |
X |
X |
X |
X |
|
|
|
|
|
awk |
X |
X |
X |
X |
|
|
X |
X |
X |
X |
sed |
X |
X |
X |
X |
X |
X |
|
|
|
|
Tcl |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
ex |
X |
X |
X |
X |
X |
X |
|
|
|
|
grep |
X |
X |
X |
X |
X |
X |
|
|
|
|
egrep |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
fgrep |
X |
X |
X |
X |
X |
|
|
|
|
|
perl |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
vi替换命令简介
Vi的替换命令:
其中
网上有很多vi的在线手册,你可以访问他们以获得更加完整的信息。
已知: 字段A='F:\photo\Winter Leaves.jpg'
要求:分段截取每段字符[字段A不能为TEXT类型,否则报错]
解决方法:
+++++++++++++++++++++++++++++++++++++++++++++++++++
---截取字符串A的第一个\左边的字符串
select left(A,charindex('/',A)-1)
输出结果:F:
++++++++++++++++++++++++++++++++++++++++++++++++++++
---截取\中间的字符串
select left(stuff(A,1,charindex('/',A),''),charindex('/',stuff(A,1,charindex('/',A),''))-1)
输出结果:photo
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
---截取最后一个\后面的字符串
select reverse(left(reverse(A),charindex('/',reverse(A))-1))
输出结果:Winter Leaves.jpg
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
---截取字符串A的首字幕
select STUFF(A,1, 1, '')
输出结果::\photo\Winter Leaves.jpg
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
把你的光标放到相应文字上查看效果
|
要注意光标的实际效果依赖于用户的系统设置,与你在这里看到的效果并不一定一致。
|
cursor: crosshair;
|
|
十字准心
|
The cursor render as a crosshair
游标表现为十字准线
|
cursor: pointer;
cursor: hand;
写两个是为了照顾IE5,它只认hand。
|
|
手
|
The cursor render as a pointer (a hand) that indicates a link
游标以暗示(手指)的形式来表明有一个连接
|
cursor: wait;
|
|
等待/沙漏
|
The cursor indicates that the program is busy (often a watch or an hourglass)
游标暗示当前程序正忙(一般为一块表或是一个沙漏)
|
cursor: help;
|
|
帮助
|
The cursor indicates that help is available (often a question mark or a balloon)
游标暗示当前位置可得到帮助(一般为问号或是气球)
|
cursor: no-drop;
|
|
无法释放
|
cursor: no-drop;
|
cursor: text;
|
|
文字/编辑
|
The cursor indicates text
游标暗示当前所处位置为文字内容
|
cursor: move;
|
|
可移动对象
|
The cursor indicates something that should be moved
游标暗示一些东西应该被移动
|
cursor: n-resize;
|
|
向上改变大小(North)
|
The cursor indicates that an edge of a box is to be moved up (north)
边缘可向上移动(北)
|
cursor: s-resize;
|
|
向下改变大小(South)
|
The cursor indicates that an edge of a box is to be moved down (south)
边缘可向下方移动(南)
|
cursor: e-resize;
|
|
向右改变大小(East)
|
The cursor indicates that an edge of a box is to be moved right (east)
box边缘可向右(东)边移动
|
cursor: w-resize;
|
|
向左改变大小(West)
|
The cursor indicates that an edge of a box is to be moved left (west)
边缘可向左移动(西)
|
cursor: ne-resize;
|
|
向上右改变大小(North East)
|
The cursor indicates that an edge of a box is to be moved up and right (north/east)
游标暗示box的边缘可向右上方移动(东北方向)
|
cursor: nw-resize;
|
|
向上左改变大小(North West)
|
The cursor indicates that an edge of a box is to be moved up and left (north/west)
边缘可向左上方移动(西北)
|
cursor: se-resize;
|
|
向下右改变大小(South East)
|
The cursor indicates that an edge of a box is to be moved down and right (south/east)
边缘可向右下方移动(东南)
|
cursor: sw-resize;
|
|
向下左改变大小(South West)
|
The cursor indicates that an edge of a box is to be moved down and left (south/west)
边缘可向左下方移动(西南)
|
cursor: auto;
|
|
自动
|
The browser sets a cursor
浏览器设置一个游标
|
cursor:not-allowed;
|
|
禁止
|
cursor:not-allowed;
|
cursor: progress;
|
|
处理中
|
cursor: progress;
|
cursor: default;
|
|
系统默认
|
The default cursor (often an arrow)
默认的游标状态(通常为一个箭头)
|
cursor: url(' # ');
# = 光标文件地址 (注意文件格式必须为:.cur 或 .ani)。
|
|
用户自定义(可用动画)
|
The url of a custom cursor to be used.
自定义游标的url位置
Note: Always define a generic cursor at the end of the list in case none of the url-defined cursors can be used
注意:在定义完自定义的游标之后在末尾加上一般性的游标,以防那些url所定义的游标不能使用
|
Logo生成网站:
天空秘书网,J/m!h._HK&Rq 强烈推荐:http://www.logomaker.cn
http://phorum.com.tw/Generator.aspx 天空秘书网,VK2V#J&t#W`*A http://www.logoyes.com/lc_leftframe.htm 天空秘书网&jD-L|;X+O8c http://cooltext.com/Default.aspx
Banner图片制作网站:
http://www.bannerbreak.com/index.php
在线图片生成网站:
http://www.streetsigngenerator.com/ %u` U l5e&Bt;Fd0http://www.letterjames.de/
图片分割器: http://www.html-kit.com/e/is.cgi
立体图片生成器: http://www.chami.com/html-kit/services/imge/
图片生成邮票: http://photo.stamps.com/PhotoStamps/?source=si00001331
个性拼图: http://www.jigcool.com/jigonline/jigonline1_sc.php
印章制作: http://caishu.sina.com.cn/
GIF图片的文字LOGO在线生成:http://www.3dtextmaker.com/cgi-bin/3dtext.pl
在线制作logo,bannar的网站:http://www.crazystudy.com
SAINT.Assess the Security of Computer Networks :http://www.saintcorporation.com/saint/
Abacus.Intrusion Prevention System :http://www.psionic.com/abacus/
Firewall Generator(在线生成简单的防火墙脚本)
http://www.citadec.com/FirewallGenerator.html
在线字体图片生成:http://www.youmade.com/font/
在线生成条码打印
http://www.027hc.com/y1.asp [tG+|$M{~/@"dV0http://www.nlscan.com/soucecentre/demo.asp #U|TA I%F Ts0http://www.gzbonny.com/asp/barcode.asp
在线图形特效生成:http://www.chami.com/
聊天工具在线状态生成器:http://www.onlinestatus.org/forum/usage.php
粉丝身份证:http://id.igogo8.com/
制作印章:http://www.makepic.com/print.php
邮址图片生成:http://www.makepic.com/email.php
条形码生成:http://www.makepic.com/barcode.php
Kiss学堂 颁发结业证:http://www.makepic.com/kiss/cert.php
生成头像:http://www.eoool.com/ImageDIY/DIYChooseImg.aspx?ImgSize=96x96x1
邮件:http://www.eoool.com/Sevice.aspx?TypeID=1
聊天图标:http://www.eoool.com/Sevice.aspx?TypeID=2
博客图标:http://www.eoool.com/Sevice.aspx?TypeID=3
网络书签:http://www.eoool.com/Sevice.aspx?TypeID=5
朋友圈:http://www.eoool.com/Sevice.aspx?TypeID=4
按扭:http://www.eoool.com/Sevice.aspx?TypeID=11
生成拼凑图:http://blog.outer-court.com/letters/
一个日本武士刀劈出你需要的字:
http://tools.fodey.com/generators/animated/ninjatext.asp
生成几种卡通人物对话动态图片:
http://tools.fodey.com/generators/animated/talking_squirrel.asp天空秘书网P R_9]H,|,x
^0g3pI#}5l;z.v`F0香烟盒生成,可以做警告图片:http://tools.fodey.com/generators/cigarette_packet/generator.cig
支持多种域名的Email图标的生成: http://www.nhacks.com/email/
两个地址支持两种风格任意文本的Email图标的生成:
http://sagittarius.dip.jp/~toshi/cgi-bin/designmail/designmail.html天空秘书网Q T2z6^9~ \8kZ g'IW http://sagittarius.dip.jp/~toshi/cgi-bin/catmark/catmark.html
在线favicon生成器: http://www.html-kit.com/e/favicon.cgi
支持各种类型图片的生成,可以选择设置的条件非常灵活:
http://www.abi-station.com/tchinese/
支持大量中文字体签名图标生成: http://www.youmade.com/font/
Flickr杂志封面生成器: http://flagrantdisregard.com/flickr/magazine.php
动态生成有趣图片:
爱因斯坦:http://www.hetemeel.com/einsteinform.php
山姆大叔:http://www.hetemeel.com/unclesamform.php
辞典:http://www.hetemeel.com/dictionaryform.php
魔法师:http://www.imagegenerator.net/create/dumbledore/
Flickr Logo风格图片生成器::http://flickr.nosv.org/
按钮生成网站:
http://kalsey.com/tools/buttonmaker/ %u"l*FdqLp X8K9A6Z]0http://www.lucazappa.com/brilliantMaker/buttonImage.php天空秘书网A,W!s)[c{ http://www.feedforall.com/public/rss-graphic-tool.htm天空秘书网L|e"j*BP8kS0A http://www.kalsey.com/tools/buttonmaker/ y$A Si1Y&N/Z0http://www.yugatech.com/make.php天空秘书网R SS2_ E^9[.O http://www.hkwebs.net/catalog/tools/buttonmaker/index.php
Email图标生成网站::
http://email.playtime.uni.cc/ :y]~5d'[d3L Y+k$j0http://services.nexodyne.com/email/ 6nPlesL0http://gizmo967.mgs3.org/Gmail/ R-GD kl3[z0http://www.hkwebs.net/catalog/tools/gmail/ 天空秘书网;N5ha5a*V`P3` http://sagittarius.dip.jp/~toshi/cgi-bin/designmail/designmail.html ~z'Z'GK%h4cq0http://www.eoool.com/
|