2010年8月22日星期日

JAVA开发二三事(续一)

dom4j 写入 xml 文件汉字乱码的问题

1、代码:
XMLWriter xmlWriter = new XMLWriter(new java.io.FileWriter(path));

2、问题:
在集成开发环境下启动的程序,写入文件,汉字确实是utf-8,不乱码;但是在console控制台下启动的独立运行,汉字就是乱码了。

3、解决的方法:
代码改成:
XMLWriter xmlWriter = new XMLWriter(new java.io.FileOutputStream());
问题解决。

4、原因:
java.io.FileWriter 没有实现 ENCODING 相关的接口(该说法来源于网络),所以在集成开发环境都是utf-8的情况下,可以输出utf-8,但是在console控制台下,是GBK环境,写入的就不是utf-8了。即使设置了也不起作用。
java.io.FileOutputStream 是二进制的流,实现了相关接口,所以没有问题。

2010年8月15日星期日

JAVA开发二三事

最近做了一个简单的JAVA图形界面程序,给一个同事的项目用。网上搜索解决了不少问题,现在总结一下,做个存档。

一、JAVA Sybase数据库连接:
1、JDBC连接字符串:
String url = "jdbc:sybase:Tds:" + machine + ":" + port + "/" + dbname + "?charset=cp936";
properties = new Properties();
properties.put("user", userID);
properties.put("password", password);
其中:machine是数据库服务器的IP地址。不能是机器名、不能是Localhost、不能是127.0.0.1。我的操作系统是Win7,除了静态IP地址,其他的都连接不上,这个折腾的时间最多。还有端口port是5000,不是文档上说的那个43XX。dbname是数据库的名字。加上 "?charset=cp936"参数是指明了字符集,为了返回结果中的中文正常显示,否是是乱码。默认的userID是sa,password是空字符串。

2、打开连接:
Class.forName("com.sybase.jdbc3.jdbc.SybDriver").newInstance();
connection1 = DriverManager.getConnection(url, properties);

