2007年6月9日 星期六

用C#及Interop取出Office同義字資訊

感謝Interop,讓我們能再利用ms office中的超炫功能,在開發研究專案時需要能取得同義字,以往都是使用Wordnet,但我發現Wordnet中的同義字分的太細了,例如good及nice在wordnet就不算同義字。而手邊也沒有現成可供程式讀取的字典,所以就開始打MS office 中同義字功能的腦筋。在搜尋網路上相關的文章後,發現也很少人在研究這個問題,以下是一篇不錯的討論,
Unable to cast object of type 'System.String[*]' to type 'System.String[]'.
我也以這篇為基礎用C#寫了以下測試程式,列出hard這個字的同義字:


using System;
using System.Collections;
using System.Text;

//記得先在Visual Studio的方案總管中加入參考,
//這個參考位於COM標籤中
//的Microsoft Word 11.0 Object Library (Office 2003的狀況)
using Microsoft.Office.Interop.Word;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
ApplicationClass AC = new ApplicationClass();

object LangID = WdLanguageID.wdEnglishUS;
// 沒試過其他語言,將來有機會再試試

SynonymInfo SI = AC.get_SynonymInfo("hard",ref LangID);

IEnumerable PartOfSpeechList = (IEnumerable)SI.PartOfSpeechList;
// 不可以直接轉換為int[],原因請參考上述的討論連結
// 取出hard這個字的詞性代號,如形容詞是0,副詞是2
// 因為hard的形容詞同義字有四種意思,副詞有一種
// 所以列出來是 0 0 0 0 2

foreach (int p in PartOfSpeechList)
Console.Out.WriteLine(p);

// 試了幾個字,大部分都沒有相關字
IEnumerable RelatedWordList = (IEnumerable)SI.RelatedWordList;
foreach (string r in RelatedWordList)
Console.Out.WriteLine(r);

// 如前述,有五個意義
IEnumerable MeaningList = (IEnumerable)SI.MeaningList;
foreach (string m in MeaningList)
{
// 在MeaningList裡的是同義字的第一個字
Console.Out.WriteLine("-----" + m + "-----");
// 要利用這個字去取出所有同義字
object m1 = m;
IEnumerable SynonymList = (IEnumerable)SI.get_SynonymList(ref m1);
foreach (string s in SynonymList)
Console.Out.WriteLine(s);
}
}
catch (Exception ex)
{
Console.Out.WriteLine(ex.Message);
}
Console.Out.WriteLine("Done");
}
}
}


利用以上程式,在配合上詞性判斷程式,應該就可以取出較為一般人所接受的同義字了。

2007年6月6日 星期三

以陣列作為Hashtable的Key值

這星期在開發專案時,遇上了需要以陣列作為Hashtable的Key值的狀況,但直接以陣列當Key值加入Hashtable卻會發生get或containsKey無法正常運作的狀況,瀏覽了一下網路,發現只有少數在討論的網頁,下面是最接近的一篇討論:
http://www.velocityreviews.com/forums/t150335-arrays-as-key-in-a-hashmap.html
但實際測試後卻發現有以下問題

1. 利用Wrapper的例子只能先將陣列先轉換成Object陣列後才能使用
2. List(利用Arrays.asList做轉換)只有在物件陣列有效,如果是基礎型別(primitive type)的陣列,例如int[]及double[]就不適用

根據上述問題,如果將Wrapper加入泛型,也一樣無法解決基礎型別的問題,最後找到的通用解決方案如下:

1. 如果是Java基礎型別陣列或內建類別陣列(應該都已實作hashCode()及equals(Object obj)),則可利用下列Wrapper


class ArrayWrapper
{
Object[] m_array;

public ArrayWrapper(Object array) {

m_array = new Object[Array.getLength(array)];
for (int i=0;i<array.getlength(array);i++)
m_array[i] = Array.get(array, i);
}

public int hashCode() {
return Arrays.hashCode(m_array);
}

public boolean equals(Object o) {
ArrayWrapper aw = (ArrayWrapper)o;
return Arrays.equals(aw.m_array, this.m_array);
}
}

由於無論是基礎型別陣列或Java內建類別陣列皆可以Object當參數,所以不須轉換成Object陣列。當然在當參數傳入前或後,必須先確定(或檢查)是否為陣列。

