Every Day's Gain

Larry.Zhao's Coding Diary
随笔 - 23, 文章 - 1, 评论 - 2, 引用 - 0
数据加载中……

2006年3月1日

Linux笔记--Process Control

  • fork()函数完全复制本身进程的状态,并且给父进程返回pid,给子进程返回0,所以在代码中应该保存pid = fork(); 并在if(pid == 0){}中控制子进程。由于是完全复制本身进程,所以在父进程调用fork()前对父进程变量的改动都会影响子进程中变量的值,而子进程中的操作则不会影响父进程中变量的值。

posted @ 2006-03-01 23:26 Larry.Zhao's Coding Diary 阅读(287) | 评论 (0)编辑 收藏

Linux笔记--File IO

  • lseek大于文件现有长度,不过不进行写操作的话,不会增加文件长度。
  • 用O_APPEND打开文件,可以再用lseek来调整文件指针。

posted @ 2006-03-01 23:17 Larry.Zhao's Coding Diary 阅读(319) | 评论 (0)编辑 收藏

2006年2月2日

【转贴】创建带图标和缩进的JComboBox

默认的JComboBox无法在每个条目上显示图标、缩进等样式。但是Swing的MVC设计结构为各种组件提供了无与伦比的可扩展性。为了实现这一点,我们可以创建一个新的Renderer来负责每个条目的绘制。

首先我们新写一个类ImagedComboBoxItem,它封装了一个下拉条目的信息,包括图标、文字、缩进等:

class ImagedComboBoxItem {
    private Icon icon = null;
    private String text = null;
    private int indent = 0;

    ImagedComboBoxItem(String text, Icon icon, int indent) {
        this.text = text;
        this.icon = icon;
        this.indent = indent;
    }

    public String getText() {
        return text;
    }

    public Icon getIcon() {
        return icon;
    }

    public int getIndent() {
        return indent;
    }
}

然后新建JImagedComboBox类并从JComboBox继承。在构造函数中,新建一个DefaultListCellRenderer作为新的Renderer,并覆盖其getListCellRendererComponent方法。在新的getListCellRendererComponent方法中,首先依旧调用父对象的该方法,以便完成普通条目的绘制;然后判断条目是否是ImagedComboBoxItem实例。如果是,则显示ImagedComboBoxItem的文字、图标,并显示缩进。为了更方便的显示左侧缩进,我们直接创建一个EmptyBorder并设置左侧缩进数量,并设置到DefaultListCellRenderer中。DefaultListCellRenderer从JLabel继承而来,所以可直接接受各种Border。这里我们以每个缩进10象素为例。

好了,以下是完整代码:

import java.util.*;

import java.awt.*;
import javax.swing.*;

public class JImagedComboBox extends JComboBox {
    public JImagedComboBox(Vector values) {
        super(values);
        ListCellRenderer renderer = new DefaultListCellRenderer() {
            public Component getListCellRendererComponent(
                JList list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof ImagedComboBoxItem) {
                    ImagedComboBoxItem item = (ImagedComboBoxItem) value;
                    this.setText(item.getText());
                    this.setIcon(item.getIcon());
                    if (isPopupVisible()) {
                        int offset = 10 * item.getIndent();
                        this.setBorder(BorderFactory.createEmptyBorder(0, offset, 0, 0));
                    }
                }
                return this;
            }
        };
        this.setRenderer(renderer);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(400, 400);
        Vector values = new Vector();
        Icon openIcon = new ImageIcon(JImagedComboBox.class.getResource("Open16.gif"));
        Icon newIcon = new ImageIcon(JImagedComboBox.class.getResource("New16.gif"));
        for (int i = 0; i < 5; i++) {
            values.addElement(new ImagedComboBoxItem("Directory " + i, openIcon, i));
        }
        for (int i = 0; i < 5; i++) {
            values.addElement(new ImagedComboBoxItem("Image Item " + i, newIcon, 5));
        }
        JImagedComboBox comboBox = new JImagedComboBox(values);
        frame.getContentPane().add(comboBox, BorderLayout.NORTH);
        frame.show();
    }
}

class ImagedComboBoxItem {
    private Icon icon = null;
    private String text = null;
    private int indent = 0;

    ImagedComboBoxItem(String text, Icon icon, int indent) {
        this.text = text;
        this.icon = icon;
        this.indent = indent;
    }

    public String getText() {
        return text;
    }

    public Icon getIcon() {
        return icon;
    }

    public int getIndent() {
        return indent;
    }
}

作者: solo, 原文地址:http://blog.csdn.net/solo/archive/2004/09/10/100380.aspx

posted @ 2006-02-02 00:13 Larry.Zhao's Coding Diary 阅读(292) | 评论 (0)编辑 收藏

2006年1月30日

【转贴】Java中日期时间的格式化

Java 语言的Calendar,GregorianCalendar (日历),Date(日期), 和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分. 日期是商业逻辑计算一个关键的部分. 所有的开发者都应该能够计算未来的日期, 定制日期的显示格式, 并将文本数据解析成日期对象。学习日期, 日期格式, 日期的解析和日期的计算。 

我们将讨论下面的类: 

1、  具体类(和抽象类相对)java.util.Date 

2、  抽象类java.text.DateFormat 和它的一个具体子类,java.text.SimpleDateFormat 

3、  抽象类java.util.Calendar 和它的一个具体子类,java.util.GregorianCalendar 

具体类可以被实例化, 但是抽象类却不能. 你首先必须实现抽象类的一个具体子类.

1.   java.util.Date及其格式化
Date 类从Java 开发包(JDK) 1.0 就开始进化, 当时它只包含了几个取得或者设置一个日期数据的各个部分的方法, 比如说月, 日, 和年. 这些方法现在遭到了批评并且已经被转移到了Calendar类里去了, 我们将在本文中进一步讨论它. 这种改进旨在更好的处理日期数据的国际化格式. 就象在JDK 1.1中一样, Date 类实际上只是一个包裹类, 它包含的是一个长整型数据, 表示的是从GMT(格林尼治标准时间)1970年, 1 月 1日00:00:00这一刻之前或者是之后经历的毫秒数. 