3、数据库读取操作以及结果集循环:
Statement selectStatement;
ResultSet resultSet;
String kmdm;
String mc;
try {
if (connection1 != null) {
selectStatement = connection1.createStatement();
resultSet = selectStatement.executeQuery("SELECT KMDM, "
+ " MC "
+ " FROM KM" + cyear
+ " WHERE 1=1 ");
while (resultSet.next()) {
kmdm = resultSet.getString("kmdm");
mc = resultSet.getString("mc");
jTextArea1.append(kmdm + " - '" + mc + "'\n");
} catch (Exception exception) {
jTextArea1.append("Error: " + exception.getMessage());
}

二、JAVA Sybase Spring JdbcTemplate数据库连接:
1、连接字符串
String url = "jdbc:sybase:Tds:" + machine + ":" + port + "/" + dbname + "?charset=cp936";
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.sybase.jdbc3.jdbc.SybDriver");
dataSource.setUrl(url);
dataSource.setUsername(userID);
dataSource.setPassword(password);
其中:dataSource.setUsername和dataSource.setPassword不能省略。其实连接字符串中的url里面也能写上用户名和密码,但是会报错,一定要写。
2、查询一:
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
String qry;
qry = "select count(MAX(SXH)) from BAK_PZ" + cyear + " GROUP BY YUE,PZLB,PZBH";
pzcount = jt.queryForInt(qry);
其中:queryForInt能直接得到整数型的返回值;
3、查询二:
qry = "SELECT A.* FROM BAK_PZ" + cyear + " A JOIN "
+ " (SELECT MAX(SXH) AS SXH,YUE,PZLB,PZBH FROM BAK_PZ" + cyear
+ " GROUP BY YUE,PZLB,PZBH) B "
+ " ON A.SXH =B.SXH AND A.YUE=B.YUE AND A.PZLB=B.PZLB AND A.PZBH=B.PZBH "
+ " ORDER BY A.YUE,A.PZLB,A.PZBH ";

List rows = jt.queryForList(qry);

Iterator it = rows.iterator();
String shrNull;
int tmpi = 0;
while (it.hasNext()) {
Map userMap = (Map) it.next();
shrNull = userMap.get("SHR") == null ? "" : userMap.get("SHR").toString().trim();
其中:queryForList 能直接把返回结果集组装成一个list,每个list里面是一个个的map,存放的是字段和对应的值,遍历、使用都非常方便。唯一的缺点是字段的值被装箱成Object了,一是要自己类型转换;二是内存消耗很大。方便飞副作用就是内存占用上升。

三、dom4j的使用(XML的生成、写入文件)
1、创建xml跟节点:
Document doc = null;
Element root = null;
doc = DocumentHelper.createDocument();
root = DocumentHelper.createElement("ufinterface");
doc.setRootElement(root);

2、给某个节点添加属性:
root.addAttribute("roottag", "voucher");

3、增加子节点
Element voucherElem = root.addElement("voucher");
voucherElem.addAttribute("id");
或者直接连续增加:
voucher_head.addElement("fiscal_year").addText(cyear);

4、DOM XML写入文件:
OutputFormat outFmt = OutputFormat.createPrettyPrint();
outFmt.setEncoding("UTF-8");
outFmt.setIndent(" ");//设置缩进
outFmt.setNewlines(true);//让xml换行,方便查看
XMLWriter xmlWriter = new XMLWriter(new java.io.FileWriter("c:\\凭证_" + dbname +pk_corp + "公司" + cyear + "年.xml"), outFmt);
xmlWriter.write(doc);
xmlWriter.close();
其中:Encoding设置为UTF-8能保证不乱码。缩进和换行是为了后续调试xml方便。为了减少体积可以不加。

四、用Netbeans来写GUI 图形界面程序太爽了,简直就是VB、C#图形程序的开发方式,拖拉控件、设置布局、写事件响应代码、挂接。很高效。当然,这个是调用的Netbeans自带的Swing开发框架。您老要是觉得不地道,自己整Swing或者AWT都行。Eclipse里面也可以自己去弄响应的框架。

五、JAVA 程序的打包发行。偷懒的也是最简单的做法,把编译生成的jar和自己加工过的jdk一起压缩发给客户。就40MB。然后写个简单的批处理文件,如下调用:
.\jdk1.6.0_20L\jre\bin\java -jar .\DesktopApplication1\dist\DesktopApplication1.jar
看到了吗,我直接把整个Netbeans项目文件夹都给客户了。顶多删除源码。jdk就是安装之后的1.6.0.20版,自己删除了里面带的源码、例子等等不需要的做了精简。

六、想到再说。吃饭了。

2010年4月25日星期日

Java 读写 Word doc 文件的方法(POI代码示例)

都说懒人推动社会进步。
我的出发点是,想做个自动生成工作日志的工具。思路是读取google日历中我每日的工作记录,填充生成公司规定格式的word文件,然后再调用邮件函数发送邮件到领导的信箱里面去。

这其中一环就是Java读写doc文件。网上找了不少资料,初步选用了Apache POI,可以读写整个Office系列的各种文档,并且不依赖Office的任何库,纯JAVA代码。直接读写doc的二进制文件,我最喜欢了。相对的有个Java com 桥的方法,用Java来操作com对象,后台运行隐藏的word程序,甚为不喜。

POI API Documentation 的文档写的太差了,没有例子,Apache 的 POI 项目主页也没有代码,直接说想看代码,请去svn下载代码,阅读他的单元测试用例,TestCase。
最后磕磕绊绊写了下面的一个测试的例子。基本可以看清这个POI的hwpf库,读写word doc 文件的方法。代码、注释都是原创,高手飘过去就行了。

import java.io.*;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.hwpf.usermodel.Section;

public class Main {

public static void main(String[] args) {
try {
//新建 HWPFDocument 对象,读入doc文件
HWPFDocument doc = new HWPFDocument(new FileInputStream("c:\\test.doc"));
//得到整个doc文档的Range,可以理解为文档对象
Range r = doc.getRange();

System.out.println("Example you supplied:");
System.out.println("---------------------");

String text = new String("");
//得到整个文档里面的所有纯文字,包含回车换行。一段是一行
text = r.text();
//System.out.println(text);

//得到整个文档的分节数。一般只有一节,排版很漂亮的word文档一般分为多节
System.out.println("numSections: " + r.numSections());
//得到倒数第一节的Section对象
Section section = r.getSection(r.numSections() - 1);
//得到该节里面的段落数
System.out.println(section.numParagraphs());
System.out.println("numParagraphs: " + section.numParagraphs());

String searchText = "${Ryan}";
String replacementText = "Apache Software Foundation";

//循环得到每一段落的文字。这个跟Range.text()是不同的。
for (int np = 0; np < section.numParagraphs(); np++) {
Paragraph para = section.getParagraph(np);
//得到该段落的文字
text = para.text();
//System.out.println(Integer.toString(np) + ":" + text);
int offset = text.indexOf(searchText);
if (offset >= 0) {
System.out.println(Integer.toString(np) + ":" + para.text());
//如果找到了,就进行文字的替换。replaceText只能针对段落
para.replaceText(searchText, replacementText);
break;
}
}
//写入到新的doc文件
OutputStream outdoc = new FileOutputStream("c:\\test2.doc");
doc.write(outdoc);
outdoc.flush();
outdoc.close();

} catch (Throwable t) {
t.printStackTrace();
}
}
}


编译运行很顺利,但是悲剧的是,最后调用write方法写入的doc文件打不开了,报格式错误,用Notepad++打开这个doc文件对比原来的,发现文字确实替换成功了,但是文件的尾巴上少了一部分内容,二进制和assic混合的,格式看来是被破坏了。

忙活了半天,最后发现杯具了。应该是兼容性的问题。改天去他们的邮件列表问问。或者路过的看官知道的,指导一下我。

我在写这个文章的时候,又想到一个绝妙的注意。何必绕道这么远呢?
实施工作日志内容来源于google calendar,如果能够利用google app doc 自己本身的宏:google script,的功能,读取日历数据,形成doc文件,调用gmail发送到指定的信箱,不是齐活了吗?费不着用Java了。

其实在决定用java之前,想过用VB,这个做肯定不存在问题,VB操作com对象本身是轻车熟路,顶多再找个发邮件的库,实在不行了没人手工发送。方法还是很多的。

科技以人为本。

2010年3月20日星期六

在 Ubuntu 下安装 SUN JDK 6 和 Eclipse

Ubuntu下安装JDK 6和Eclipse

开发笔记:在Ubuntu下安装JDK 6和Eclipse 
首先需要安装JDK 6 
sudo apt-get install sun-java6-jdk 

之后需要设置默认的java程序 
sudo update-alternatives --config java 
按照提示输入对应的选项,指定为JDK 6 

下面设置 
sudo vim /etc/environment 

在其中添加如下两行: 
CLASSPATH=/usr/lib/jvm/java-6-sun/lib 
JAVA_HOME=/usr/lib/jvm/java-6-sun 
如果其中已经设置了CLASSPATH和JAVA_HOME,则将其修改为上面的形式,保存退出。 

接下来安装Eclipse 
sudo apt-get install eclipse 

需要注意的是,此时Eclipse并不关心之前设置的update-alternative 

所以应该修改Eclipse的配置。首先将 SUN-JDK-6彻底设为系统默认: 

sudo update-java-alternatives -s java-6-sun 

然后编辑JVM配置文件: 
sudo vim /etc/jvm 

在文件顶部添加 
/usr/lib/jvm/java-6-sun 
(如果没有则自己新建) 

sudo vim /etc/eclipse/java_home 

也是在文件顶部添加 
/usr/lib/jvm/java-6-sun 

之后启动Eclipse,选择Help->About Eclipse SDK,选择Configuration Details,可以看到JDK6的设置已经生效。 

ubuntu下设置华文黑体

如何安装Mac的华文黑体来美化汉字的显示:
(1)先到这里sthei.ttf.gz去下载sthei.ttf字体,解压;
(2)把它copy到"/usr/share/fonts/custom/cn"目录下;fonts下没有custom/cn目录就先建一个
(3)减少中文字体过度渲染:sudo fontconfig-voodoo -f -s zh_CN
(4)修改字体配置文件,加入sthei字体:sudo gedit /etc/fonts/language-selector.conf;在<family>Bitstream Vera Sans</family>行下面添加:<family>STHeiti</family>保存后退出
(5)强制刷新字体缓存:sudo fc-cache -f -v
(6)系统 -首选项 � 字体:设置所有非等宽字体为 Bitstream Vera Sans 10号,其中窗口标题字体设置为粗体。等宽字体设置为Courier 10 Pitch,在AA时,Courier 10 Pitch比Courier New字体更清楚。
(7)顶顶重要的:在"字体渲染"中,选择"细节","平滑"设置为"灰度","微调"设置为"无"。
(8)打开firefox,设置firefox字体,无衬线字体:Bitstream Vera Sans;等宽字体:Courier 10 Pitch 大小:13;其它的可以不改。
(9)退出当前登录,重新登录。一切OK了。

以上来自我2006年在donews的博客。现在网站已经打不开来。好在还有google cache。
最关键的是box.net的文件共享依然还在,每当有人下载了我共享的华文黑体,它都会发一封邮件给我。
作为一个美国网站,免费到为一个中国人提供来长达5年的免费文件寄存服务,这是怎样的一种精神病啊!
类似这样到外国公司,你们要把他们赶出中国去,你们连精神病都不如。