2. 如果是自訂類別陣列,ㄧ定要實作hashCode()及equals(Object obj),再使用ArrayWrapper,例如下面的例子,

public class ObjectKey{

private String m_String;
private double m_Double;
private int m_Int;

public ObjectKey(String str, double dou, int i) {
this.m_String = str;
this.m_Double = dou;
this.m_Int = i;
}

public int hashCode(){
return this.m_String.hashCode();
}

public boolean equals(Object o) {
ObjectKey ok = (ObjectKey)o;

return ((this.m_String.equals(ok.m_String))
&& (this.m_Int == this.m_Int)
&& (this.m_Double == this.m_Double));
}
}

基本上hashCode的設計可以簡化,差別只會影響效能,但一定要有;equals就要看設計時如何定義兩物件相等,如上例子,如果認定m_String及m_Double相等即可,那就只要寫成
return ((this.m_String.equals(o.m_String)) && (this.m_Double == o.m_Double));

Java的壓縮

當處理資料量大時,而記憶體或硬碟不夠大時,壓縮是ㄧ個很好的解決方案,最近開始接觸Web網頁資料處理,所面對的都是至少10GB以上的資料量,所以希望能藉助Java中內建的壓縮機制,一個簡單的範例如下:


import java.io.*;

import java.util.zip.*;
import java.util.*;

public class TestGZipString {

public TestGZipString() {
}

public static void main(String[] args){
try {

String str1 = "Test compressing string";

//壓縮,將壓縮結果存成byte陣列
ByteArrayOutputStream baout = new ByteArrayOutputStream();

GZIPOutputStream gzipout = new GZIPOutputStream(baout);
OutputStreamWriter writer = new OutputStreamWriter(gzipout);
writer.write(str1);
writer.close();

byte[] source1=baout.toByteArray(); //壓縮的結果

//解壓縮
ByteArrayInputStream bain = new ByteArrayInputStream(source1);
GZIPInputStream gzipin = new GZIPInputStream(bain);
BufferedReader reader= new BufferedReader(new InputStreamReader(gzipin));

StringBuilder sb = new StringBuilder();
String s = reader.readLine();

while (s!=null) {
sb.append(s);
s = reader.readLine();
}

System.out.println(sb.toString());
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}

初步使用以上觀念試著將所壓縮的內容(byte陣列)儲存在MySQL中(Blob欄位)發現約有50%的壓縮率,雖然不如預期,但沒魚蝦也好。

另外,最初的程式用DataOutputStream而不是OutputStreamWriter,原以為只DataOutputStream有writeUTF可以處理Unicode,但實際運作時卻發現writeUTF有64k的大小限制,不得已改用OutputStreamWriter,發現處理Unicode也沒問題。

Java中的foreach及Hashtable存取

C#中許多語法及用法令我懷念但我發現Java也可以寫的一樣簡潔,只是我還不習慣罷了下面的程式示範了如何使用如C#中的foreach及Hashtable的內容存取,


import java.util.*;

public class Main {

public Main() {}

public static void main(String[] args){

String str1 = "Test 1";
String str2 = "Test 2";

Hashtable hashtable = new Hashtable();

hashtable.put(1,str1);
hashtable.put(2, str2);
hashtable.put(3, str1 + str2);
hashtable.put(4, str2 + str1);

// Java中的foreach用法, 只要:後面是Collection(例如Array)就可以了
for (int i: hashtable.keySet())
System.out.println(hashtable.get(i));
}
}

搬家

雖然才寫了5篇文章,但對Yahoo部落的設計有點感冒,所以在今天搬了家,希望這是個好地方。

不要再忘了帶自備餐具

自從看了 推廣環保筷 日辣妹女社長率隊遊街 這篇報導後,就馬上去多購買了一副餐具放在辦公室,準備好好響應一下,哪知道知難行易,每次都是等到點完菜之後,才發現沒帶自備餐具,就這樣過了三個星期。在寫這篇的同時,我也準備要出發去吃中餐了,將自備餐具緊緊握在手上,不要再忘了!

前言

以前從未想過發表自己,,進入過一些討論區,但僅限於在旁觀看。人老了,不想讓自己研究很久的東西,在離開它們幾天就連名字都忘了,強迫自己寫日記,除了幫助自己,也許有機會幫到別人,也許這就是數位記憶的好處,讓人有機會窺視他人的大腦。