1.1. 创建java.util.Date
Java统计从1970年1月1日起的毫秒的数量表示日期。也就是说,例如,1970年1月2日,是在1月1日后的86,400,000毫秒。同样的,1969年12月31日是在1970年1月1日前86,400,000毫秒。Java的Date类使用long类型纪录这些毫秒值.因为long是有符号整数,所以日期可以在1970年1月1日之前,也可以在这之后。Long类型表示的最大正值和最大负值可以轻松的表示290,000,000年的时间,这适合大多数人的时间要求。

让我们看一个使用系统的当前日期和时间创建一个日期对象并返回一个长整数的简单例子. 这个时间通常被称为Java 虚拟机(JVM)主机环境的系统时间. 
import java.util.Date; 

public class DateExample1 { 

public static void main(String[] args) { 

// Get the system date/time 

Date date = new Date(); 

// 打印出具体的年,月,日,小时,分钟,秒钟以及时区

System.out.println(date.getTime()); 

}   



在星期六, 2001年9月29日, 下午大约是6:50的样子, 上面的例子在系统输出设备上显示的结果是 1001803809710. 在这个例子中,值得注意的是我们使用了Date 构造函数创建一个日期对象, 这个构造函数没有接受任何参数. 而这个构造函数在内部使用了System.currentTimeMillis() 方法来从系统获取日期. 

//1年前日期

   java.util.Date myDate=new java.util.Date();  

   long myTime=(myDate.getTime()/1000)-60*60*24*365;

   myDate.setTime(myTime*1000);

   String mDate=formatter.format(myDate);

//明天日期

   myDate=new java.util.Date(); 

   myTime=(myDate.getTime()/1000)+60*60*24;

   myDate.setTime(myTime*1000);

   mDate=formatter.format(myDate);

//两个时间之间的天数

   SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");

   java.util.Date date= myFormatter.parse("2003-05-1"); 

   java.util.Date mydate= myFormatter.parse("1899-12-30");

   long  day=(date.getTime()-mydate.getTime())/(24*60*60*1000);

//加半小时

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

java.util.Date date1 = format.parse("2002-02-28 23:16:00");

long Time=(date1.getTime()/1000)+60*30;

date1.setTime(Time*1000);

String mydate1=formatter.format(date1);

//年月周求日期

SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM F E");

java.util.Date date2= formatter2.parse("2003-05 5 星期五"); 

SimpleDateFormat formatter3 = new SimpleDateFormat("yyyy-MM-dd");

String mydate2=formatter3.format(date2);

//求是星期几

mydate= myFormatter.parse("2001-1-1");

SimpleDateFormat formatter4 = new SimpleDateFormat("E");

String mydate3=formatter4.format(mydate);

 
 

 

1.2. Date格式化
能以一种用户明白的格式来显示这个日期呢? 在这里类java.text.SimpleDateFormat 和它的抽象基类 java.text.DateFormat。那么, 现在我们已经知道了如何获取从1970年1月1日开始经历的毫秒数了. 我们如何才format 就派得上用场了. 

// 我们能不能用下面的代码构件出 2001/8/8 8:8
    import java.io.*;
    import java.util.*;
 
    public class WhatIsDate
    {
        public static void main(String[] args) {
            Date date = new Date(2001, 8, 8, 8, 8, 8);
            System.out.println(date);
        }
    }
 

Java 的编译器竟然报如下信息 (Sun JDK1.3, Windows 2000 中文下)

注意:
WhatIsDate.java 使用或覆盖一个不鼓励使用的API。
注意:
使用-deprecation重新编译,以得到详细信息。!
 

那么 Date 对象究竟是为了满足哪个需求呢?看来它不是用来实现基于年/月/日小时:分钟 的时间表述。我们查看 Java 的文档,我们看到有 getTime() 方法,它返回的竟然是一个 long 值。

文档进一步又告诉我们这个值代表了当前系统的时间离1970/1/1 0:0 的毫秒差,而且是在 GMT 时区下(也被称为 EPOC)。如果我们指定的时间是在此之前的,那它将返回一个负数值。

这个发现让我们对 Date 对象有了一个全新的认识-Date 存放的是与 EPOC 的偏差值。换而言之我们也可通过 long 类型来表示时间?对了,这个猜想是得到了 Java 的支持:

   // 第二种获得当前时间的方法
    long dateInMilliSeconds = System.currentTimeMillis();
    // 这时候打印出的只是一串数字而已
    System.out.println(dateInMilliSeconds);
 

对程序执行效率敏感的程序员可以发现这个方法只是生成一个 Java 的原始类型 (primitive type) long, 不需要实例化一个对象。因此如果我们对时间的处理只是在内部进行时,可以用 long 来代替 Date 对象。

最典型的应用就是在一段代码开始和结束时,分别获得系统当前的时间,然后计算出代码执行所需的时间(微秒级)。

