由胡正开发的星际译王是Linux平台上很强大的一个开源的翻译软件(也有Windows版本的)支持多种词库多种语言版本尤其词库设计比较合理之前看到一篇博文《星际译王词库应用自制英汉词典》中用简短的程序就实现了词典的基本功能不过那个是Linux 下的C/C++版本的于是决定参考移植一个JAVA版本
import javaioByteArrayOutputStream;
import javaioIOException;
import javaioInputStream;
import javaio*;
/**
* {@docRoot}
* Java版词典测试版可以在控制台下输入要查询的单词回车后会给出单词在词典中的释义
* 词典采用星际译王的词典本程序主要针对英汉词典
*
* @author menglongbor
* @updateDate
* @version v
*
* 相关参考链接
* l
* _zhu_xiang/item/fbeeeee
*
*
*
* /downloads/list
*
*/
public class testdict
{
final static intMAX_WORD= ;// 最长输入单词字符数
final static intMAX_KEYS= ;// 个字母+开头的后缀
final static intSIZEINT= ;
final static StringKEY[]= {// 个字母索引+开头的后缀不区分大小写
A b c d e f
g h i j k l m n o p q r s
t u v w x y z };
public static InputStreamisidx= null;// 读取idx文件时所要的流
public static InputStreamisdict= null;// 读取dict文件时所要的流
public static longSTREAM_LOCAL= ;// 记录单词索引在文件流中的位置
public static StringidxfileString= oxfordgbidx;// idx文件路径
public static StringdictfileString= oxfordgbdict;// dict文件路径
/**
* 从idx文件中获取当前目标单词
* @param word_buf 保存的是c/c++字符串数组转换为JAVA字符串
* @param data_poffset 用来保存单词的data偏移位置信息
* @param data_plength 用来保存单词的data长度信息
* @param len
* @return
*/
public static boolean get_word(String[] word_buf int[] data_poffset
int[] data_plength int[] len)
{
// int len = ;
boolean flag = true;
len[] = ;
int index = ;
byte wd[] = new byte[MAX_WORD];
int value = ;
try
{
// 读取单词对每个字母开头的单词都进行搜索最多考虑个字符的单词
// 读到单词结束符\时赋值表达式的值就不满足while条件而退出
while (true)
{
index = isidxread()
STREAM_LOCAL++;// 每读取一次位置标识加一以记录下单词在文件流中的起始位置
if (index == )
{
// isidxreset()
flag = false;
break;
}
if ((index != ) && (len[] < MAX_WORD))
{
wd[len[]] = (byte) index;// 将int转换为byte
len[]++;
} else
{
break;
}
}
// 转换为JAVA字符串
// 此处不用再需要像c/c++那样去掉了最后那个结束符了
byte wd[] = new byte[len[]];
for (int i = ; i < len[]; i++)
{
wd[i] = wd[i];
}
word_buf[] = new String(wd)
// Systemoutprintln(get_word:+word_buf[]+ len:+len[])
// wd = null;// 释放内存
// wd = null;
// 读取偏移量值
for (int i = ; i < SIZEINT; i++)
{
// 将个byte转换为int
int shift = ( i) * ;
index = isidxread()
STREAM_LOCAL++;// 每读取一次位置标识加一以记录下单词在文件流中的起始位置
if (index == )
{
// isidxreset()
flag = false;
return flag;
}
value += (index & xFF) 《 shift;
}
data_poffset[] = value;
// 读取区块大小值
value = ;
for (int i = ; i < SIZEINT; i++)
{
// 将个byte转换为int
int shift = ( i) * ;
index = isidxread()
STREAM_LOCAL++;// 每读取一次位置标识加一以记录下单词在文件流中的起始位置
if (index == )
{
// isidxreset()
flag = false;
return flag;
}
value += (index & xFF) 《 shift;
}
data_plength[] = value;
}
catch (Exception e)
{
Systemoutprintln(idx file read error!)
}
// Systemoutprintln(Now local is:+STREAM_LOCAL)
// 得到单词字符长度
return flag;
}
/**
* 通过偏移位置offset和长度length 来从dict文件中获取data内容UTF编码的字符
* @param offset 要读取的内容的起始偏移为字节数
* @param length 要读取的内容的数据块大小为字节数
* @return 字节数组的data int
*/
public static byte[] get_data(int[] offset int[] length)
{
long oft = offset[];
long len = length[];
long skip;
byte data_buf[] = new byte[length[]];
Systemoutprintln(This words + offset: + offset[] + len:
+ length[])
try
{
isdictreset()
long valuedata = isdictavailable()
if (valuedata < oft + len)
{
Systemoutprintln(No so much value data! + valuedata)
}
// skip=isdictskip(oft)
skip = skipBytesFromStream(isdict oft)
if (skip != oft)
{
Systemoutprintln(Skip + skip + dict file error!)
}
if (isdictread(data_buf) == )
{
Systemoutprintln(Arrive at the end of file!)
}
// // Unicode
// StringBuffer sb = new StringBuffer()
//
// int size =isdictread(data_buf)
//
// for (int j = ; j < size;)
// {
//
// int l = data_buf[j++];
//
// int h = data_buf[j++];
//
// char c = (char) ((l & xff) | ((h 《 ) & xff))
//
// sbappend(c)
//
// }
//
// // return sbtoString()
}
catch (Exception e)
{
data_buf = null;
Systemoutprintln(dict file read error!)
eprintStackTrace()
}
if (data_buf == null)
{
return null;
}
return data_buf;
}
/**
* utf解码 参考自 用法
* 假如 newContent 为UTF编码的字符串 byte[] b = newContentgetBytes() newContent =
* URLEncoderUTFDecode( b blength )
* @param in 要进行解码的UTF编码的字节数组
* @param offset
* @param length
* @return
*/
public static String UTFDecode(byte in[] int offset int length)
{
StringBuffer buff = new StringBuffer()
int max = offset + length;
for (int i = offset; i < max; i++)
{
char c = ;
if ((in[i] & x) == )
{
c = (char) in[i];
} else if ((in[i] & xe) == xc) //
{
c |= ((in[i] & xf) 《 ) //
i++;
c |= ((in[i] & xf) 《 ) //
} else if ((in[i] & xf) == xe) //
{
c |= ((in[i] & xf) 《 ) //
i++;
c |= ((in[i] & xf) 《 ) //
i++;
c |= ((in[i] & xf) 《 ) //
} else if ((in[i] & xf) == xf) //
{
c |= ((in[i] & x) 《 ) // (move not )
i++;
c |= ((in[i] & xf) 《 ) //
i++;
c |= ((in[i] & xf) 《 ) //
i++;
c |= ((in[i] & xf) 《 ) //
} else
{
c = ;
}
buffappend(c)
}
return bufftoString()
}
public static byte[] UTFEncode(String str)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream()
try
{
int strlen = strlength()
for (int i = ; i < strlen; i++)
{
char t = strcharAt(i)
int c = ;
c |= (t & xffff)
if (c >= && c < x)
{
boswrite((byte) (c & xff))
} else if (c > xf && c < x)
{
boswrite((byte) (((c >>> ) & xf) | xc))
boswrite((byte) (((c >>> ) & xf) | x))
} else if (c > xff && c < x)
{
boswrite((byte) (((c >>> ) & xf) | xe)) // <
// correction
// (mb)
boswrite((byte) (((c >>> ) & xf) | x))
boswrite((byte) (((c >>> ) & xf) | x))
} else if (c > xffff && c < xfffff)
{
boswrite((byte) (((c >>> ) & x) | xf))
boswrite((byte) (((c >>> ) & xf) | x))
boswrite((byte) (((c >>> ) & xf) | x))
boswrite((byte) (((c >>> ) & xf) | x))
}
}
bosflush()
}
catch (Exception e)
{
}
return bostoByteArray()
}
/**
* 将UTF字节数据转化为Unicode字符串
*
* @param utf_data
* byte[] UTF编码字节数组
* @param len
* int 字节数组长度
* @return String 变换后的Unicode编码字符串
*/
public static String UTFUni(byte[] utf_data int len)
{
StringBuffer unis = new StringBuffer()
char unic = ;
int ptr = ;
int cntBits = ;
for ( ptr < len;)
{
cntBits = getCntBits(utf_data[ptr])
if (cntBits == )
{
++ptr;
continue;
} else if (cntBits == )
{
unic = UTFCUniC(utf_data ptr cntBits)
++ptr;
} else
{
unic = UTFCUniC(utf_data ptr cntBits)
ptr += cntBits;
}
unisappend(unic)
}
return unistoString()
}
/**
* 将指定的UTF字节组合成一个Unicode编码字符
* @param utf byte[] UTF字节数组
* @param sptr int 编码字节起始位置
* @param cntBits int 编码字节数
* @return char 变换后的Unicode字符
*/
public static char UTFCUniC(byte[] utf int sptr int cntBits)
{
/*
* Unicode <> UTF U UF: xxxxxxx U
* UFF: xxxxx xxxxxx U UFFFF: xxxx
* xxxxxx xxxxxx U UFFFFF: xxx xxxxxx xxxxxx
* xxxxxx U UFFFFFF: xx xxxxxx xxxxxx xxxxxx
* xxxxxx U UFFFFFFF: x xxxxxx xxxxxx xxxxxx
* xxxxxx xxxxxx
*/
int uniC = ; // represent the unicode char
byte firstByte = utf[sptr];
int ptr = ; // pointer ~
// resolve single byte UTF encoding char
if (cntBits == )
return (char) firstByte;
// resolve the first byte
firstByte &= ( 《 ( cntBits)) ;
// resolve multiple bytes UTF encoding char(except the first byte)
for (int i = sptr + cntBits ; i > sptr; i)
{
byte utfb = utf[i];
uniC |= (utfb & xf) 《 ptr;
ptr += ;
}
uniC |= firstByte 《 ptr;
return (char) uniC;
}
/**
* 根据给定字节计算UTF编码的一个字符所占字节数UTF规则定义字节标记只能为或~
* @param b
* @return
*/
private static int getCntBits(byte b)
{
int cnt = ;
if (b == )
return ;
for (int i = ; i >= ; i)
{
if (((b 》 i) & x) == )
++cnt;
else
break;
}
return (cnt > || cnt == ) : cnt;
}
/**
* 显示data内容
* @param data_buf UTF的单词释义数组
* @param data_length UTF的单词释义数组长度
*/
public static void display_data(byte[] data_buf int data_length[])
{
// 将UTFbyte字节数组转为当前环境字符并显示
// String tempString = UTFDecode(data_buf data_length[])
String tempString = UTFUni(data_buf data_length[])
// String tempString = new String(data_buf)
data_buf = null;
Systemoutprintln(tempString)
}
/**
* 从idx文件中搜索由word指定的单词并保存相应的偏移和长度信息
* @param word
* @param data_poffset
* @param data_plength
* @return 是否搜索成功
*/
public static boolean search_word(String word int[] data_poffset
int[] data_plength)
{
String wd[] = new String[];
boolean temp = false;
int len[] = new int[];
// 从idx文件中获取当前目标单词
// for (get_word(wd data_poffset data_plength) end; get_word(wd
// data_poffset data_plength))
// {
while (get_word(wd data_poffset data_plength len))
{
// Systemoutprintln(compared_word:+wd[])
// if (wd[pareToIgnoreCase(word) == ) //
// 比较字符串s和s但不区分字母的大小写
if (strsEqualsIgnoreCase(wd[] word) == )
{
Systemoutprintln(compared_word: + word + + wd[])
temp = true;
break;
}
}
return temp;
}
/**
* 从标准输入获取待查询的单词控制台下为GBK字符字典索引中的英文单词字母也是如此
* @param max_len
* @param count
* @return
*/
public static String get_input(int max_len int[] count)
{
byte input_buf[] = new byte[max_len];
count[] = ;
String tempString[] = new String[];
try
{
count[] = Systeminread(input_buf) ;// 返回实际读取到的字符数减去个控制字符
byte temp_buf[] = new byte[count[]];
for (int i = ; i < count[]; i++)
{
temp_buf[i] = input_buf[i];
}
tempString[] = new String(temp_buf)
}
catch (Exception e)
{
Systemoutprintln(Input error!)
}
Systemoutprintln(Your input is: + tempString[])
return tempString[];
}
/**
* 从标准输入获取待查询的单词控制台下为GBK字符字典索引中的英文单词字母也是如此
* @param input_buf
* @param count
* @return
*/
public static byte[] get_input(byte[] input_buf int[] count)
{
try
{
count[] = Systeminread(input_buf) ;// 返回实际读取到的字符数减去个控制字符
}
catch (Exception e)
{
input_buf = null;
Systemoutprintln(Input error!)
}
return input_buf;
}
/**
* 缓存KEYS在idx中的偏移信息以便加快search_word的搜索速度
* @param idx_cache 保存每个单字母单词对应的起始位置
* @return
*/
public static void cache_idx(long[] idx_cache)
{
int i;
long[] p = idx_cache;
int unused[] = new int[];
int unused[] = new int[];
try
{
// 将文件内部的位置指针重新指向一个流(数据流/文件)的开头返回FILE指针当前位置
// 然后重新遍历整个文件搜寻下一个字母开头的单词
isidxreset()
STREAM_LOCAL = ;
for (i = ; i < MAX_KEYS; i++)
{
// Systemoutprintln(Start search_word: + KEY[i])
if (search_word(KEY[i] unused unused))// 从idx文件中搜索由word指定的单词并保存相应的偏移和长度信息
{
p[i] = STREAM_LOCAL; // 返回当前文件位置
// String tempString = LongtoString(STREAM_LOCAL)
// Systemoutprintln(KEY[i] + s local is: + tempString)
Systemoutprintln(KEY[i] + s local is: + STREAM_LOCAL
+ offset: + unused[] + length: + unused[])
} else
p[i] = ;
}
// isidxreset()
}
catch (Exception e)
{
// TODO: handle exception
}
}
/**
* 定位由word指定的单词在idx文件中的大概偏移位置
* @param word
* @param idx_cache
* @return
*/
public static long locate_idx(String word long[] idx_cache)
{
int i = ;
int pre = ;
String tempString = wordtoLowerCase()
while (i < MAX_KEYS && KEY[i]charAt() < tempStringcharAt())
{
pre = i;
++i;
}
if (tempStringcharAt() == )
{
pre = ;
}
Systemoutprintln(Now words locate is: + idx_cache[pre])
return idx_cache[pre];
}
/**
* 主要查询函数
*/
public static void consult()
{
byte data[] = null;// 释义数据UTF数据
long idx[] = new long[MAX_KEYS];// 个字母孤立单词+开头的后缀对应的索引缓沖
int offset[] = new int[];
int length[] = new int[];
Systemoutprintln(Start cache_idx…!)
try
{
Systemoutprintln(Open files…!)
// 读取字典索引文件
isidx = new BufferedInputStream(new FileInputStream(
idxfileString))
isidxmark(isidxavailable() + )
if (!isidxmarkSupported())
{
Systemoutprintln(This stream do not support mark…!)
}
}
catch (Exception e)
{
Systemoutprintln(Open files error!)
eprintStackTrace()
}
cache_idx(idx)// 缓存KEYS在idx中的偏移信息以便加快search_word的搜索速度
try
{
isdict = new BufferedInputStream(new FileInputStream(
dictfileString))
isdictmark(isdictavailable() + )
if (!isdictmarkSupported())
{
Systemoutprintln(This stream do not support mark…!)
}
}
catch (Exception e)
{
Systemoutprintln(Open files error!)
eprintStackTrace()
}
while (true)
{
Systemoutprintln(INPUT A WORD OR PHRASE: )
int count[] = new int[];
String word = get_input(MAX_WORD count)
long skips skips;
if (count[] > )// 从控制台得到输入单词字符
{
try
{
// 从文件开头跳到单词大致索引所在位置
// isidxmark()
isidxreset()
skips = locate_idx(word idx)
// skips = isidxskip(skips)
skips = skipBytesFromStream(isidx skips)
Systemout
println(skips: + skips + skips: + skips)
}
catch (Exception e)
{
Systemoutprintln(locate_idx run error)
eprintStackTrace()
}
if (search_word(word offset length))
{
data = get_data(offset length)
display_data(data length)
data = null;
} else
Systemoutprintln(SORRY + word + CANNOT BE FOUND!\n)
Systemout
println(\n\n\n)
} else
break;
}
}
/**
* 不区分大小写比较两个字符串
*
* @param s
* @param s
* @return
*/
public static int strsEqualsIgnoreCase(String s String s)
{
int n = slength() n = slength()
for (int i = i = ; i < n && i < n; i++ i++)
{
char c = scharAt(i)
char c = scharAt(i)
if (c != c)
{
// 源字符串全部都转为大写字符串
c = CharactertoUpperCase(c)
c = CharactertoUpperCase(c)
if (c != c)
{
// 源字符串全部都转为小写字符串
c = CharactertoLowerCase(c)
c = CharactertoLowerCase(c)
if (c != c)
{
return c c;
}
}
}
}
return n n;// 如果其中一个或者两个String都比较完了还没有同样的char的话那就return两个String的长度差距
}
/**
* 重写了Inpustream 中的skip(long n) 方法将数据流中起始的n 个字节跳过
* 参考
* @param inputStream
* @param n
* @return
*/
private static long skipBytesFromStream(InputStream inputStream long n)
{
long remaining = n; // SKIP_BUFFER_SIZE is used to determine the size of
// skipBuffer
int SKIP_BUFFER_SIZE = ; // skipBuffer is initialized in
// skip(long) if needed
byte[] skipBuffer = null;
int nr = ;
if (skipBuffer == null)
{
skipBuffer = new byte[SKIP_BUFFER_SIZE];
}
byte[] localSkipBuffer = skipBuffer;
if (n <= )
{
return ;
}
while (remaining > )
{
try
{
nr = inputStreamread(localSkipBuffer (int) Mathmin(
SKIP_BUFFER_SIZE remaining))
}
catch (IOException e)
{
eprintStackTrace()
}
if (nr < )
{
break;
}
remaining = nr;
}
return n remaining;
}
/**
* 主函数
* @param args
*/
public static void main(String args[])
{
consult()
try
{
isidxclose()
isdictclose()
}
catch (Exception e)
{
Systemoutprintln(Close files error!)
eprintStackTrace()
}
}
}
如果要在windows平台下编译l文章中的程序代码最好保存为cpp文件以C++项目编译执行而且strcasecmp函数应该换为stricmp函数并且上面作者原来的程序是在linux平台下的字符编码本身就是UTF的不需要进行编码转换但在windows平台下中文为gb编码就需要进行编码的转换下面为需要添加修改上的字符编码转换后的程序
//UTF到GB的转换
char* UG(const char* utf)
{
int len = MultiByteToWideChar(CP_UTF utf NULL )
wchar_t* wstr = new wchar_t[len+];
memset(wstr len+)
MultiByteToWideChar(CP_UTF utf wstr len)
len = WideCharToMultiByte(CP_ACP wstr NULL NULL NULL)
char* str = new char[len+];
memset(str len+)
WideCharToMultiByte(CP_ACP wstr str len NULL NULL)
if(wstr) delete[] wstr;
return str;
}
//GB到UTF的转换
char* GU(const char* gb)
{
int len = MultiByteToWideChar(CP_ACP gb NULL )
wchar_t* wstr = new wchar_t[len+];
memset(wstr len+)
MultiByteToWideChar(CP_ACP gb wstr len)
len = WideCharToMultiByte(CP_UTF wstr NULL NULL NULL)
char* str = new char[len+];
memset(str len+)
WideCharToMultiByte(CP_UTF wstr str len NULL NULL)
if(wstr) delete[] wstr;
return str;
}
/*
* 显示data内容
*/
void display_data(char *data_buf unsigned int data_length)
{
fwrite(data_bufdata_lengthstdout)
char *data=(char *)malloc(data_length)
memcpy(datadata_bufdata_length)
char *p=UG(data_buf)
printf(%s\np)
free(data)
delete p;
}
以星际译王所支持的牛津英汉词典oxfordgb作为测试词典格式为UTF编码的单词字符串然后是四个字节的int型数据表示该单词在dict释义文件中的起始偏移量再后四个字节的int型数据表示dict文件中该单词释义总共的长度如下图所示
结果显示能够正确得到单词的释义只是音标未能正确解码如下图所示