利用回调简化JDBC编程
简单看了一下spring 的jdbc支持,现在又要直接用到jdbc,想想就是痛苦。于是参考了spring,自己写了一些简单的java封装类来简化编程。
废话少说,我这里就用代码来代言吧,看看怎样简化我们的JDBC编程,可以和以前进行对比。
(1) JdbcTemplate。
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;import javax.sql.DataSource;import org.winter.util.DBUtil;/**
* a simple JDBC template 模仿spring的JdbcTemplate
*
* @author bluestone
* @version 1.0 2006-8-8
*
*/
public class JdbcTemplate { private DataSource dataSource = null; public JdbcTemplate(DataSource ds) {
this.dataSource = ds;
}/**
* 执行更新操作
*
* @param sql
* @param setter
* @return
* @throws SQLException
*/
public int update(String sql, PreparedStatementSetter setter)
throws SQLException {
Connection conn = null;
reparedStatement ps = null;
try {
conn = dataSource.getConnection();
ps = conn.prepareStatement(sql);
setter.setValues(ps);
return ps.executeUpdate();
} finally {
DBUtil.colseConnection(conn, ps, null);
}
} /**
*
* @param sql
* @return
* @throws SQLException
*/
public boolean execute(String sql) throws SQLException {
Connection conn = null;
Statement stmt = null;
try {
conn = dataSource.getConnection();
stmt = conn.createStatement();
return stmt.execute(sql);
} finally {
DBUtil.colseConnection(conn, stmt, null);
}
} /**
*
* @param sql
* @param setter
* @param extractor
* @return
* @throws SQLException
*/
public Object query(String sql, PreparedStatementSetter setter,
ResultSetExtractor extractor) throws SQLException {
Connection conn = null;
reparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
ps = conn.prepareStatement(sql);
setter.setValues(ps);
rs = ps.executeQuery();
return extractor.extractData(rs);
} finally {
DBUtil.colseConnection(conn, ps, rs);
}
} // .........................
}(2) PreparedStatementSetterpublic interface PreparedStatementSetter {
void setValues(PreparedStatement ps) throws SQLException;
}(3)
ResultSetExtractorpublic interface ResultSetExtractor {
Object extractData(ResultSet rs) throws SQLException;
}(4) 可以参考spring自己定义其他接口。。。
用了这些辅助类,我们就可以像用spring那样编程了(当然这只能用在对事务要求不高的应用环境中)。看看怎么使用:
private JdbcTemplate template; public JobManageDao() throws BusinessException {
try {
template = new JdbcTemplate(DBHelper.getDataSource());
} catch (NamingException e) {
throw new BusinessException(e);
}
}public long saveJobInfo(final JobInfo info) throws BusinessException {
final long id = IdGenerator.getIdLong();
try {
int j = template.update(INSERT_JOB_SQL, new PreparedStatementSetter() { public void setValues(PreparedStatement ps) throws SQLException {
int i = 1;
ps.setLong(i++, id); //...... }
});
return j > 0 ? id : 0L;
} catch (SQLException e) {
throw new BusinessException(e);
}
}
web开发经常遇到这样的情形:写代码的模式基本相同,特别是在写jdbc代码时,会经常要先写sql,然后调用PreparedStatement的setXXX方法,而读取数据时要调用ResultSet的getXXX方法。如果表中的字段很多,那可够你受的了;等你耐心把这些写完,可能在某个地方却出错了。
如果没有用ORM工具,这些又不能省了不写。于是我考虑用代码来生成这些sql 和 setXXX及getXXX方法。
生成代码有许多方法,比如可以用脚本语言(个人喜欢用perl),也可以用模板技术。发现java里面已经有很多模板技术可以直接使用了,比如velocity、freemaker等。我一开始是直接用perl来生成代码的,方法比较原始,就是字符串拼凑在一起。 后来发现有许多的模板技术可以利用。现在打算用velocity来生成代码。说不定可以直接生成DAO、Biz、Bean、XML等一大堆东西,呵呵。等有空要好好研究一下。
在jsp页面,一个表单如果字段很多的话,要写很多request.getParameter(name)之类的代码,如果用web framework的话,则可以免去写这些代码的麻烦。但如果不用framework是否也可以达到参数自动填充的功能呢? 答案是肯定的。
下面是我在就业网重构时用到的一个java类,其中就是对BeanUtils进行了简单的封装。
import java.sql.Date;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.SqlDateConverter;
public class NullSafeBeanUtils {
public final static String EMPTY_STRING = "";
public static boolean isNull(Object obj) {
return obj == null;
}
public static String getProperty(Object bean, String property) {
if (bean == null) {
return EMPTY_STRING;
}
try {
String str = BeanUtils.getProperty(bean, property);
if (str == null) {
return EMPTY_STRING;
}
return str;
} catch (Exception e) {
return EMPTY_STRING;
}
}
public static void populate(Object bean, Map props) {
if (bean == null) {
return;
}
try {
SqlDateConverter con = new SqlDateConverter(new Date(System.currentTimeMillis()));
ConvertUtils.register(con, java.sql.Date.class);
BeanUtils.populate(bean, props);
} catch (Exception e) {
e.printStackTrace();
}
}
// 此处省略了一些其他代码
}
在这里,poplulate方法就是我用来自动填充参数的。要实现自动填充,只需简单调用此方法就行了。看一个例子:
JobExperience jobExp = new JobExperience();
NullSafeBeanUtils.populate(jobExp, request.getParameterMap());
是不是简单了许多?要注意的是表单的各输入字段名要和bean的各属性名对应才能自动填充。另外NullSafeBeanUtils 的getProperty方法也很有用,可以避免写
if (bean != null) {
yyy = bean.getXXX()==null?"":bean.getXXX()
}
这样的代码,直接写NullSafeBeanUtils.getProperty(bean, "XXX")就可以了。
UNIQ(1) User Commands UNIQ(1)
NAME
uniq - remove duplicate lines from a sorted file
SYNOPSIS
uniq [OPTION]... [INPUT [OUTPUT]]
DESCRIPTION
Discard all but one of successive identical lines from INPUT (or stan-
dard input), writing to OUTPUT (or standard output).
Mandatory arguments to long options are mandatory for short options
too.
-c, --count
prefix lines by the number of occurrences
-d, --repeated
only print duplicate lines
-D, --all-repeated[=delimit-method] print all duplicate lines
delimit-method={none(default),prepend,separate} Delimiting is
done with blank lines.
-f, --skip-fields=N
avoid comparing the first N fields
-i, --ignore-case
ignore differences in case when comparing
-s, --skip-chars=N
avoid comparing the first N characters
-u, --unique
only print unique lines
-w, --check-chars=N
compare no more than N characters in lines
--help display this help and exit
--version
output version information and exit
A field is a run of whitespace, then non-whitespace characters.
Fields are skipped before chars.
AUTHOR
Written by Richard Stallman and David MacKenzie.
REPORTING BUGS
Report bugs to <bug-coreutils@gnu.org>.
COPYRIGHT
Copyright © 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICU-
LAR PURPOSE.
SEE ALSO
The full documentation for uniq is maintained as a Texinfo manual. If
the info and uniq programs are properly installed at your site, the
command
info coreutils uniq
should give you access to the complete manual.
作者:cicc 2005-02-03 15:53:59 来自:Linux先生 |
|
a w k是一种程序语言,对文档资料的处理具有很强的功能。awk 名称是由它三个最初设计者的姓氏的第一个字母而命名的: Alfred V. Aho、Peter J. We i n b e rg e r、Brian W. Kernighan。 a w k最初在1 9 7 7年完成。1 9 8 5年发表了一个新版本的a w k,它的功能比旧版本增强了不少。a w k能够用很短的程序对文档里的资料做修改、比较、提取、打印等处理。如果使用C 或P a s c a l等语言编写程序完成上述的任务会十分不方便而且很花费时间,所写的程序也会很大。 a w k不仅仅是一个编程语言,它还是L i n u x系统管理员和程序员的一个不可缺少的工具。a w k语言本身十分好学,易于掌握,并且特别的灵活。 gawk 是G N U计划下所做的a w k,gawk 最初在1 9 8 6年完成,之后不断地被改进、更新。gawk 包含awk 的所有功能。
6.1 gawk的主要功能
gawk 的主要功能是针对文件的每一行( l i n e ),也就是每一条记录,搜寻指定的格式。当某一行符合指定的格式时,gawk 就会在此行执行被指定的动作。gawk 依此方式自动处理输入文件的每一行直到输入文件档案结束。 g a w k经常用在如下的几个方面: • 根据要求选择文件的某几行,几列或部分字段以供显示输出。 • 分析文档中的某一个字出现的频率、位置等。 • 根据某一个文档的信息准备格式化输出。 • 以一个功能十分强大的方式过滤输出文档。 • 根据文档中的数值进行计算。
6.2 如何执行gawk程序
基本上有两种方法可以执行g a w k程序。 如果gawk 程序很短,则可以将gawk 直接写在命令行,如下所示: gawk 'program' input-file1 input-file2 ... 其中program 包括一些pattern 和a c t i o n。 如果gawk 程序较长,较为方便的做法是将gawk 程序存在一个文件中,gawk 的格式如下所示: gawk -f program-file input-file1 input-file2 ... gawk 程序的文件不止一个时,执行gawk 的格式如下所示: gawk -f program-file1 -f program-file2 ... input-file1 input-file2 ...
6.3 文件、记录和字段
一般情况下,g a w k可以处理文件中的数值数据,但也可以处理字符串信息。如果数据没有存储在文件中,可以通过管道命令和其他的重定向方法给g a w k提供输入。当然, g a w k只能处理文本文件(A S C I I码文件)。 电话号码本就是一个g a w k可以处理的文件的简单例子。电话号码本由很多条目组成,每一个条目都有同样的格式:姓、名、地址、电话号码。每一个条目都是按字母顺序排列。在g a w k中,每一个这样的条目叫做一个记录。它是一个完整的数据的集合。例如,电话号码本中的Smith John这个条目,包括他的地址和电话号码,就是一条记录。 记录中的每一项叫做一个字段。在g a w k中,字段是最基本的单位。多个记录的集合组成了一个文件。 大多数情况下,字段之间由一个特殊的字符分开,像空格、TA B、分号等。这些字符叫做字段分隔符。请看下面这个/ e t c / p a s s w d文件: t p a r k e r ; t 3 6 s 6 2 h s h ; 5 0 1 ; 1 0 1 ; Tim Parker;/home/tparker;/bin/bash etreijs;2ys639dj3h;502;101;Ed Tr e i j s ; / h o m e / e t r e i j s ; / b i n / t c s h y c h o w ; 1 h 2 7 s j ; 5 0 3 ; 1 0 1 ; Yvonne Chow;/home/ychow;/bin/bash 你可以看出/ e t c / p a s s w d文件使用分号作为字段分隔符。/ e t c / p a s s w d文件中的每一行都包括七个字段:用户名;口令;用户I D;工作组I D;注释; h o m e目录;启始的外壳。如果你想要查找第六个字段,只需数过五个分号即可。 但考虑到以下电话号码本的例子,你就会发现一些问题: Smith John 13 Wilson St. 555-1283 Smith John 2736 Artside Dr Apt 123 555-2736 Smith John 125 Westmount Cr 555-1726 虽然我们能够分辨出每个记录包括四个字段,但g a w k却无能为力。电话号码本使用空格作为分隔符,所以g a w k认为S m i t h是第一个字段, John 是第二个字段,1 3是第三个字段,依次类推。就g a w k而言,如果用空格作为字段分隔符的话,则第一个记录有六个字段,而第二个记录有八个字段。 所以,我们必须找出一个更好的字段分隔符。例如,像下面一样使用斜杠作为字段分隔符: Smith/John/13 Wilson St./555-1283 Smith/John/2736 Artside Dr/Apt/123/555-2736 Smith/John/125 Westmount Cr/555-1726 如果你没有指定其他的字符作为字段分隔符,那么g a w k将缺省地使用空格或TA B作为字段分隔符。
6.4 模式和动作
在g a w k语言中每一个命令都由两部分组成:一个模式( p a t t e r n)和一个相应的动作(a c t i o n)。只要模式符合,g a w k就会执行相应的动作。其中模式部分用两个斜杠括起来,而动作部分用一对花括号括起来。例如: / p a t t e r n 1 / { a c t i o n 1 } / p a t t e r n 2 / { a c t i o n 2 } / p a t t e r n 3 / { a c t i o n 3 } 所有的g a w k程序都是由这样的一对对的模式和动作组成的。其中模式或动作都能够被省略,但是两个不能同时被省略。如果模式被省略,则对于作为输入的文件里面的每一行,动作都会被执行。如果动作被省略,则缺省的动作被执行,既显示出所有符合模式的输入行而不做任何的改动。 下面是一个简单的例子,因为gawk 程序很短,所以将gawk 程序直接写在外壳命令行: gawk '/tparker/' /etc/passwd
此程序在上面提到的/ e t c / p a s s w d文件中寻找符合t p a r k e r模式的记录并显示(此例中没有动作,所以缺省的动作被执行)。 让我们再看一个例子: gawk '/UNIX/{print $2}' file2.data 此命令将逐行查找f i l e 2 . d a t a文件中包含U N I X的记录,并打印这些记录的第二个字段。你也可以在一个命令中使用多个模式和动作对,例如: gawk '/scandal/{print $1} /rumor/{print $2}' gossip_file 此命令搜索文件g o s s i p _ f i l e中包括s c a n d a l的记录,并打印第一个字段。然后再从头搜索g o s s i p _ f i l e中包括r u m o r的记录,并打印第二个字段。
6.5 比较运算和数值运算
g a w k有很多比较运算符,下面列出重要的几个: = = 相等 ! = 不相等 > 大于 < 小于 > = 大于等于 < = 小于等于 例如: gawk '$4 > 100' testfile 将会显示文件testfile 中那些第四个字段大于1 0 0的记录。 下表列出了g a w k中基本的数值运算符。 运算符说明示例 + 加法运算2+6 - 减法运算6-3 * 乘法运算2*5 / 除法运算8/4 ^ 乘方运算3^2 (=9) % 求余数9%4 (=1) 例如:{print $3/2} 显示第三个字段被2除的结果。 在g a w k中,运算符的优先权和一般的数学运算的优先权一样。例如:{print $1+$2*$3} 显示第二个字段和第三个字段相乘,然后和第一个字段相加的结果。 你也可以用括号改变优先次序。例如: {print ($1+$2)*$3} 显示第一个字段和第二个字段相加,然后和第三个字段相乘的结果。
6.6 内部函数
g a w k中有各种的内部函数,现在介绍如下:
6.6.1 随机数和数学函数
sqrt(x) 求x 的平方根 sin(x) 求x 的正弦函数 cos(x) 求x 的余弦函数 a t a n 2 ( x,y) 求x / y的余切函数 log(x) 求x 的自然对数 exp(x) 求x 的e 次方 int(x) 求x 的整数部分 rand() 求0 和1之间的随机数 srand(x) 将x 设置为r a n d ( )的种子数
6.6.2 字符串的内部函数
• i n d e x ( i n,find) 在字符串in 中寻找字符串find 第一次出现的地方,返回值是字符串find 出现在字符串in 里面的位置。如果在字符串in 里面找不到字符串f i n d,则返回值为0。 例如: print index("peanut"," a n " ) 显示结果3。 • length(string) 求出string 有几个字符。 例如: l e n g t h ( " a b c d e " ) 显示结果5。 • m a t c h ( s t r i n g,r e g e x p ) 在字符串string 中寻找符合regexp 的最长、最靠左边的子字符串。返回值是regexp 在string 的开始位置,即i n d e x值。match 函数将会设置系统变量R S TA RT 等于i n d e x的值,系统变量RLENGTH 等于符合的字符个数。如果不符合,则会设置R S TA RT 为0、RLENGTH 为- 1。 • s p r i n t f ( f o r m a t,e x p r e s s i o n 1,. . . ) 和printf 类似,但是sprintf 并不显示,而是返回字符串。例如: sprintf("pi = %.2f (approx.)",2 2 / 7 ) 返回的字符串为pi = 3.14 (approx.) • s u b ( r e g e x p,r e p l a c e m e n t,t a rg e t ) 在字符串t a rget 中寻找符合regexp 的最长、最靠左的地方,以字串replacement 代替最左边的r e g e x p。 例如: str = "water,w a t e r,e v e r y w h e r e " s u b ( / a t /, " i t h ",s t r ) 结果字符串s t r会变成 w i t h e r,w a t e r,e v e r y w h e r e • g s u b ( r e g e x p,r e p l a c e m e n t,t a rget) 与前面的s u b类似。在字符串t a rget 中寻找符合r e g e x p的所有地方,以字符串replacement 代替所有的r e g e x p。例如: s t r = " w a t e r,w a t e r,e v e r y w h e r e "g s u b ( / a t /, " i t h ",s t r ) 结果字符串s t r会变成 w i t h e r,w i t h e r,e v e r y w h e r e • s u b s t r ( s t r i n g,s t a r t,length) 返回字符串string 的子字符串,这个子字符串的长度为l e n g t h,从第start 个位置开始。例如: s u b s t r ( " w a s h i n g t o n ",5,3 )返回值为i n g 如果没有length ,则返回的子字符串是从第start 个位置开始至结束。 例如: s u b s t r ( " w a s h i n g t o n ",5 ) 返回值为i n g t o n。 • tolower(string) 将字符串s t r i n g的大写字母改为小写字母。 例如: tolower("MiXeD cAsE 123") 返回值为mixed case 123。 • toupper(string) 将字符串s t r i n g的小写字母改为大写字母。 例如: toupper("MiXeD cAsE 123") 返回值为MIXED CASE 123。
6.6.3 输入输出的内部函数
• close(filename) 将输入或输出的文件filename 关闭。 • system(command) 此函数允许用户执行操作系统的指令,执行完毕后将回到g a w k程序。例如: BEGIN {system("ls")}
6.7 字符串和数字
字符串就是一连串的字符,它可以被g a w k逐字地翻译。字符串用双引号括起来。数字不能用双引号括起来,并且g a w k将它当作一个数值。例如: gawk '$1 != "Tim" {print}' testfile 此命令将显示第一个字段和Ti m不相同的所有记录。如果命令中Ti m两边不用双引号,g a w k将不能正确执行。再如: gawk '$1 == "50" {print}' testfile 此命令将显示所有第一个字段和5 0这个字符串相同的记录。g a w k不管第一字段中的数值的大小,而只是逐字地比较。这时,字符串5 0和数值5 0并不相等。
6.8 格式化输出
我们可以让动作显示一些比较复杂的结果。例如: gawk '$1 != "Tim" {print $1,$ 5,$ 6,$2}' testfile 将显示t e s t f i l e文件中所有第一个字段和Ti m不相同的记录的第一、第五、第六和第二个字段。进一步,你可以在p r i n t动作中加入字符串,例如: gawk '$1 != "Tim" {print "The entry for ",$ 1,"is not Tim. ",$2}' testfile p r i n t动作的每一部分用逗号隔开。 借用C语言的格式化输出指令,可以让g a w k的输出形式更为多样。这时,应该用p r i n t f而不是p r i n t。例如: {printf "%5s likes this language\n",$ 2 } p r i n t f中的%5s 部分告诉gawk 如何格式化输出字符串,也就是输出5个字符长。它的值由printf 的最后部分指出,在此是第二个字段。\ n是回车换行符。如果第二个字段中存储的是人名,则输出结果大致如下: Tim likes this language G e o ff likes this language Mike likes this language Joe likes this language gawk 语言支持的其他格式控制符号如下: • c 如果是字符串,则显示第一个字符;如果是整数,则将数字以ASCII 字符的形式显示。 例如: printf “% c”,6 5 结果将显示字母A。 • d 显示十进制的整数。 • i 显示十进制的整数。 • e 将浮点数以科学记数法的形式显示。 例如: print “$ 4 . 3 e”,1 9 5 0 结果将显示1 . 9 5 0 e + 0 3。 • f 将数字以浮点的形式显示。 • g 将数字以科学记数法的形式或浮点的形式显示。数字的绝对值如果大于等于0 . 0 0 0 1则 以浮点的形式显示,否则以科学记数法的形式显示。 • o 显示无符号的八进制整数。 • s 显示一个字符串。 • x 显示无符号的十六进制整数。1 0至1 5以a至f表示。 • X 显示无符号的十六进制整数。1 0至1 5以A至F表示。 • % 它并不是真正的格式控制字符,% %将显示%。 当你使用这些格式控制字符时,你可以在控制字符前给出数字,以表示你将用的几位或几个字符。例如,6 d表示一个整数有6位。再请看下面的例子: {printf "%5s works for %5s and earns %2d an hour",$ 1,$ 2,$ 3 } 将会产生类似如下的输出: Joe works for Mike and earns 12 an hour 当处理数据时,你可以指定数据的精确位数 {printf "%5s earns $%.2f an hour",$ 3,$ 6 } 其输出将类似于: Joe earns $12.17 an hour
你也可以使用一些换码控制符格式化整行的输出。之所以叫做换码控制符,是因为g a w k对这些符号有特殊的解释。下面列出常用的换码控制符:
\a 警告或响铃字符。 \b 后退一格。 \f 换页。 \n 换行。 \r 回车。 \t Ta b。 \v 垂直的t a b。
6.9 改变字段分隔符
在g a w k中,缺省的字段分隔符一般是空格符或TA B。但你可以在命令行使用- F选项改变字符分隔符,只需在- F后面跟着你想用的分隔符即可。 gawk -F" ;"'/tparker/{print}' /etc/passwd 在此例中,你将字符分隔符设置成分号。注意: - F必须是大写的,而且必须在第一个引号之前。
6.10 元字符
g a w k语言在格式匹配时有其特殊的规则。例如, c a t能够和记录中任何位置有这三个字符的字段匹配。但有时你需要一些更为特殊的匹配。如果你想让c a t只和c o n c a t e n a t e匹配,则需要在格式两端加上空格: / cat / {print} 再例如,你希望既和c a t又和C AT匹配,则可以使用或(|): / cat | CAT / {print} 在g a w k中,有几个字符有特殊意义。下面列出可以用在g a w k格式中的这些字符: • ^ 表示字段的开始。 例如:$3 ~ /^b/ 如果第三个字段以字符b开始,则匹配。 • $ 表示字段的结束。 例如:$3 ~ /b$/ 如果第三个字段以字符b结束,则匹配。 • . 表示和任何单字符m匹配。 例如:$3 ~ /i.m/ 如果第三个字段有字符i,则匹配。 • | 表示“或”。 例如:/ c a t | C AT/ 和cat 或C AT字符匹配。 • * 表示字符的零到多次重复。 例如:/UNI*X/ 和U N X、U N I X、U N I I X、U N I I I X等匹配。 • + 表示字符的一次到多次重复。 例如: /UNI+X/ 和U N I X、U N I I X等匹配。 • \{a,b\} 表示字符a次到b次之间的重复。 例如: / U N I \ { 1,3 \ } X 和U N I X、U N I I X和U N I I I X匹配。 • ? 表示字符零次和一次的重复。 例如: /UNI?X/ 和UNX 和U N I X匹配。 • [] 表示字符的范围。 例如: /I[BDG]M/ 和I B M、I D M和I G M匹配 • [^] 表示不在[ ]中的字符。 例如: /I[^DE]M/ 和所有的以I开始、M结束的包括三个字符的字符串匹配,除了I D M和I E M之外。
6.11 调用gawk程序
当需要很多对模式和动作时,你可以编写一个g a w k程序(也叫做g a w k脚本)。在g a w k程序中,你可以省略模式和动作两边的引号,因为在g a w k程序中,模式和动作从哪开始和从哪结束时是很显然的。你可以使用如下命令调用g a w k程序: gawk -f script filename 此命令使g a w k对文件f i l e n a m e执行名为s c r i p t的g a w k程序。 如果你不希望使用缺省的字段分隔符,你可以在f选项后面跟着F选项指定新的字段分隔符(当然你也可以在g a w k程序中指定),例如,使用分号作为字段分隔符: gawk -f script -F";" filename 如果希望gawk 程序处理多个文件,则把各个文件名罗列其后: gawk -f script filename1 filename2 filename3 ... 缺省情况下, g a w k的输出将送往屏幕。但你可以使用L i n u x的重定向命令使g a w k的输出送往一个文件: gawk -f script filename > save_file
6.12 BEGIN和END
有两个特殊的模式在g a w k中非常有用。B E G I N模式用来指明g a w k开始处理一个文件之前执行一些动作。B E G I N经常用来初始化数值,设置参数等。E N D模式用来在文件处理完成后执行一些指令,一般用作总结或注释。 BEGIN 和E N D中所有要执行的指令都应该用花括号括起来。BEGIN 和E N D必须使用大写。 请看下面的例子: BEGIN { print "Starting the process the file" } $1 == "UNIX" {print} $2 > 10 {printf "This line has a value of %d",$ 2 } END { print "Finished processing the file. Bye!"} 此程序中,先显示一条信息: Starting the process the file,然后将所有第一个字段等于U N I X的整条记录显示出来,然后再显示第二个字段大于10 的记录,最后显示信息: F i n i s h e dprocessing the file. Bye!。
6.13 变量
在g a w k中,可以用等号( = )给一个变量赋值: var1 = 10 在g a w k中,你不必事先声明变量类型。 请看下面的例子: $1 == "Plastic" { count = count + 1 } 如果第一个字段是P l a s t i c,则c o u n t的值加1。在此之前,我们应当给c o u n t赋予过初值,一般是在B E G I N部分。 下面是比较完整的例子: BEGIN { count = 0 } $5 == "UNIX" { count = count + 1 } END { printf "%d occurrences of UNIX were found",count } 变量可以和字段和数值一起使用,所以,下面的表达式均为合法: count = count + $6 count = $5 - 8 count = $5 + var1 变量也可以是格式的一部分,例如: $2 > max_value {print "Max value exceeded by ",$2 -max_value} $4 - var1 < min_value {print "Illegal value of ",$ 4 }
6.14 内置变量
g a w k语言中有几个十分有用的内置变量,现在列于下面:
NR 已经读取过的记录数。 FNR 从当前文件中读出的记录数。 F I L E N A M E 输入文件的名字。 FS 字段分隔符(缺省为空格)。 RS 记录分隔符(缺省为换行)。 OFMT 数字的输出格式(缺省为% g)。 OFS 输出字段分隔符。 ORS 输出记录分隔符。 NF 当前记录中的字段数。
如果你只处理一个文件,则NR 和FNR 的值是一样的。但如果是多个文件, N R是对所有的文件来说的,而FNR 则只是针对当前文件而言。例如: NR <= 5 {print "Not enough fields in the record"} 检查记录数是否小于5,如果小于5,则显示出错信息。 F S十分有用,因为F S控制输入文件的字段分隔符。例如,在B E G I N格式中,使用如下的 命令: F S = " : "
6.15 控制结构
6.15.1 if 表达式
if 表达式的语法如下: if (expression){ c o m m a n d s } e l s e { c o m m a n d s } 例如: # a simple if loop (if ($1 == 0){ print "This cell has a value of zero" } else { printf "The value is %d\n",$ 1 } ) 再看下一个例子: # a nicely formatted if loop (if ($1 > $2){ print "The first column is larger" } else { print "The second column is larger" } )
6.15.2 while 循环 while 循环的语法如下: while (expression){ c o m m a n d s } 例如: # interest calculation computes compound interest # inputs from a file are the amount,interest_rateand years {var = 1 while (var <= $3) { p r i n t f ( " % f \ n ",$ 1 * ( 1 + $ 2 ) ^ v a r ) v a r + +} }
6.15.3 for 循环
for 循环的语法如下: for (initialization; expression; increment) { c o m m a n d } 例如: # interest calculation computes compound interest # inputs from a file are the amount,interest_rateand years {for (var=1; var <= $3; var++) { p r i n t f ( " % f \ n ",$ 1 * ( 1 + $ 2 ) ^ v a r ) } } 6.15.4 next 和exit
next 指令用来告诉gawk 处理文件中的下一个记录, 而不管现在正在做什么。语法如下: { command1 c o m m a n d 2 c o m m a n d 3 n e x t c o m m a n d 4 } 程序只要执行到n e x t指令,就跳到下一个记录从头执行命令。因此,本例中, c o m m a n d 4指令永远不会被执行。 程序遇到e x i t指令后,就转到程序的末尾去执行E N D,如果有E N D的话。
6.16 数组
g a w k语言支持数组结构。数组不必事先初始化。声明一个数组的方法如下: a r r a y n a m e [ n u m ] = v a l u e 请看下面的例子: # reverse lines in a file {line[NR] = $0 } # remember each line END {var=NR # output lines in reverse order while (var > 0){ print line[var] v a r - - } } 此段程序读取一个文件的每一行,并用相反的顺序显示出来。我们使用N R作为数组的下标来存储文件的每一条记录,然后在从最后一条记录开始,将文件逐条地显示出来。
6.17 用户自定义函数
复杂的gawk 程序常常可以使用自己定义的函数来简化。调用用户自定义函数与调用内部函数的方法一样。函数的定义可以放在gawk 程序的任何地方。 用户自定义函数的格式如下: function name (parameter-list) { b o d y - o f - f u n c t i o n } name 是所定义的函数的名称。一个正确的函数名称可包括一序列的字母、数字、下标线( u n d e r s c o r e s ),但是不可用数字做开头。p a r a m e t e r-list 是函数的全部参数的列表,各个参数之间以逗点隔开。body-of-function 包含gawk 的表达式,它是函数定义里最重要的部分,它决定函数实际要做的事情。 下面这个例子,会将每个记录的第一个字段的值的平方与第二个字段的值的平方加起来。 {print "sum =",S q u a r e S u m ( $ 1,$ 2 ) } function SquareSum(x,y) { s u m = x * x + y * y return sum } 到此,我们已经知道了g a w k的基本用法。g a w k语言十分易学好用,例如,你可以用g a w k编写一段小程序来计算一个目录中所有文件的个数和容量。如果用其他的语言,如C语言,则会十分的麻烦,相反,g a w k只需要几行就可以完成此工作。
6.18 几个实例
最后,再举几个g a w k的例子: gawk '{if (NF > max) max = NF} END {print max}' 此程序会显示所有输入行之中字段的最大个数。 gawk 'length($0) > 80' 此程序会显示出超过80 个字符的每一行。此处只有模式被列出,动作是采用缺省值显示整个记录。 gawk 'NF > 0' 显示拥有至少一个字段的所有行。这是一个简单的方法,将一个文件里的所有空白行删除。 gawk 'BEGIN {for (i = 1; i <= 7; i++) print int(101 * rand())}' 此程序会显示出范围是0 到100 之间的7 个随机数。 ls -l files | gawk '{x += $4}; END {print "total bytes: " x}' 此程序会显示出所有指定的文件的总字节数。 expand file | gawk '{if (x < length()) x = length()} END {print "maximum line length is " x}' 此程序会将指定文件里最长一行的长度显示出来。expand 会将tab 改成s p a c e,所以是用实际的右边界来做长度的比较。 gawk 'BEGIN {FS = ":"} {print $1 | "sort"}' /etc/passwd 此程序会将所有用户的登录名称,依照字母的顺序显示出来。 gawk '{nlines++} END {print nlines}' 此程序会将一个文件的总行数显示出来。 gawk 'END {print NR}' 此程序也会将一个文件的总行数显示出来,但是计算行数的工作由g a w k来做。 gawk '{print NR,$ 0 } ' 此程序显示出文件的内容时,会在每行的最前面显示出行号,它的函数与‘ cat -n’类似。
|