|
2006年4月2日
/Files/rttw/Excel.zip
本程序用来将多个excel 报表合并成一个文件,取第一个文件的前n 行做为报表的题头,n 可自定义。
1.支持直接添加多个文件
2.支持直接添加某个文件夹下面的所有excel文件
3 支持直接将excel文件拖放到程序界面
4.可以单选或多选记录然后用右键删除
5.可以定义excel表头记录数
6.本版本暂时只支持合并Excel文件的第一个sheet
7.双击某条记录可直接删除
8.开发环境 VC6+OFFICE2007,其他版本OFFICE有可能不兼容
北京寄来的件,已经第四天了,打电话到广州中转,说还没到广州,竟然让我自己打电话到北京去问.再说了两句竟然挂了我的电话.简直是垃圾啊.客户打电话上来的有问题的件应该是快递公司内部处理解决吧,竟然让客户自己去跟踪??奉劝大家以后千万不要用申通快递,切记且记
[root@linux-test188 ~]# cdrecord -scanbus Cdrecord-Clone 2.01-dvd (i686-pc-linux-gnu) Copyright (C) 1995-2004 J枚rg Schilling Note: This version is an unofficial (modified) version with DVD support Note: and therefore may have bugs that are not present in the original. Note: Please send bug reports or support requests to http://bugzilla.redhat.com/bugzilla Note: The author of cdrecord should not be bothered with problems in this version. scsidev: 'ATA' devname: 'ATA' scsibus: -2 target: -2 lun: -2 Linux sg driver version: 3.5.27 Using libscg version 'schily-0.8'. cdrecord: Warning: using inofficial libscg transport code version (schily - Red Hat-scsi-linux-sg.c-1.83-RH '@(#)scsi-linux-sg.c 1.83 04/05/20 Copyright 1997 J. Schilling'). scsibus1: 1,0,0 100) 'HL-DT-ST' 'CD-RW GCE-8400B ' '1.02' Removable CD-ROM 1,1,0 101) * 1,2,0 102) * 1,3,0 103) * 1,4,0 104) * 1,5,0 105) * 1,6,0 106) * 1,7,0 107) * [root@linux-test188 ~]# cdrecord -v speed=8 dev=1,0,0 test.iso
[root@linux-test188 ~]# mkisofs -r -o cd.iso -m temp ./tempfiles
select * from a,b where a.id=b.id(+); select * from a left join b on a.id=b.id;
很多资料说上面两个语句的效果是一样的,实际上今天经过测试发现两者的执行计划大不相同(查询结果是一样的); 至于为什么会这样,暂时没有深究,手头的一个例子表明按照第一种写法的效率会高,或许其他的例子结果不一样,等有时间的时候再测试一下吧.
同样的一条语句,一条是在pl/sql里面组装成sql之后提交执行,另外一条是用pro*c程序后绑定执行,没想到执行策略和效率天差地别。看来做什么事都不能想当然,要细心学习才行。保留此条语句以做纪念:
select * from (select /*+ INDEX(A IDX_HISBUFAREJOUR_ACCOUNT) +*/ a.*, f.sort_name from hs_his.hisbufarejour a, hs_fund.faresort f where (((((((a.fare_sort = f.fare_sort and a.client_id = :b0) and (:b1 = 0 or a.fund_account = :b2)) and a.init_date >= :b3) and a.init_date <= :b4) and (trim(:b5) is null or instr(((',' || :b6) || ','), ((',' || a.exchange_type) || ',')) > 0)) and (trim(:b7) is null or instr(((',' || :b8) || ','), ((',' || a.money_type) || ',')) > 0)) and (trim(:b9) is null or a.position_str > :b10)) order by a.position_str) where rownum <= :b11
b0:130330000516 b1:595995 b2:595995 b3:20061221 b4:20061221 b5:1 b6:1 b7:2 b8:2 b9: b10: b11:30
vc的对话框中,如果直接用picturebox作为对话框的背景,则其他控件会被挡住。经过高手指点,重载了WM_ERASEBKGND消息处理函数,很少的解决了这个问题。而且按照这个思路,可以很方便的为dialog增加动态的skin:
声明消息处理函数:
// Generated message map functions //{{AFX_MSG(Input) afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnButtonOk(); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnPaint(); //}}AFX_MSG DECLARE_MESSAGE_MAP()
声明消息循环: BEGIN_MESSAGE_MAP(Input, CDialog) //{{AFX_MSG_MAP(Input) ON_BN_CLICKED(IDC_BUTTON_OK, OnButtonOk) ON_WM_LBUTTONDBLCLK() ON_WM_PAINT() ON_WM_ERASEBKGND() //}}AFX_MSG_MAP END_MESSAGE_MAP()
实现: BOOL Input::OnEraseBkgnd(CDC* pDC) {
if(m_hBmp) { BITMAP bm; GetObject(m_hBmp,sizeof(bm),&bm); HDC hMemdc=CreateCompatibleDC(pDC->m_hDC); if(hMemdc) { HBITMAP hOldBmp=(HBITMAP)SelectObject(hMemdc,m_hBmp); if(hOldBmp) { BitBlt(pDC->m_hDC,0,0,bm.bmWidth,bm.bmHeight,hMemdc,0,0,SRCCOPY); SelectObject(hMemdc,hOldBmp); DeleteDC(hMemdc); DeleteObject(hOldBmp); return TRUE; } else DeleteDC(hMemdc); } } return CDialog::OnEraseBkgnd(pDC); }
今天系统莫明其妙的故障,以前用的好好的客户信息汇总统计突然出现异常,查看日志显示oracle的sql语句异常. 跟踪调试后台发现问题出现在sprintf语句上面,sql预定义 char[8000],然后用sprintf来格式化,结果当语句的数据超过8000的时候就不能正常处理了. google了一下:
众所周知,sprintf不能检查目标字符串的长度,可能造成众多安全问题,所以都会推荐使用snprintf.
snprintf(_snprintf)的声明是这样的
int _snprintf( char *buffer, size_t count, const char *format [, argument] ... );
If len < count, then len characters are stored in buffer, a null-terminator is appended, and len is returned.
If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.
If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.
最常见的错误用法有: 1. char sa[256]={0}; _snprintf(sa,sizeof(sa),"%s",sb); //错误原因:当sb的长度>=256的时候,sa将没有'\0'结尾
2. char sa[256]; _snprintf(sa,sizeof(sa)-1,"%s",sb); //错误原因:当sb的长度>=255的时候,sa将没有'\0'结尾,忘记给sa初始化
3. char sa[256]; _snprintf(sa,sizeof(sa)-1,"%s",sb); sa[sizeof(sa)]=0; //错误原因:最后一行数组越界
正确的用法 1. //推荐用法 char sa[256]; sa[sizeof(sa)-1]=0; _snprintf(sa,sizeof(sa),"%s",sb); if(sa[sizeof(sa)-1]!=0) { printf("warning:string will be truncated"); sa[sizeof(sa)-1]=0; }
2. char sa[256]={0}; int result = _snprintf(sa,sizeof(sa),"%s",sb); if(result==sizeof(sa) || result<0) { printf("warning:sting will be truncated"); sa[sizeof(sa)-1]=0; }
首先定义消息:
#define POST_DATA_START WM_USER+1 #define POST_DATA_END WM_USER+2 #define RECEIVE_DATA_START WM_USER+3 #define RECEIVE_DATA_END WM_USER+4 #define THREAD_START WM_USER+5 #define THREAD_END WM_USER+6 #define TASK_START WM_USER+7 #define TASK_END WM_USER+8 #define TASK_IDEL WM_USER+9
声明处理函数 //{{AFX_MSG(CMyDlg) ........ afx_msg void OnPostDataStart(WPARAM wParam, LPARAM lParam); afx_msg void OnPostDataEnd(WPARAM wParam, LPARAM lParam); afx_msg void OnReceiveDataStart(WPARAM wParam, LPARAM lParam); afx_msg void OnReceiveDataEnd(WPARAM wParam, LPARAM lParam); afx_msg void OnThreadStart(WPARAM wParam, LPARAM lParam); afx_msg void OnThreadEnd(WPARAM wParam, LPARAM lParam); afx_msg void OnTaskStart(WPARAM wParam, LPARAM lParam); afx_msg void OnTaskEnd(WPARAM wParam, LPARAM lParam); afx_msg void OnTaskIdel(WPARAM wParam, LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() 注意所有函数声明都要在 DECLARE_MESSAGE_MAP()前面
绑定消息和处理函数: BEGIN_MESSAGE_MAP(CMyDlg, CDialog) //{{AFX_MSG_MAP(CMyDlg) ON_MESSAGE(POST_DATA_START,OnPostDataStart) ON_MESSAGE(POST_DATA_END,OnPostDataEnd) ON_MESSAGE(POST_DATA_START,OnReceiveDataStart) ON_MESSAGE(RECEIVE_DATA_START,OnReceiveDataEnd) ON_MESSAGE(RECEIVE_DATA_END,OnThreadStart) ON_MESSAGE(THREAD_START,OnThreadEnd) ON_MESSAGE(TASK_END,OnTaskStart) ON_MESSAGE(TASK_START,OnTaskEnd) ON_MESSAGE(TASK_IDEL,OnTaskIdel)
//}}AFX_MSG_MAP END_MESSAGE_MAP()
当然最后还要完成函数实体
新装的redhat linux,先增加系统用户,然后
cat /etc/passwd|mksmbpasswd.sh > /etc/samba/smbpasswd 此命令根据系统的用户文件 /etc/passwd自动生成了samba的用户文件
smbpasswd 修改用户登陆samba的密码
service smb restart 重启samba服务
通过以上三步可以用os用户登陆samba(密码不是os的,可以单独改),登陆后默认可以访问该用户在os的home目录
补充: 修改 [global]下面的 security = user为security = share可以允许匿名登陆,登陆后可以访问的内容可以用guest ok = yes 来标记
为了工作需要不得不开始学习delphi。 看了一个星期的代码和资料,由于有C++和java的基础,所以看起来并不算费力。 主观上觉得delphi的设计理念更简单和容易理解,程序架构比较清晰,但是也有比较别扭的地方,觉得pascal的条条框框太多,不想C++和Java那样随心所欲。 delphi有相对非常丰富的lib可以使用,可以用来快速的开发解决一些简单的问题,尤其是对数据库的支持比vc/java要方便得多(只是说方便,不是强大),还是值得一学的。
我所向往的, 是最简单的、平凡的幸福, 不用呼吸浑浊的空气, 不用厌烦吵闹拥挤的交通和人群.
我只希望, 每天早上醒来, 可以看到从窗口射进来的第一缕阳光, 照着你的脸
以前写jdbc相关的程序一直没有注意到 statement/resultset 是需要关闭的,直到最近在一个系统里面发现了这个“maximum open cursors exceeded”的异常。 通过查找相关文档,原来一个statement通常会对应一个db cursor,如果大量使用statement而不关闭就会引起此异常,关闭的代码很简单:
if(rs!=null) //ResultSet try { rs.close(); } catch (SQLException e1) { logger.error(e1.getMessage());
}
if(pst!=null) //PreparedStatement try { pst.close(); } catch (SQLException e1) { logger.error(e1.getMessage()); }
有二个和尚住在隔壁,所谓隔壁是隔壁那座山,他们分别住在相邻的二座山上的庙里。这 二座山之间有一条溪,於是这二个和尚每天都会在同一时间下山去溪边挑水,久而久之他 们便成为好朋友了。就这样时间在每天挑水中不知不觉已经过了五年,突然有一天左边这 座山的和尚没有下山挑水,右边那座山的和尚心想:「他大概睡过头了。」便不以为意。 哪知第二天左边这座山的和尚还是没有下山挑水,第三天也一样,过了一个星期还是一样 ,直到过了一个月右边那座山的和尚终於受不了了,他心想:「我的朋友可能生病了,我 要过去拜访他,看看能帮上什么忙。」於是他便爬上了左边这座山,去探望他的老朋友。
等他到达左边这座山的庙,看到他的老友之後大吃一惊,因为他的老友正在庙前打太 极拳,一点也不像一个月没喝水的人。他很好奇地问:「你已经一个月没有下山挑水了, 难道你可以不用喝水吗?」 左边这座山的和尚说:「来来来,我带你去看。」於是他带著右边那座山的和尚走到 庙的後院,指著一口井说:「这五年来,我每天做完功课後都会抽空挖这口井,即使有时 很忙,能挖多少就算多少。如今终於让我挖出井水,我就不必再下山挑水,我可以有更多 时间练我喜欢的太极拳。」 我们在公司领的薪水股票再多,那是挑水。若我们只是把握下班後的时间挖一口属於 自己的井,未来当我们年纪大了,体力拼不过年轻人了,我们还是有水喝,而且喝得很悠 闲。
时间管理专家为一群商学院学生讲课。他现场做了演示,给学生们留下一生难以磨灭的印象。
站在那些高智商、高学历的学生前面,他说:"我们来个小测验",拿出一个一加仑的广口瓶放在他面前的桌上。 随后,他取出一堆拳头大小的石块,仔细地一块块放进玻璃瓶里。直到石块高出瓶口,再也放不下了,他问道:"瓶子满了吗?"所有学生应道:"满了"。时间管理专家反问:"真的?"他伸手从桌下拿出一桶砾石,倒了一些进去,并敲击玻璃瓶壁使砾石填满下面石块的间隙。"现在瓶子满了吗?"他第二次问道。
但这一次学生有些明白了,"可能还没有",一位学生应道。"很好!"专家说。他伸手从桌下拿出一桶沙子,开始慢慢倒进玻璃瓶。沙子填满了石块和砾石的所有间隙。他又一次问学生:"瓶子满了吗?""没满!"学生们大声说。他再一次说:"很好。"然后他拿过一壶水倒进玻璃瓶直到水面与瓶口平。抬头看着学生,问道:"这个例子说明什么?" 一个心急的学生举手发言:"它告诉我们:无论你的时间表多么紧凑,如果你确实努力,你可以做更多的事!"。"不!",时间管理专家说,"那不是它真正的意思。这个例子告诉我们:
如果你不是先放大石块,那你就再也不能把它放进瓶子里。那么,什么是你生命中的大石块呢,与你爱人共度时光,你的信仰,教育,梦想,或是和我一样,教育指导其他人?切切记得先去处理这些''大石块'',否则,一辈子你都不能做到。"
那么,今晚,或许是今晨,你正在阅读这篇短文,可曾试着问自己这个问题:我今生的''大石头''是什么?然后,请把它们先放进你人生的瓶子.
package com.gf.rttw.warrants;
import java.util.Timer; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator;
public class Main { static Logger logger = Logger.getLogger(Main.class); static { PropertyConfigurator.configure("log4jconfig.properties"); } public static void main(String[] arg) { //get args from the console int gap=0; try { if(arg.length<1) { throw new Exception(); } gap=Integer.parseInt(arg[0]); if((gap<1)||(gap>3600)) { throw new Exception(); } } catch (Exception e) { logger.error("PLEASE INPUT THE TIMER GAP 1-3600(SECONDS)"); System.exit(1); } java.util.Timer timer= new Timer(true); timer.schedule( new java.util.TimerTask() { public void run() { logger.debug("run task once"); //define any task you like } }, 0, gap*1000 ); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { logger.fatal(e.getMessage()); } } }
上面的代码很简单,从命令行读取一个参数(以秒为单位的时间间隔)然后该程序就会每隔一段时间打印一个"run task once";
说明:根据提供的sql/标题/参数列表进行查询,返回的二维String数组可以直接用来显示,将会陆续增加排序/格式化等功能,核心的execQuery方法如下:
public String[][] execSQL(String sql,String[] titles,Object[] params) throws Exception {
//todo:验证数据库连接是否可用 if(con==null||con.isClosed())throw new Exception("数据库连接不可用");
//todo:简单的验证输入参数 if(sql==null) throw new Exception("SQL语句不能为空"); sql=sql.trim(); if(sql.equals("")) throw new Exception("SQL语句不能为空"); //对输入参数做简单的日志记录 logger.info(sql); logger.info(Dao.msgOfArray(titles)); logger.info(Dao.msgOfArray(params)); //准备sql PreparedStatement pst = con.prepareStatement(sql);
//如果参数不空,则准备参数 if(params!=null) { for(int i=0;i<params.length;i++) { if(params[i]==null) { break; } pst.setObject(i+1,params[i]); //此处依赖JDBC Driver实现,不同的驱动可能实现的程度不一样 } }
//查询 ResultSet rs = pst.executeQuery(); //metaData ResultSetMetaData md = rs.getMetaData(); int colCount = md.getColumnCount(); ArrayList al = new ArrayList(maxCount); //maxCount为Dao的一个属性,定义每次查询返回的最大记录数
//如果参数没有输入表格头信息,则从metadata取得列名字作为默认值 if (titles == null) { titles = new String[colCount]; for (int i = 0; i < colCount; i++) { String columnName = md.getColumnName(i + 1); if (columnName == null) { columnName = "无列名"; }; titles[i] = columnName; } }
int count = 0; al.add(count++, titles); // 标题
//遍历结果集合 while (rs.next()) { String[] buffer = new String[colCount]; for (int i = 0; i < colCount; i++) { int type = md.getColumnType(i + 1); int scale = md.getScale(i + 1); String value=null;
//todo:根据对于页面显示的要求,对不同的数据类型用不同的方式格式化 switch (type) { case Types.LONGVARCHAR: // -1 dataType="Long"; value=rs.getString(i+1); break; case Types.CHAR: // 1 dataType="Character"; value=rs.getString(i+1); break; case Types.NUMERIC: // 2 switch (scale) { case 0: // dataType="Number"; value=rs.getString(i+1); break; case -127: // dataType="Float"; value=rs.getString(i+1); break; default: value=rs.getString(i+1); break; } break;
case Types.VARCHAR: // 12 // dataType="String"; value=rs.getString(i+1); break; case Types.DATE: // 91 // dataType="Date"; value=rs.getString(i+1); break; case Types.TIMESTAMP: // 93 // dataType="Date"; value=rs.getString(i+1); break; case Types.BLOB: // dataType="Blob"; value="不支持的数据类型"; break; default: value=rs.getString(i+1); } buffer[i]=value; } al.add(count++, buffer); if (count >= maxCount) { break; } }
//convert to String[][] and return String[][] returnValue = new String[count][]; for (int i = 0; i < count; i++) { returnValue[i] = (String[]) al.get(i); } return returnValue; } //end here
最近做一个基于J2EE的WEB项目,该项目的特点是查询比较多,本来设计中打算用hibernate实现持久层,但是基于项目时间紧张和开发人员不熟悉hibernate而取消.于是想到了传统的Dao,加上最近看了好多关于类反射的东西,于是自己写了一个简单的Dao,基本的方法很简单,输入sql语句/参数/,返回String二维数组(直接在页面显示).由于只是针对web显示,所以在设计上加入了一些小技巧,例如数组的第一行是表头信息,通过参数传入,如果参数为null则根据dbms metadata来读取,相关的api列表如下:
其中以Simple开头的查询方法只支持单条查询并直接返回结果,其打开和关闭连接等过程在内部自动实现.以exec开头的的方法支持多条查询和update,支持事务,要自己打开(init)和关闭(close)连接,下面是一个stuts actionbean里面的代码片断,是不是看起来很简单呢:)
Dao d = new Dao(); String[][] result = null; String sql="select name,phone from users where schoolID=? and classID=?" result=d.simplyQuery(sql, new String[]{"姓名","电话号码"}, //表头 new String[]{theForm.getSchoolID(),theForm.getClassID} //参数 ); request.getSession().setAttribute("xxxx..",result);
作者:刘颖博
时间:2004-6-12 mail:liuyingbo@126.com,请指正 转载请注明出处及作者
本文讨论的是关于oracle从8i开始引进object的概念后的rowid,即扩展(extended)的rowid:
1. rowid的介绍
先对rowid有个感官认识:
SQL> select ROWID from Bruce_test where rownum<2;
ROWID ------------------ ---------- AAABnlAAFAAAAAPAAA ROWID的格式如下:
数据对象编号 文件编号 块编号 行编号 OOOOOO FFF BBBBBB RRR
我们可以看出,从上面的rowid可以得知: AAABnl 是数据对象编号 AAF是相关文件编号 AAAAAP是块编号 AAA 是行编号
怎么依据这些编号得到具体的十进制的编码值呢,这是经常遇到的问题。这里需要明白rowid的是基于64位编码的18个字符显示(数据对象编号(6) +文件编号(3) +块编号(6)+ 行编号(3)=18位),其中 A-Z <==> 0 - 25 (26) a-z <==> 26 - 51 (26) 0-9 <==> 52 - 61 (10) +/ <==> 62 - 63 (2)
共64位,明白这个后,就可以计算出10进制的编码值,计算公式如下: d * (b ^ p) 其中:b就是基数,这里就是64,p就是从右到左,已0开始的位置数 比如:上面的例子 文件号AAF,具体的计算应该是: 5*(64^0)=5; 0*(64^1)=0; 0*(64^2)=0; 文件号就是0+0+5=5 刚才提到的是rowid的显示方式:基于64位编码的18个字符显示,其实rowid的存储方式是:10 个字节即80位存储,其中数据对象编号需要32 位,相关文件编号需要10 位,块编号需要22,位行编号需要16 位,由此,我们可以得出: 32bit的object number,每个数据库最多有4G个对象 10bit的file number,每个对象最多有1022个文件(2个文件预留) 22bit的block number,每个文件最多有4M个BLOCK 16bit的row number,每个BLOCK最多有64K个ROWS
2. rowid相关的有用的sql
最简单的基于rowid的显示方式得到的响应的64位编码对应值的sql: select rowid , substr(rowid,1,6) "OBJECT", substr(rowid,7,3) "FILE", substr(rowid,10,6) "BLOCK", substr(rowid,16,3) "ROW" from TableName;
OWID OBJECT FILE BLOCK ROW ------------------ ------------ ------ ------------ ------ AAABc4AADAAAGLUAAA AAABc4 AAD AAAGLU AAA AAABc4AADAAAGLUAAB AAABc4 AAD AAAGLU AAB AAABc4AADAAAGLUAAC AAABc4 AAD AAAGLU AAC AAABc4AADAAAGLUAAD AAABc4 AAD AAAGLU AAD AAABc4AADAAAGLUAAE AAABc4 AAD AAAGLU AAE
通过dbms_rowid这个包,可以直接的得到具体的rowid包含的信息: select dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id, dbms_rowid.rowid_block_number(rowid) block_id ,dbms_rowid.rowid_row_number(rowid) num from bruce_t where rownum<5;
OBJECT_ID FILE_ID BLOCK_ID NUM ---------- ---------- ---------- ---------- 5944 3 25300 0 5944 3 25300 1 5944 3 25300 2 5944 3 25300 3
一些使用ROWID的函数 ROWIDTOCHAR(rowid) :将ROWID转换成STRING CHARTOROWID('rowid_string') :将STRING转换成ROWID
另外,就是自己写的一些函数:(下面的函数是网友eygle提供)
create or replace function get_rowid
(l_rowid in varchar2) return varchar2 is ls_my_rowid varchar2(200); rowid_type number; object_number number; relative_fno number; block_number number; row_number number;
begin dbms_rowid.rowid_info(l_rowid,rowid_type,object_number,relative_fno, block_number, row_number); ls_my_rowid := 'Object# is :'||to_char(object_number)||chr(10)|| 'Relative_fno is :'||to_char(relative_fno)||chr(10)|| 'Block number is :'||to_char(block_number)||chr(10)|| 'Row number is :'||to_char(row_number); return ls_my_rowid ; end;
/
应用上面的函数如下: SQL> select get_rowid(rowid), name from bruce_t; GET_ROWID(ROWID) NAME
-------------------------------------------------------------------------------- -------------------------------- Object# is :5944 BruceLau Relative_fno is :3 Block number is :25300 Row number is :0 Object# is :5944 MabelTang Relative_fno is :3 Block number is :25300 Row number is :1
SELECT * INTO XLImport3 FROM OPENDATASOURCE('Microsoft.Jet.OLEDB.4.0', 'Data Source=C:\test\xltest.xls;Extended Properties=Excel 8.0')...[Customers$]
SELECT * INTO XLImport4 FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=C:\test\xltest.xls', [Customers$])
SELECT * INTO XLImport5 FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=C:\test\xltest.xls', 'SELECT * FROM [Customers$]')
crontab是一个很方便的在unix/linux系统上定时(循环)执行某个任务的程序
基本用法: 1. crontab -l 列出当前的crontab任务 2. crontab -d 删除当前的crontab任务 3. crontab -e (solaris5.8上面是 crontab -r) 编辑一个crontab任务,ctrl_D结束 4. crontab filename 以filename做为crontab的任务列表文件并载入
crontab file的格式: crontab 文件中的行由 6 个字段组成,不同字段间用空格或 tab 键分隔。前 5 个字段指定命令要运行的时间 分钟 (0-59) 小时 (0-23) 日期 (1-31) 月份 (1-12) 星期几(0-6,其中 0 代表星期日) 第 6 个字段是一个要在适当时间执行的字符串
例子: #MIN HOUR DAY MONTH DAYOFWEEK COMMAND #每天早上6点10分 10 6 * * * date #每两个小时 0 */2 * * * date (solaris 5.8似乎不支持此种写法) #晚上11点到早上8点之间每两个小时,早上8点 0 23-7/2,8 * * * date #每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点 0 11 4 * mon-wed date #1月份日早上4点 0 4 1 jan * date
补充:在使用crontab的时候,要特别注意的是运行脚本中能够访问到的环境变量和当前测试环境中的环境变量未必一致,一个比较保险的做法是在运行的脚本程序中自行设置环境变量(export)
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。
一、定义配置文件
其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:
1.配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。 appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。
2.配置日志信息输出目的地Appender,其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class log4j.appender.appenderName.option1 = value1 … log4j.appender.appenderName.option = valueN
其中,Log4j提供的appender有以下几种: org.apache.log4j.ConsoleAppender(控制台), org.apache.log4j.FileAppender(文件), org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
3.配置日志信息的格式(布局),其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 … log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以下几种: org.apache.log4j.HTMLLayout(以HTML表格形式布局), org.apache.log4j.PatternLayout(可以灵活地指定布局模式), org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串), org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL %r 输出自应用启动到输出该log信息耗费的毫秒数 %c 输出所属的类目,通常就是所在类的全名 %t 输出产生该日志事件的线程名 %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n” %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
二、在代码中使用Log4j
1.得到记录器
使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:
public static Logger getLogger( String name)
通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:
static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )
2.读取配置文件
当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:
BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。 PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。 DOMConfigurator.configure ( String filename ) :读取XML形式的配置文件。
3.插入记录信息(格式化日志信息)
当上两个必要步骤执行完毕,您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:
Logger.debug ( Object message ) ; Logger.info ( Object message ) ; Logger.warn ( Object message ) ; Logger.error ( Object message ) ;
本文给出了一些指导性的意见和建议,用于帮助用户在产品开发中遵循自由软件的许可证条款,并避免出现违反自由软件许可证的情况发生。
1.如果您不接受 GPL/LGPL 许可证,请勿使用任何遵循该许可证条款发布的软件。如果您在自己的产品中使用了 GPL/LGPL 软件,则说明您已经接受了 GPL/LGPL 许可证中定义的所有条款,并有义务向产品的最终用户提供源代码——无论该源代码是否经过您的修改。如果经过了您自己的修改,则必须公开“衍生作品”的源代码,并以相同的许可证条款发布。
2.当您从 GPL/LGPL 软件中拿出 10 行以上的源代码用于自己的作品中时,则您的作品将成为该 GPL/LGPL 软件的衍生作品,无论您的作品的整体代码规模有多大。因此,如果您不打算将自己的作品作为自由软件发布,则应该远离自由软件代码,以免因为受到自由软件代码的影响而编写出和这些软件相类似的代码。
3.如果在您的作品中使用了 GPL/LGPL 软件,但没有对这些软件做任何修改,则可以在产品手册或者其他类似的文档中、程序界面上或者帮助信息中指明您使用的自由软件名称、版权拥有者以及能够获取该自由软件全部源代码的公共网站或第三方。如果因为某种原因,最终用户无法从您提到的第三方或者公共网站上获得该自由软件的源代码,您应该担负提供源代码的责任和义务。
4.GPL/LGPL 条款赋予您修改作品的权利,经修改之后的作品称为“衍生作品”。当您的衍生作品以某种方式发布时(典型情况就是用于您的产品中),您必须依照 GPL/LGPL 许可证发布您的衍生作品。当然,一种更加可取的办法是,将自己所做的修改提交给原始作品的维护者,并由该维护者负责发布,而您在产品中始终使用由维护者发布的作品。
5.自由软件不等于免费。提供自由软件的人可以要求您支付一定的费用,该费用通常有两层含义:第一,自由软件以某种介质发行时,该介质的制作、发布等费用;第二,当您希望获得对某自由软件的技术支持、缺陷修正等服务,要求某个人或组织提供相应的产品质量担保时,该组织或个人可以要求您就质量担保收取服务费用,甚至是专有软件产品惯用的使用许可费用。这里提到的组织或个人是任何遵循上述自由软件许可证条款发布自由软件、并向您提供质量担保的组织或个人,并不限于自由软件作品的作者或主要的版权拥有人。
6.对 LGPL 条款的自由软件(通常是函数库)的“正常使用”,通常的理解是,始终以动态链接的形式链接这个函数库——如果以静态的方式链接,将使该函数库成为您作品的一部分,从而使之成为该函数库的衍生作品。但实质上,LGPL 许可证的宗旨和精神是禁止将自由软件成为专用和独享的软件,而至少应该确保其他软件也能通过某种途径使用这个函数库的接口。当然,静态链接显然违背了上述精神和宗旨,从而是不允许将私有作品和 LGPL 函数库静态链接在一起。但如果您的产品没有提供任何扩展功能,而只能由您自己的私有作品使用其中包含的某 LGPL 函数库,这无异于将该函数库静态链接到您自己的私有作品中。因此,我们认为这种情况下,您的作品是该函数库的“衍生作品”——无论您的作品通过静态链接还是通过动态链接的方式链接该 LGPL 函数库。
上述这种情况经常会出现在嵌入式系统中。在这种情况下,您可以有如下选择:
* 以动态链接方式链接 LGPL 函数库,并为您的产品提供扩展接口及程序上载接口, 以便用户或者其他人能够对该产品进行扩展。
* 最简单的方式:将衍生作品置于 LGPL 条款下发布。
* 和 LGPL 条款的版权拥有人联系,看看是否能够以其他许可证方式授权您 在自己的产品中使用该函数库,而不必遵循 LGPL 条款使自己的作品成为 衍生作品。许多自由软件为商业用户提供另外一种可选的许可方式。
* 当然,如果您觉得麻烦,可以选择不使用任何自由软件。
令狐冲十四岁那年进入华山,那年岳琳珊八岁,岳不群白天给两人指点剑法,晚上令狐冲给小师妹讲故事哄她入睡。后来,岳不群陆续收了劳德诺,陆大有等徒弟,又忙于修炼紫霞神功,就没有时间指点徒弟。于是他做了一个HUB,从此华山派实现教育电子化,岳不群在网上同时给每个徒弟授课,这种方法很快在五岳剑派内部推广。为了在五岳剑派之间互连,嵩山派掌门左冷禅研制出路由器,使得五岳剑派之间可以互联互通。令狐冲晚上就通过网络给小师妹讲故事。 很快,岳琳珊已经十六岁,变成了一个亭亭玉立的小姑娘了。令狐冲发现自己的目光总是不由自主的在小师妹身上停留,每次和小师妹在一起的时候,总能听到自己强烈的心跳声,经过了一段时间的茶饭不思后,终于有一天晚上,令狐冲在网上给小师妹发了一首情意绵绵的诗:你是风儿我是沙,你是蜜蜂我是花,你是梳子我是头发,你是牙膏我是牙刷。 第二天,华山派开例会,令狐冲怀着忐忑不安的心情来到了会议室,发现小师妹红着脸躲在师父后面,而其它的师弟都在偷偷朝自己笑,开完会,一个调皮的师弟就过来叫牙刷师兄,赶紧蒙面逃走。问陆大有,才知道是劳德诺用一个叫NetXRay的工具把自己在网上的大作全抓了出来。令狐冲悔恨万分,于是,闭门研究RFC,成功的研制出LanSwitch。它能够识别设备MAC地址,这样,令狐冲发送给小师妹的数据只有她一个人能够收到。令狐冲晚上可以在网上放心的给小师妹讲故事,偶尔手痒还能敲几句平时心里想又说不出口的话来过瘾,然后,红着脸想象小师妹看到后的表情。 // LanSwitch是二层交换设备,它可以理解二层网络协议地址MAC地址。二层交换机在操作过程中不断的收集资料去建立它本身的地址表,这个表相当简单,主要标明某个MAC地址是在哪个端口上被发现的,所以当交换机接收到一个数据封包时,它会检查该封包的目的MAC地址,核对一下自己的地址表以决定从哪个端口发送出去。而不是象HUB那样,任何一个发方数据都会出现在HUB的所有端口上(不管是否为你所需)。这样,LanSwitch在提高效率的同时,也提高了系统的安全性。// 接下来的一年,岳不群大量招收门徒,华山派得以极大的壮大,所使用的LanSwitch也多次级连。但门徒中难免鱼龙混杂,当时华山派一批三、四代弟子崇拜万里独行田伯光,成立了一个田协,经常广播争论比赛八百米还是一千米很合理的问题;第三代弟子中有一个叫xxx的,每天在华山派内部广播xx大法;更让令狐冲受不了的是,随着师父年龄的增大,变得越来越罗嗦,每句话都要重复二十遍,然后在网上广播。令狐冲想和小师妹,陆大有等人专门使用一个广播域,但如果另外使用一个LanSwitch的话,师父肯定不会同意,于是,他修改了LanSwitch的软件,把小师妹,陆大有等人和自己划成一个虚拟网(VLAN),其它人使用另外的VLAN,广播包只在VLAN内发送,VLAN间通过路由器连接。岳不群也深受田协,xxx其害,但为与左冷禅抗争,用人之际,只能隐忍,知道了这件事,大为高兴,但仍为令狐冲私自修改软件一事,罚他到思过崖面壁一年,一年之内不得下山。 在华山派内重新使用VLAN进行子网划分,分为五个子网,师父和师娘,小师妹还有林平之在一个VLAN,xx功弟子用一个VLAN;田协弟子用一个VLAN,其它弟子用一个VLAN,而思过崖上也有单独的一个VLAN。令狐冲到了思过崖,并不难过,终于,世界安静了,依靠左冷禅的路由器,令狐冲还可以每天在网上给小师妹讲故事,聊天。 // 局域网交换机的引入,使得网络节点间可独享带宽,但是,对于二层广播报文,二层交换机会在各网络节点上进行广播;同时,对于二层交换机无法识别的MAC地址,也必须在广播域内进行广播。当多个二层交换机级连时,二层交换网络上的所有设备都会收到广播消息。在一个大型的二层广播域内,大量的广播使二层转发的效率大大减低,为了避免在大型交换机上进行的广播所引起的广播风暴,需要在一个二层交换网络内进一步划分为多个虚拟网(VLAN)。在一个虚拟网(VLAN)内,由一个工作站发出的信息只能发送到具有相同虚拟网号(VLANID)的其他站点,其它虚拟网(VLAN)的成员收不到这些信息或广播帧。采用虚拟网(VLAN)可以控制网络上的广播风暴和增加网络的安全性。不同虚拟网(VLAN)之间的通信必须通过路由器进行。// 但是幸福永远是短暂的,接下来总是无尽的烦恼。随着整个五岳剑派势力的增大,路由器的速度越来越慢。令狐冲发现每次给小师妹讲故事时,小师妹的回答总是姗姗来迟,而且话也很少,总是"嗯","噢"或者"我听着呢"。终于有一天,路由器再也PING不通的,令狐冲三天没有得到小师妹的消息,对着空空的显示屏,再也忍不住,在一个下着雪的晚上,偷偷下山找小师妹,到了小师妹窗前,发现小师妹正在网上和小林子热烈的聊天,全没注意一边的自己,内心一阵酸痛,回到思过崖,大病一场。病好后潜心研究,终于有一天,做出来一个路由器,这时,令狐冲发现,此时华山派已经有了三十个VLAN,路由器必须为每个VLAN分配一个接口,接口不够用,而且,两个子网内通过路由器的交换速度远远低于二层交换的速度。 // 二层交换机划分虚拟子网后,就出现了一个问题:不同虚拟子网之间的转发需要通过其它路由器来实现。二层交换机的不同VLAN节点间的转发需要通过路由器设备来实现大大浪费了端口,而路由器的高成本,低效率又使它无法满足大量子网情况下的三层转发需求,三层交换的概念就在这种情况下被提了出来。// 这天晚上,令狐冲心灰意懒,借酒消愁,这时,一个黑影出现在他的面前,原来是一个道风仙骨的老人,正是风清扬。风清扬听了令狐冲的疑惑,说:路由器接口不够,把路由器做在LanSwitch内部不就可以了;交换速度慢,是因为路由器查找的是网段路由,而LanSwitch直接查MAC对应出端口,当然速度快。为什么不能直接根据IP地址查到出端口呢?令狐冲一听,大为仰慕,但还是不明白,IP地址那么多,而且经常变化,如何能够直接查到出端口呢?风清扬说: "你先坐下,让我来问你,华山派有多少弟子?" "一万六千左右。" "你全知道他们住哪里吗?" "不知道。" "岳不群要你找一个不知道住哪里的人,如何去找?" "查华山派电话号码查询系统,找到他的地址,然后去找他。" "如果你回来后再让你找这个人,又如何去找?" "如何.... ,查华山派电话号码查询系统,找到他的地址,然后去找他。" "你不知道到这个人的地址吗?" "知道,但师父说,华山派的地址那么多,而且经常变化,不用知道地址。" "岳不群这小子,把徒弟都教成木头了!我问你,你自己认为应该如何找?" "直接去找!" "好!你这人还不算太苯。那你知道了一个人的地址后,是不是永远记住了?" "有的人记住了。其它的都忘了。" "为什么忘了?" "因为我记不了那么多人,而且一段时间没有去找他。" "华山派电话号码查询系统里的地址是如何获得的?" "我在空旷处大喊一声他的名字,他听到后就会来找我,告诉我他的地址。" 风清扬又问了大把类似脑筋急转弯的问题,然后风清扬说:"现在你明白根据IP地址直接查出端口的道理了吗?等到你明白这个道理,你自然会做出三层交换机来",令狐冲仔细回忆了今天的话,终于明白了和二层转发由MAC地址对应到出端口的道理一样,三层转发也可以直接由IP地址对应到出端口,IP地址的路由可以通过ARP来学习,同样需要老化。这样,VLAN间转发除第一个包需要通过ARP获得主机路由外,其它的报文直接根据IP地址就能够查找到出端口,转发速度远远高于路由器转发的速度。抬头看时,风清扬已经走了。 一年后,令狐冲下思过崖,成功的推出Quidway S8016路由交换机。实现了VLAN间的互通,并且与嵩山,黑木崖等路由器实现互通。 // 三层交换机是在二层交换机的基础上增加三层交换功能,但它不是简单的二层交换机加路由器,二而是采用了不同的转发机制。路由器的转发采用最长匹配的方式,实现复杂,通常使用软件来实现,。而三层交换机的路由查找是针对流的,它利用CACHE技术,很容易采用ASIC实现,因此,可以大大的节约成本,并实现快速转发。 很多文章会提及三层交换机和路由器的区别,一般的比较是三层交换机又快又便宜。这些话没有错,但场合是汇聚层。我们看到,在汇聚层,面向三层交换机直接下挂的主机,因为能够获得其主机路由,所以三层交换机能够实现快速查找;而对于通过其它路由器连接多个子网后到达的主机,三层交换机和路由器的处理是一样的,同样采用最长匹配的方法查找到下一跳,由下一跳路由器进行转发。 因此,通常的组网方式是在骨干层使用GSR,汇聚层使用三层交换机。当然,对于一个小型的城域网,也可以直接拿三层交换机组网,不需要GSR。//
|