   long start = System.currentTimeMillis();
    // 代码段
    System.out.println("需要 "+(System.currentTimeMillis()-start)+" 微秒");
 

那么当我们要把这个 long 值已更为友好的表现形式显示处理的时候,我们可以用它来构造 Date 对象:

Date date = new Date(dateInMilliSeconds);

System.out.println(date);
 

我们看到了在 Java 中对时间最为基本的表示,有通过对EPOC 的偏差值进行处理。Date 对象是对它的一个对象的封装。我们同时也看到了,在现时世界中我们对时间的描述通常是通过"某年某月某日某时某分"来定义的。Date 的显示(实际上是 toString() 方法)描述了这些信息,但 Java 并不建议我们用这种方式直接来构件 Date 对象。因此我们需要找出哪个对象可以实现这个需求。这就是我们下面就要讲述的 Calendar 对象的功能。

在我们进一步研究 Calendar 之前,请记住 Date 只是一个对 long 值(基于 GMT 时区)的对象封装。它所表现出来的年/月/日小时:分钟时区的时间表述,只是它的 toString() 方法所提供的。千万不要为这个假象所迷惑。

假如我们希望定制日期数据的格式, 比方星期六-9月-29日-2001年. 下面的例子展示了如何完成这个工作: 

import java.text.SimpleDateFormat; 

import java.util.Date; 

public class DateExample2 { 

public static void main(String[] args) { 

SimpleDateFormat bartDateFormat = new SimpleDateFormat("EEEE-MMMM-dd-yyyy"); Date date = new Date(); 

System.out.println(bartDateFormat.format(date)); 

}

}
 

只要通过向SimpleDateFormat 的构造函数传递格式字符串"EEE-MMMM-dd-yyyy", 我们就能够指明自己想要的格式. 你应该可以看见, 格式字符串中的ASCII 字符告诉格式化函数下面显示日期数据的哪一个部分. EEEE是星期, MMMM是月, dd是日, yyyy是年. 字符的个数决定了日期是如何格式化的.传递"EE-MM-dd-yy"会显示 Sat-09-29-01. 请察看Sun 公司的Web 站点获取日期格式化选项的完整的指示. 

1.3. 文本数据解析成日期对象 
假设我们有一个文本字符串包含了一个格式化了的日期对象, 而我们希望解析这个字符串并从文本日期数据创建一个日期对象. 我们将再次以格式化字符串"MM-dd-yyyy" 调用SimpleDateFormat类, 但是这一次, 我们使用格式化解析而不是生成一个文本日期数据. 我们的例子, 显示在下面, 将解析文本字符串"9-29-2001"并创建一个值为001736000000 的日期对象. 

通过parse()方法,DateFormat能够以一个字符串创立一个Date对象。这个方法能抛出ParseException异常,所以你必须使用适当的异常处理技术。

例子程序: 

import java.text.SimpleDateFormat; 

import java.util.Date; 

public class DateExample3 { 

public static void main(String[] args) { 

// Create a date formatter that can parse dates of 

// the form MM-dd-yyyy. 

SimpleDateFormat bartDateFormat = new SimpleDateFormat("MM-dd-yyyy"); 

// Create a string containing a text date to be parsed. 

String dateStringToParse = "9-29-2001"; 

try { 

// Parse the text version of the date. 

// We have to perform the parse method in a 

// try-catch construct in case dateStringToParse 

// does not contain a date in the format we are expecting. 

Date date = bartDateFormat.parse(dateStringToParse); 

// Now send the parsed date as a long value 

// to the system output. 

System.out.println(date.getTime()); 

}catch (Exception ex) { 

System.out.println(ex.getMessage()); 

}




 

1.4. 使用标准的日期格式化过程 
既然我们已经可以生成和解析定制的日期格式了, 让我们来看一看如何使用内建的格式化过程. 方法 DateFormat.getDateTimeInstance() 让我们得以用几种不同的方法获得标准的日期格式化过程. 在下面的例子中, 我们获取了四个内建的日期格式化过程. 它们包括一个短的, 中等的, 长的, 和完整的日期格式. 

import java.text.DateFormat; 

import java.util.Date; 

public class DateExample4 { 

public static void main(String[] args) { 

Date date = new Date(); 

DateFormat shortDateFormat = DateFormat.getDateTimeInstance( 

DateFormat.SHORT, DateFormat.SHORT); 

DateFormat mediumDateFormat = DateFormat.getDateTimeInstance( 

DateFormat.MEDIUM, DateFormat.MEDIUM); 

DateFormat longDateFormat = DateFormat.getDateTimeInstance( 

DateFormat.LONG, DateFormat.LONG); 

DateFormat fullDateFormat = DateFormat.getDateTimeInstance( 

DateFormat.FULL, DateFormat.FULL); 

System.out.println(shortDateFormat.format(date)); System.out.println(mediumDateFormat.format(date)); System.out.println(longDateFormat.format(date)); System.out.println(fullDateFormat.format(date)); 





注意我们在对 getDateTimeInstance的每次调用中都传递了两个值. 第一个参数是日期风格, 而第二个参数是时间风格. 它们都是基本数据类型int(整型). 考虑到可读性, 我们使用了DateFormat 类提供的常量: SHORT, MEDIUM, LONG, 和 FULL. 要知道获取时间和日期格式化过程的更多的方法和选项, 请看Sun 公司Web 站点上的解释. 

运行我们的例子程序的时候, 它将向标准输出设备输出下面的内容: 
9/29/01 8:44 PM 
Sep 29, 2001 8:44:45 PM 
September 29, 2001 8:44:45 PM EDT 
Saturday, September 29, 2001 8:44:45 PM EDT 

2.   Calendar 日历类 
首先请记住 Calendar 只是一个抽象类, 也就是说你无法直接获得它的一个实例,换而言之你可以提供一个自己开发的 Calendar 对象。

那究竟什么是一个 Calendar 呢?中文的翻译就是日历,那我们立刻可以想到我们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢?

比如有:

月份的定义 - 阳`(公)历 一年12 个月,每个月的天数各不同;阴(农)历,每个月固定28天,每周的第一天 - 阳(公)历星期日是第一天;阴(农)历,星期一是第一天

实际上,在历史上有着许多种纪元的方法。它们的差异实在太大了,比如说一个人的生日是"八月八日" 那么一种可能是阳(公)历的八月八日,但也可以是阴(农)历的日期。所以为了计时的统一,必需指定一个日历的选择。那现在最为普及和通用的日历就是 "Gregorian Calendar"。也就是我们在讲述年份时常用 "公元几几年"。Calendar 抽象类定义了足够的方法,让我们能够表述日历的规则。Java 本身提供了对 "Gregorian Calendar" 规则的实现。我们从 Calendar.getInstance() 中所获得的实例就是一个 "GreogrianCalendar" 对象(与您通过 new GregorianCalendar() 获得的结果一致)。

下面的代码可以证明这一点:

   import java.io.*;
    import java.util.*;
 
    public class WhatIsCalendar
    {
        public static void main(String[] args) {
            Calendar calendar = Calendar.getInstance();
            if (calendar instanceof GregorianCalendar)
                System.out.println("It is an instance of GregorianCalendar");
        }
    }
 

 

Calendar 在 Java 中是一个抽象类(Abstract Class),GregorianCalendar 是它的一个具体实现。

Calendar 与 Date 的转换非常简单:

   Calendar calendar = Calendar.getInstance();
    // 从一个 Calendar 对象中获取 Date 对象
    Date date = calendar.getTime();
    // 将 Date 对象反应到一个 Calendar 对象中,
    // Calendar/GregorianCalendar 没有构造函数可以接受 Date 对象
    // 所以我们必需先获得一个实例,然后设置 Date 对象
    calendar.setTime(date);
 
 

 

Calendar 对象在使用时,有一些值得注意的事项:

1. Calendar 的 set() 方法

set(int field, int value) - 是用来设置"年/月/日/小时/分钟/秒/微秒"等值

field 的定义在 Calendar 中

set(int year, int month, int day, int hour, int minute, int second) 但没有set(int year, int month, int day, int hour, int minute, int second, int millisecond) 前面 set(int,int,int,int,int,int) 方法不会自动将 MilliSecond 清为 0。

另外,月份的起始值为0而不是1,所以要设置八月时,我们用7而不是8。

calendar.set(Calendar.MONTH, 7);

我们通常需要在程序逻辑中将它清为 0,否则可能会出现下面的情况:

   import java.io.*;
    import java.util.*;
 
    public class WhatIsCalendarWrite
    {
        public static void main(String[] args) throws Exception{
            ObjectOutputStream out =
                new ObjectOutputStream(
                    new FileOutputStream("calendar.out"));
            Calendar cal1 = Calendar.getInstance();
            cal1.set(2000, 7, 1, 0, 0, 0);
            out.writeObject(cal1);
            Calendar cal2 = Calendar.getInstance();
            cal2.set(2000, 7, 1, 0, 0, 0);
            cal2.set(Calendar.MILLISECOND, 0);
            out.writeObject(cal2);
            out.close();
        }
    }
 

我们将 Calendar 保存到文件中

   import java.io.*;
    import java.util.*;
 
    public class WhatIsCalendarRead
    {
        public static void main(String[] args) throws Exception{
            ObjectInputStream in =
                new ObjectInputStream(
                    new FileInputStream("calendar.out"));
            Calendar cal2 = (Calendar)in.readObject();
            Calendar cal1 = Calendar.getInstance();
            cal1.set(2000, 7, 1, 0, 0, 0);
            if (cal1.equals(cal2))
                System.out.println("Equals");
            else
                System.out.println("NotEqual");
            System.out.println("Old calendar "+cal2.getTime().getTime());
            System.out.println("New calendar "+cal1.getTime().getTime());
            cal1.set(Calendar.MILLISECOND, 0);
            cal2 = (Calendar)in.readObject();
            if (cal1.equals(cal2))
                System.out.println("Equals");
            else
                System.out.println("NotEqual");
            System.out.println("Processed Old calendar "+cal2.getTime().getTime());
            System.out.println("Processed New calendar "+cal1.getTime().getTime());
        }
    }
 

然后再另外一个程序中取回来(模拟对数据库的存储),但是执行的结果是:

NotEqual
Old calendar 965113200422 <------------ 最后三位的MilliSecond与当前时间有关
New calendar 965113200059 <-----------/
Equals
Processed Old calendar 965113200000
Processed New calendar 965113200000
 
 

另外我们要注意的一点是,Calendar 为了性能原因对 set() 方法采取延缓计算的方法。在 JavaDoc 中有下面的例子来说明这个问题:

Calendar cal1 = Calendar.getInstance();
    cal1.set(2000, 7, 31, 0, 0 , 0); //2000-8-31
    cal1.set(Calendar.MONTH, Calendar.SEPTEMBER); //应该是 2000-9-31,也就是 2000-10-1
    cal1.set(Calendar.DAY_OF_MONTH, 30); //如果 Calendar 转化到 2000-10-1,那么现在的结果就该是 2000-10-30
    System.out.println(cal1.getTime()); //输出的是2000-9-30,说明 Calendar 不是马上就刷新其内部的记录
 

在 Calendar 的方法中,get() 和 add() 会让 Calendar 立刻刷新。Set() 的这个特性会给我们的开发带来一些意想不到的结果。我们后面会看到这个问题。

2. Calendar 对象的容错性,Lenient 设置

我们知道特定的月份有不同的日期,当一个用户给出错误的日期时,Calendar 如何处理的呢?

   import java.io.*;
    import java.util.*;
 
    public class WhatIsCalendar
    {
        public static void main(String[] args) throws Exception{
            Calendar cal1 = Calendar.getInstance();
            cal1.set(2000, 1, 32, 0, 0, 0);
            System.out.println(cal1.getTime());
            cal1.setLenient(false);
            cal1.set(2000, 1, 32, 0, 0, 0);
            System.out.println(cal1.getTime());
        }
    }
 

它的执行结果是:

   Tue Feb 01 00:00:00 PST 2000
    Exception in thread "main" java.lang.IllegalArgumentException
        at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:1368)
        at java.util.Calendar.updateTime(Calendar.java:1508)
        at java.util.Calendar.getTimeInMillis(Calendar.java:890)
        at java.util.Calendar.getTime(Calendar.java:871)
        at WhatIsCalendar.main(WhatIsCalendar.java:12)
 

当我们设置该 Calendar 为 Lenient false 时,它会依据特定的月份检查出错误的赋值。

3. 不稳定的 Calendar

我们知道 Calendar 是可以被 serialize 的,但是我们要注意下面的问题

   import java.io.*;
    import java.util.*;
 
    public class UnstableCalendar implements Serializable
    {
 
        public static void main(String[] args) throws Exception{
            Calendar cal1 = Calendar.getInstance();
            cal1.set(2000, 7, 1, 0, 0 , 0);
            cal1.set(Calendar.MILLISECOND, 0);
            ObjectOutputStream out =
                new ObjectOutputStream(
                new FileOutputStream("newCalendar.out"));
            out.writeObject(cal1);
            out.close();
            ObjectInputStream in =
                new ObjectInputStream(
                new FileInputStream("newCalendar.out"));
            Calendar cal2 = (Calendar)in.readObject();
            cal2.set(Calendar.MILLISECOND, 0);
            System.out.println(cal2.getTime());
        }
    }
 

 

运行的结果竟然是: Thu Jan 01 00:00:00 PST 1970

它被复原到 EPOC 的起始点,我们称该 Calendar 是处于不稳定状态。这个问题的根本原因是 Java 在 serialize GregorianCalendar 时没有保存所有的信息,所以当它被恢复到内存中,又缺少足够的信息时,Calendar 会被恢复到 EPOCH 的起始值。Calendar 对象由两部分构成:字段和相对于 EPOC 的微秒时间差。字段信息是由微秒时间差计算出的,而 set() 方法不会强制 Calendar 重新计算字段。这样字段值就不对了。

下面的代码可以解决这个问题:

   import java.io.*;
    import java.util.*;
 
    public class StableCalendar implements Serializable
    {
        public static void main(String[] args) throws Exception{
            Calendar cal1 = Calendar.getInstance();
            cal1.set(2000, 7, 1, 0, 0 , 0);
            cal1.set(Calendar.MILLISECOND, 0);
            ObjectOutputSt

posted @ 2006-01-30 16:28 Larry.Zhao's Coding Diary 阅读(4388) | 评论 (0)编辑 收藏

2006年1月28日

【转贴】java抽取word,pdf的四种武器

chris
2003-07-01 19:04:00
542 次浏览

chris (chris@matrix.org.cn)
毕业于中国人民大学信息学院
2003 年 6 月

很多人用java进行文档操作时经常会遇到一个问题,就是如何获得word,excel,pdf等文档的内容?我研究了一下,在这里总结一下抽取word,pdf的几种方法。
1 .用jacob
其实jacob是一个bridage,连接java和com或者win32函数的一个中间件,jacob并不能直接抽取word,excel等文件,需要自己写dll哦,不过已经有为你写好的了,就是jacob的作者一并提供了。

jacob jar与dll文件下载: http://www.matrix.org.cn/down_view.asp?id=13

下载了jacob并放到指定的路径之后(dll放到path,jar文件放到classpath),就可以写你自己的抽取程序了,下面是一个简单的例子:

import java.io.File;
import com.jacob.com.*;
import com.jacob.activeX.*;
/**
* Title: pdf extraction
* Description: email:chris@matrix.org.cn
* Copyright: Matrix Copyright Coffee 2003
* Company: Matrix.org.cn
* @author chris
* @version 1.0,who use this example pls remain the declare
*/
public class FileExtracter{
public static void main(String[] args) {
ActiveXComponent component = new ActiveXComponent("Word.Application");
String inFile = "c:\\test.doc";
String tpFile = "c:\\temp.htm";
String otFile = "c:\\temp.xml";
boolean flag = false;
try {
component.setProperty("Visible", new Variant(false));
Object wordacc = component.getProperty("document.").toDispatch();
Object wordfile = Dispatch.invoke(wordacc,"Open", Dispatch.Method,
new Object[]{inFile,new Variant(false), new Variant(true)},
new int[1] ).toDispatch();
Dispatch.invoke(wordfile,"SaveAs", Dispatch.Method, new Object[]{tpFile,new VariantMusical Note}, new int[1]);
Variant f = new Variant(false);
Dispatch.call(wordfile, "Close", f);
flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
component.invoke("Quit", new Variant[] {});
}
}
}



2. 用apache的poi来抽取word,excel。
poi是apache的一个项目,不过就算用poi你可能都觉得很烦,不过不要紧,这里提供了更加简单的一个接口给你:

下载经过封装后的poi包: http://www.matrix.org.cn/down_view.asp?id=14

下载之后,放到你的classpath就可以了,下面是如何使用它的一个例子:

import java.io.*;
import org.textmining.text.extraction.WordExtractor;
/**
* <p>Title: word extraction</p>
* <p>Description: email:chris@matrix.org.cn</p>
* <p>Copyright: Matrix Copyright Coffee 2003</p>
* <p>Company: Matrix.org.cn</p>
* @author chris
* @version 1.0,who use this example pls remain the declare
*/

public class PdfExtractor {
public PdfExtractor() {
}
public static void main(String args[]) throws Exception
{
FileInputStream in = new FileInputStream ("c:\\a.doc");
WordExtractor extractor = new WordExtractor();
String str = extractor.extractText(in);
System.out.println("the result length is"+str.length());
System.out.println("the result is"+str);
}
}



3. pdfbox-用来抽取pdf文件
但是pdfbox对中文支持还不好,先下载pdfbox: http://www.matrix.org.cn/down_view.asp?id=12

下面是一个如何使用pdfbox抽取pdf文件的例子:

import org.pdfbox.pdmodel.PDdocument.
import org.pdfbox.pdfparser.PDFParser;
import java.io.*;
import org.pdfbox.util.PDFTextStripper;
import java.util.Date;
/**
* <p>Title: pdf extraction</p>
* <p>Description: email:chris@matrix.org.cn</p>
* <p>Copyright: Matrix Copyright Coffee 2003</p>
* <p>Company: Matrix.org.cn</p>
* @author chris
* @version 1.0,who use this example pls remain the declare
*/

public class PdfExtracter{

public PdfExtracter(){
}
public String GetTextFromPdf(String filename) throws Exception
{
String temp=null;
PDdocument.nbsppdfdocument.null;
FileInputStream is=new FileInputStream(filename);
PDFParser parser = new PDFParser( is );
parser.parse();
pdfdocument.nbsp= parser.getPDdocument.);
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter( out );
PDFTextStripper stripper = new PDFTextStripper();
stripper.writeText(pdfdocument.getdocument.), writer );
writer.close();
byte[] contents = out.toByteArray();

String ts=new String(contents);
System.out.println("the string length is"+contents.length+"\n");
return ts;
}
public static void main(String args[])
{
PdfExtracter pf=new PdfExtracter();
PDdocument.nbsppdfdocument.nbsp= null;

try{
String ts=pf.GetTextFromPdf("c:\\a.pdf");
System.out.println(ts);
}
catch(Exception e)
{
e.printStackTrace();
}
}

}



4. 抽取支持中文的pdf文件-xpdf
xpdf是一个开源项目,我们可以调用他的本地方法来实现抽取中文pdf文件。

下载xpdf函数包: http://www.matrix.org.cn/down_view.asp?id=15

同时需要下载支持中文的补丁包: http://www.matrix.org.cn/down_view.asp?id=16

按照readme放好中文的patch,就可以开始写调用本地方法的java程序了

下面是一个如何调用的例子:

import java.io.*;
/**
* <p>Title: pdf extraction</p>
* <p>Description: email:chris@matrix.org.cn</p>
* <p>Copyright: Matrix Copyright Coffee 2003</p>
* <p>Company: Matrix.org.cn</p>
* @author chris
* @version 1.0,who use this example pls remain the declare
*/

public class PdfWin {
public PdfWin() {
}
public static void main(String args[]) throws Exception
{
String PATH_TO_XPDF="C:\\Program Files\\xpdf\\pdftotext.exe";
String filename="c:\\a.pdf";
String[] cmd = new String[] { PATH_TO_XPDF, "-enc", "UTF-8", "-q", filename, "-"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedInputStream bis = new BufferedInputStream(p.getInputStream());
InputStreamReader reader = new InputStreamReader(bis, "UTF-8");
StringWriter out = new StringWriter();
char [] buf = new char[10000];
int len;
while((len = reader.read(buf))>= 0) {
//out.write(buf, 0, len);
System.out.println("the length is"+len);
}
reader.close();
String ts=new String(buf);
System.out.println("the str is"+ts);
}
}



关于作者
作者简介:chris,毕业于中国人民大学信息学院。现于香港进行金融分析软件研发,作者亦活跃于 jxta p2p开源软件的开发社区,并热衷于网络安全,AI搜索引擎技术与基于java的游戏引擎技术。
如果大家谁有更好的办法,请告诉作者 : chris@matrix.org.cn

posted @ 2006-01-28 22:41 Larry.Zhao's Coding Diary 阅读(2449) | 评论 (2)编辑 收藏

2005年9月23日

匈牙利命名规范

几年以前,Charles Simonyi(他后来成为微软的著名程序员)设计了一种以前缀为基础的命名方法,这种方法后来称为"匈牙利表示法"以记念他.他的思想是根据每个标识符所代表的含义给它一个前缀.微软后来采用了这个思想,给每个标识符一个前缀以说明它的数据类型.因此,整型变量的前缀是n,长整型变量是nl,字符型数组变量是ca,以及字符串(以空类型结尾的字符数组)以sz为前缀.这些名字可能会非常古怪.比如说:lpszFoo表示"Foo"是一个指向以空字符为结尾的字符串的长整型指针.

 

这种方法的优点是使人能够通过变量的名字来辨别变量的类型,而不比去查找它的定义.遗憾的是,这种方法不仅使变量名字非常绕口,而且使改变变量类型的工作变得十分艰巨.在Windows3.1中,整型变量为16为宽.如果我们在开始时采用了一个整型变量,但是在通过30---40个函数的计算之后,发现采用整型变量宽度不够,这时我们不仅要改变这个变量的类型,而且要改变这个变量在这30--40个函数中的名字.

 

因为不切实际,除了一些顽固的Windows程序员外已经没有人再使用"匈牙利表示法"了.毫无疑问,在某种场合它依然存在,但大部分人现在已经抛弃它了.一般而言,输入前缀是一种糟糕的想法,因为它把变量于其类型紧紧地绑在了一起.

对于30行以下的函数,匈牙利方法一般有优势。

尤其是对界面编程,有优势。

但对于有强烈的算法要求、尤其是有很多抽象类型的C++程序,匈牙利方法简直是一个灾难。

看你用在什么地方。

现在有了很好的IDE工具,如:VC,SourceInsight等.

选中变量,会自动提示告诉你它的声明和定义,这样

匈牙利命名法就没有很大的必要了.

无非就是为了程序可读性较好.

实际上良好的代码书写习惯比强制使用匈牙利命名法更重要.

系统性。整体性。可读性。分类要清楚。要有注释!

 

匈牙利命名法是微软推广的一种关于变量、函数、对象、前缀、宏定义等各种类型的符号的命名规范。匈牙利命名法的主要思想是:在变量和函数名中加入前缀以增进人们对程序的理解。它是由微软内部的一个匈牙利人发起使用的,结果它在微软内部逐渐流行起来,并且推广给了全世界的Windows开发人员。下面将介绍匈牙利命名法,后面的例子里也会尽量遵守它和上面的代码风格。还是那句话,并不是要求所有的读者都要去遵守,但是希望读者作为一个现代的软件开发人员都去遵守它。

 

 

a       Array                                 数组

b       BOOL (int)                            布尔(整数)

by      Unsigned Char (Byte)                  无符号字符(字节)

c       Char                                  字符(字节)

cb      Count of bytes                        字节数

cr      Color reference value                 颜色(参考)值

cx      Count of x (Short)                    x的集合(短整数)

dw      DWORD   (unsigned long)                 双字(无符号长整数)

f       Flags   (usually multiple bit values)   标志(一般是有多位的数值)

fn      Function                              函数

g_      global                                全局的

h       Handle                                句柄

i       Integer                               整数

l       Long                                  长整数

lp      Long pointer                          长指针

m_      Data member of a class                一个类的数据成员

n       Short int                             短整数

p       Pointer                               指针

s       String                                字符串

sz      Zero terminated String                以0结尾的字符串

tm      Text metric                           文本规则

u       Unsigned int                          无符号整数

ul      Unsigned long (ULONG)                 无符号长整数

w       WORD (unsigned short)                 无符号短整数

x,y     x, y coordinates (short)              坐标值/短整数

v       void                                  空

 

 

有关项目的全局变量用g_开始,类成员变量用m_,局部变量若函数较大则可考虑用l_用以显示说明其是局部变量。

 

前缀       类型       例子

g_    全局变量       g_Servers

C     类或者结构体       CDocument,CPrintInfo

m_   成员变量       m_pDoc,m_nCustomers

 

VC常用前缀列表:

 

前缀       类型       描述       例子

ch    char 8位字符    chGrade

ch    TCHAR       16位UNICODE类型字符       chName

b     BOOL       布尔变量       bEnabled

n     int    整型(其大小由操作系统决定)       nLength

n     UINT       无符号整型(其大小由操作系统决定)       nLength

w    WORD       16位无符号整型    wPos

l      LONG       32位有符号整型    lOffset

dw   DWORD       32位无符号整型       dwRange

p     *       Ambient memory model pointer 内存模块指针,指针变量    pDoc

lp     FAR*       长指针       lpDoc

lpsz  LPSTR       32位字符串指针       lpszName

lpsz  LPCSTR       32位常量字符串指针       lpszName

lpsz  LPCTSTR       32位UNICODE类型常量指针       lpszName

h     handle       Windows对象句柄       hWnd

lpfn  (*fn)()       回调函数指针 Callback Far pointer to CALLBACK function       lpfnAbort

 

Windows对象名称缩写:

 

Windows对象       例子变量       MFC类       例子对象

HWND                   hWnd;            CWnd*       pWnd;

HDLG                    hDlg;               CDialog*     pDlg;

HDC                      hDC;               CDC*         pDC;

HGDIOBJ             hGdiObj;         CGdiObject*     pGdiObj;

HPEN     hPen;       CPen*       pPen;

HBRUSH hBrush;       CBrush*       pBrush;

HFONT   hFont;       CFont*       pFont;

HBITMAP       hBitmap;       CBitmap*       pBitmap;

HPALETTE       hPalette;       CPalette*       pPalette;

HRGN     hRgn;       CRgn*       pRgn;

HMENU hMenu;       CMenu*       pMenu;

HWND    hCtl;       CStatic*       pStatic;

HWND    hCtl;       CButton*       pBtn;

HWND    hCtl;       CEdit*       pEdit;

HWND    hCtl;       CListBox*       pListBox;

HWND    hCtl;       CComboBox*       pComboBox;

posted @ 2005-09-23 09:07 Larry.Zhao's Coding Diary 阅读(2814) | 评论 (0)编辑 收藏

2005年9月20日

有關3D方面的關鍵詞摘要

深度缓冲(又称Z-buffer)的作用是确保多边形能够正确地显示在它们本来的深度(相对于摄像机)上。深度缓冲能确定哪个对象在另一个对象的前面,正确的将被渲染。

COM 就是 Component Object Model, 组件对象模型COM 接口和C++的抽象类相似(但不一样),就像抽象类没有与之相关的实际代码一样,COM描述了一套符号和语法而非实现过程。

Page Flipping (页翻动)我们把要显示的物体通通绘制到一个不可见的页上,我们称这个页为“后缓冲区”。绘制完后,快速的把它翻动到可见的“前缓冲区”上,并重复这个过程。当用户正在观看新绘制的可见页(前缓冲区)时,程序要降下一幅要显示的东西绘制到“后缓冲区”上。快速而连续的重复次过程,用户就会看到像电影一样连续的图像了。

Devices (设备)就是你的3D卡。 你得创建一个接口来代表设备,然后使用那个接口来绘制东西。

Game Loop (游戏循环)游戏循环是一段代码,在游戏退出之前循环执行的代码。这段代码在每次循环中都要:在屏幕上绘制物体(或场景人物随便什么)、处理游戏的逻辑过程(如:物体的移动、人工智能等等)、处理Windows的消息等等。

Vertices (顶点) 顶点就是3D空间中的一个点。

3D Primitives就是设备所支持的原始的类型。它包括:Point Lists(点列)、Line Lists(线列)、Line Strips (线带)、Triangle Lists(三角形列)、Triangle Strips(三角形带) Triangle Fans(三角扇形)。使用3D primitive 完成上述的图形是很方便的。以后我们会用3D primitive 来绘制图形。

Flexible Vertex Format (FVF)   (灵活顶点格式)是用来描述顶点属性的一种格式,而这种格式是可以由我们自定义的,所以称它为“灵活顶点格式”。至此,我们至少知道了顶点有三种属性:x值、y值和z值。其实顶点还可以有其他属性,例如颜色与亮度

Vertex Buffers (顶点缓冲)顶点缓冲就是一块用于保存顶点的内存缓冲区。

Backface Culling (背面拣选) 所有的三角形面,面向我们的面将会被渲染出来(可见),否则将不被渲染(不可见,被拣选出来了)。

垂直回扫周期屏幕空白周期当电子束从上至下完成了屏幕上的每一行水平回扫后,会被暂时关闭,回到屏幕的左上角;此过程所需的时间

纹理就是3D图形中的2D贴图(位图),能应用到三角形(或一定数目的三角形)上,用来增加真实感

Materials (材质)用于描述对象(三角形)的反光性能,使它看起来有没有光泽和带有什么颜色。

Diffuse Reflection (漫反射)物体的漫反射颜色,能使物体看起来是什么颜色。

Ambient Reflection (环境反射)这也是一个颜色值,用于描述对象反射环境光的数量。你甚至可以指定它根本不反射环境光,这意味着它将不可见,除非接收到其它类型的光。

Specular Reflection and Power镜面反射的值,可以用镜面反射和高光强度设置对象,使它看起来发亮。

Emission (放射)使物体自发光,但不会影响到场景内的其它物体。

Normals (法线)法线是垂直于三角形面的一个向量,它的方向是由三角形的顶点定义顺序所决定的。

Index Buffer(索引缓冲)索引缓冲就是内存中的一块用于索引顶点的区域。

缓冲数据就是在专用的缓冲区中存储的输入设备的数据

即时数据就是指设备当前的状态

posted @ 2005-09-20 08:09 Larry.Zhao's Coding Diary 阅读(295) | 评论 (0)编辑 收藏

2005年9月16日

Windows程序设计课程笔记一--回顾C++

  • 面向对象的三大特征(以重要性排序):封装、继承、多态。
  • 一个对象的构造过程:class A、class C、class B : public A{private: C c;}这样的三个类,当作如下声明:B b时,构造函数调用的顺序依次是: A C B,先构造class C这样可以保证在class B中对成员数据c的操作是安全的。
  • 构造函数:有class A,A a, 有函数F(A a),当调用F(a)时由于是船传值调用,在这次函数调用时会调用A的拷贝构造函数。
  • 析构函数什么时候应该为虚函数:有class A; class B : public A; 有各自的构造、析构函数,A* pA = new B; delete pA; 构造函数和析构函数调用的顺序为:A(), B(), ~A()。这样就没有析构B。所以把析构函数写为虚函数virtual ~A(),就会得到我们想要的结果:A(), B(), ~B(), ~A()。
  • 多态性的实现原理:虚函数表机制。
          
                                                                                                                                                               

posted @ 2005-09-16 08:40 Larry.Zhao's Coding Diary 阅读(389) | 评论 (0)编辑 收藏

2005年8月7日

《Java核心技术卷一》读记

标记接口:普 通的接口的通常目的是保证类实现了某个或某组方法。而标记接口没有方法,像Cloneable            接口是Java提供的几个标记接口之一。使用标记接口的唯一目的是使得可以用instanceof进行          类型查询:
             if(Obj instanceof Cloneable) ...

内部类:  分为:内部类、局部内部类、匿名内部类、静态内部类几种。
         
          内部类:定 义在类的内部,可以用public、private访问指示符来修饰声明。如果是private
                  别的话则属于外部类的私有内部类,只有这个外部类的方法才能生成内 部类的对象。
                  所以我们说只有内部类才是私有的,普通类总是具有包可见性或公有可见性。

                  内部类的方法可以访问它所在的外部类的实例字段,这是通过编译器添加的隐性的
                  字段来实现的。
                  eg:
                     class BankAccount{
                         public BankAccount (double initBalance){
                            balance = initBalance;
                         }
                         private double balance;
                        
                         public void start(double rate){
                            ActionListener adder = new InterestAdder(rate);
                            Timer t = new Timer(1000, adder);
                            t.start();
                         }

                         private class InterestAdder
                                       implements ActionListener{
                            public InterestAdder (double aRate){
                               rate = aRate;
                            }
                            public void actionPerformed(ActionEvent event){
                               double interest = balance * rate / 100;
                               balance += interest;
                              
                               NumberFormat formatter
                                        = NumberFormat.getCurrencyInstance();
                               System.out.println("balance=" + formatter.format(balance));                                }
                           
                            private double rate;
                         }
                      }
                在 内部类的方法中,有一个隐含的外部类引用,例如内部类中的actionPerformed的方
                法中可以的显示的表示为:

                      double interest = BankAccount.this.balance * this.rate /100;
                内部对象的构造器即可显示的表示为:
                      Outerobject.new InnerClass(Construction paramters);
                即:
                      ActionListener adder = this.new InterestAdder(rate);

       局部内部类定义在方法内部的类,不用访问指示符来修饰。当只要在一个范围内使用该类的情
                   况下使用局部内部类。其好处在于对外界完全隐藏,仅有定义该类的方法能够使用
                   该类。可以访问外部类的实例字段,还可以访问该方法的局部变量。不过需要被访
                   问的局部变量必须被声明为final的。

       匿名内部类匿名内部类即局部内部类,不过我们仅仅需要一个类的对象,所以在定义的时候,
                   连类的名字都不必指定。
                   eg:
                      public void start(final double rate){
                          ActionListener adder = new ActionListener(){
                                public void actionPerformed(ActionEvent event){
                                    double interest = balance * rate / 100;
                                    balance += interest;
                                   
                                    NumberFormat formatter
                                        = NumberFormat.getCurrencyInstance();
                                    System.out.println("balance="+formatter.format(balance);
                                }
                          }
                       }
                   其中用于构造对象的任意参数都要放在超类型名字后的括号后,即:
                      new superclass(construction parameter)
                   这 里的超类型可以是接口,那么内部类实现这个接口;也可以是类,那么内部类扩
                   展这个类。


                   内部类没有构造器,如果内部类是扩展外部类,那么构造参数将被送到超类的构造
                   器中;如果内部类是用来实现接口,则没有任何构造参数,不过必须保留括号。
      
       静态内部类:静 态内部类用于只为了隐藏某个类在另一类内部,而不需要内部类对外部类对象的
                   引用。这是可以把内部类生命为static的来去掉编译器添加的引用。除了没有对外
                   部类对象的引用,静态内部类同其他内部类都一样。而如果某个内部类对象是在静
                   态方法中构造的话,这个内部类也必须声明成静态内部类。
      
       内部类总结:1、内部类对象能够访问创建它的对象的实现--包括那些私有数据。
                   2、内部类能够隐藏起来,不为同一包中的其他类所见。
                   3、匿名内部类可以方便地定义运行时回调。
                   4、使用内部类在编写事件驱动的程序使用起来很方便。

代理:(这个等等再看,看了再记。)

posted @ 2005-08-07 16:13 Larry.Zhao's Coding Diary 阅读(740) | 评论 (0)编辑 收藏

2005年8月3日

编写改变数字参数的方法

如果需要编写改变数字参数的方法,那么可以使用定义在org.oma.CORBA包中的某个持有者类型。这些类型包括IntHolder, BooleanHolder等。每个持有者类型都有一个公有字段value,通过它你可以访问所存储的值。例:

public static void increment (IntegerHolder x)
{
    x.value++;
}

public static void main (String[] args)
{
    IntegerHolder a = new IntegerHolder(3);
    increment (a);
    int result = a.value;
    ...
}

posted @ 2005-08-03 12:36 Larry.Zhao's Coding Diary 阅读(272) | 评论 (0)编辑 收藏