JAVA版StarDict星际译王简单实现
- 由胡正开发的星际译王是Linux平台上很强大的一个开源的翻译软件(也有Windows版本的)支持多种词库、多种语言版本。尤其词库设计比较合理。之前看到一篇博文《星际译王词库应用-自制英汉词典》中用简短的程序就实现了词典的基本功能,不过那个是Linux 下的C/C++版本的,于是决定参考移植一个JAVA版本。
import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.*;/*** {@docRoot}* Java版词典测试版,可以在控制台下输入要查询的单词,回车后会给出单词在词典中的释义* 词典采用星际译王的词典,本程序主要针对英汉词典** @author menglongbor* @updateDate 2012-12-31* @version v1.0.0** 相关参考链接:* http://blog.chinaunix.net/uid-20454005-id-1675913.html* http://hi.baidu.com/sean_zhu_xiang/item/1581342f88be430e73863eee* http://blog.csdn.net/ranxiedao/article/details/7787342* http://www.stardict.cn/* http://www.huzheng.org/* http://code.google.com/p/stardict-3/downloads/list**/public class testdict{final static intMAX_WORD= 256;// 最长输入单词字符数final static intMAX_KEYS= 27;// 26个字母+"-"开头的后缀final static intSIZEINT= 4;final static StringKEY[]= {// 26个字母索引+"-"开头的后缀,不区分大小写"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= 0;// 记录单词索引在文件流中的位置public static StringidxfileString= "oxford-gb.idx";// idx文件路径public static StringdictfileString= "oxford-gb.dict";// 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 = 0;boolean flag = true;len[0] = 0;int index = -1;byte wd[] = new byte[MAX_WORD];int value = 0;try{// 读取单词,对每个字母开头的单词都进行搜索,最多考虑256个字符的单词,// 读到单词结束符\0时赋值表达式的值就不满足while条件而退出while (true){index = isidx.read();STREAM_LOCAL++;// 每读取一次,位置标识加一以记录下单词在文件流中的起始位置if (index == -1){// isidx.reset();flag = false;break;}if ((index != 0) && (len[0] < MAX_WORD)){wd[len[0]] = (byte) index;// 将int转换为bytelen[0]++;} else{break;}}// 转换为JAVA字符串// 此处不用再需要像c/c++那样去掉了最后那个结束符了byte wd2[] = new byte[len[0]];for (int i = 0; i < len[0]; i++){wd2[i] = wd[i];}word_buf[0] = new String(wd2);// System.out.println("get_word:"+word_buf[0]+" len:"+len[0]);// wd = null;// 释放内存// wd2 = null;// 读取偏移量值for (int i = 0; i < SIZEINT; i++){// 将4个byte转换为intint shift = (4 - 1 - i) * 8;index = isidx.read();STREAM_LOCAL++;// 每读取一次,位置标识加一以记录下单词在文件流中的起始位置if (index == -1){// isidx.reset();flag = false;return flag;}value += (index & 0x00FF) << shift;}data_poffset[0] = value;// 读取区块大小值value = 0;for (int i = 0; i < SIZEINT; i++){// 将4个byte转换为intint shift = (4 - 1 - i) * 8;index = isidx.read();STREAM_LOCAL++;// 每读取一次,位置标识加一以记录下单词在文件流中的起始位置if (index == -1){// isidx.reset();flag = false;return flag;}value += (index & 0x00FF) << shift;}data_plength[0] = value;}catch (Exception e){System.out.println("idx file read error!");}// System.out.println("Now local is:"+STREAM_LOCAL);// 得到单词字符长度return flag;}/*** 通过偏移位置offset和长度length 来从dict文件中获取data内容UTF-8编码的字符* @param offset 要读取的内容的起始偏移,为字节数* @param length 要读取的内容的数据块大小,为字节数* @return 字节数组的data int*/public static byte[] get_data(int[] offset, int[] length){long oft = offset[0];long len = length[0];long skip;byte data_buf[] = new byte[length[0]];System.out.println("This word's" + "offset:" + offset[0] + "len:"+ length[0]);try{isdict.reset();long valuedata = isdict.available();if (valuedata < oft + len){System.out.println("No so much value data! " + valuedata);}// skip=isdict.skip(oft);skip = skipBytesFromStream(isdict, oft);if (skip != oft){System.out.println("Skip" + skip + " dict file error!");}if (isdict.read(data_buf) == -1){System.out.println("Arrive at the end of file!");}// // Unicode// StringBuffer sb = new StringBuffer();//// int size =isdict.read(data_buf);//// for (int j = 0; j < size;)// {//// int l = data_buf[j++];//// int h = data_buf[j++];//// char c = (char) ((l & 0xff) | ((h << 8) & 0xff00));//// sb.append(c);//// }//// // return sb.toString();}catch (Exception e){data_buf = null;System.out.println("dict file read error!");e.printStackTrace();}if (data_buf == null){return null;}return data_buf;}/*** utf8解码 参考自http://hi.baidu.com/leo10086/item/d6853813373b19001994ec24 用法:* 假如 newContent 为UTF8编码的字符串 byte[] b = newContent.getBytes(); newContent =* URLEncoder.UTF8Decode( b, 0, b.length );* @param in 要进行解码的UTF8编码的字节数组* @param offset* @param length* @return*/public static String UTF8Decode(byte in[], int offset, int length){StringBuffer buff = new StringBuffer();int max = offset + length;for (int i = offset; i < max; i++){char c = 0;if ((in[i] & 0x80) == 0){c = (char) in[i];} else if ((in[i] & 0xe0) == 0xc0) // 11100000{c |= ((in[i] & 0x1f) << 6); // 00011111i++;c |= ((in[i] & 0x3f) << 0); // 00111111} else if ((in[i] & 0xf0) == 0xe0) // 11110000{c |= ((in[i] & 0x0f) << 12); // 00001111i++;c |= ((in[i] & 0x3f) << 6); // 00111111i++;c |= ((in[i] & 0x3f) << 0); // 00111111} else if ((in[i] & 0xf8) == 0xf0) // 11111000{c |= ((in[i] & 0x07) << 18); // 00000111 (move 18, not 16?)i++;c |= ((in[i] & 0x3f) << 12); // 00111111i++;c |= ((in[i] & 0x3f) << 6); // 00111111i++;c |= ((in[i] & 0x3f) << 0); // 00111111} else{c = ' ';}buff.append(c);}return buff.toString();}public static byte[] UTF8Encode(String str){ByteArrayOutputStream bos = new ByteArrayOutputStream();try{int strlen = str.length();for (int i = 0; i < strlen; i++){char t = str.charAt(i);int c = 0;c |= (t & 0xffff);if (c >= 0 && c < 0x80){bos.write((byte) (c & 0xff));} else if (c > 0x7f && c < 0x800){bos.write((byte) (((c >>> 6) & 0x1f) | 0xc0));bos.write((byte) (((c >>> 0) & 0x3f) | 0x80));} else if (c > 0x7ff && c < 0x10000){bos.write((byte) (((c >>> 12) & 0x0f) | 0xe0)); // <--// correction// (mb)bos.write((byte) (((c >>> 6) & 0x3f) | 0x80));bos.write((byte) (((c >>> 0) & 0x3f) | 0x80));} else if (c > 0x00ffff && c < 0xfffff){bos.write((byte) (((c >>> 18) & 0x07) | 0xf0));bos.write((byte) (((c >>> 12) & 0x3f) | 0x80));bos.write((byte) (((c >>> 6) & 0x3f) | 0x80));bos.write((byte) (((c >>> 0) & 0x3f) | 0x80));}}bos.flush();}catch (Exception e){}return bos.toByteArray();}/*** 将UTF-8字节数据转化为Unicode字符串** @param utf_data* byte[] - UTF-8编码字节数组* @param len* int - 字节数组长度* @return String - 变换后的Unicode编码字符串*/public static String UTF2Uni(byte[] utf_data, int len){StringBuffer unis = new StringBuffer();char unic = 0;int ptr = 0;int cntBits = 0;for (; ptr < len;){cntBits = getCntBits(utf_data[ptr]);if (cntBits == -1){++ptr;continue;} else if (cntBits == 0){unic = UTFC2UniC(utf_data, ptr, cntBits);++ptr;} else{unic = UTFC2UniC(utf_data, ptr, cntBits);ptr += cntBits;}unis.append(unic);}return unis.toString();}/*** 将指定的UTF-8字节组合成一个Unicode编码字符* @param utf byte[] - UTF-8字节数组* @param sptr int - 编码字节起始位置* @param cntBits int - 编码字节数* @return char - 变换后的Unicode字符*/public static char UTFC2UniC(byte[] utf, int sptr, int cntBits){/** Unicode <-> UTF-8 U-00000000 - U-0000007F: 0xxxxxxx U-00000080 -* U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx* 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx* 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx* 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx* 10xxxxxx 10xxxxxx*/int uniC = 0; // represent the unicode charbyte firstByte = utf[sptr];int ptr = 0; // pointer 0 ~ 15// resolve single byte UTF-8 encoding charif (cntBits == 0)return (char) firstByte;// resolve the first bytefirstByte &= (1 << (7 - cntBits)) - 1;// resolve multiple bytes UTF-8 encoding char(except the first byte)for (int i = sptr + cntBits - 1; i > sptr; --i){byte utfb = utf[i];uniC |= (utfb & 0x3f) << ptr;ptr += 6;}uniC |= firstByte << ptr;return (char) uniC;}/*** 根据给定字节计算UTF-8编码的一个字符所占字节数,UTF-8规则定义,字节标记只能为0或2~6* @param b* @return*/private static int getCntBits(byte b){int cnt = 0;if (b == 0)return -1;for (int i = 7; i >= 0; --i){if (((b >> i) & 0x1) == 1)++cnt;elsebreak;}return (cnt > 6 || cnt == 1) ? -1 : cnt;}/*** 显示data内容* @param data_buf UTF-8的单词释义数组* @param data_length UTF-8的单词释义数组长度*/public static void display_data(byte[] data_buf, int data_length[]){// 将UTF-8byte字节数组转为当前环境字符并显示// String tempString = UTF8Decode(data_buf, 0, data_length[0]);String tempString = UTF2Uni(data_buf, data_length[0]);// String tempString = new String(data_buf);data_buf = null;System.out.println(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[1];boolean temp = false;int len[] = new int[1];// 从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)){// System.out.println("compared_word:"+wd[0]);// if (wd[0].compareToIgnoreCase(word) == 0) //// 比较字符串s1和s2,但不区分字母的大小写if (strsEqualsIgnoreCase(wd[0], word) == 0){System.out.println("compared_word:" + word + " " + wd[0]);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[0] = 0;String tempString[] = new String[1];try{count[0] = System.in.read(input_buf) - 2;// 返回实际读取到的字符数,减去2个控制字符byte temp_buf[] = new byte[count[0]];for (int i = 0; i < count[0]; i++){temp_buf[i] = input_buf[i];}tempString[0] = new String(temp_buf);}catch (Exception e){System.out.println("Input error!");}System.out.println("Your input is:" + tempString[0]);return tempString[0];}/*** 从标准输入获取待查询的单词,控制台下为GBK字符,字典索引中的英文单词字母也是如此* @param input_buf* @param count* @return*/public static byte[] get_input(byte[] input_buf, int[] count){try{count[0] = System.in.read(input_buf) - 2;// 返回实际读取到的字符数,减去2个控制字符}catch (Exception e){input_buf = null;System.out.println("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 unused1[] = new int[1];int unused2[] = new int[1];try{// 将文件内部的位置指针重新指向一个流(数据流/文件)的开头返回FILE指针当前位置,// 然后重新遍历整个文件搜寻下一个字母开头的单词isidx.reset();STREAM_LOCAL = 0;for (i = 0; i < MAX_KEYS; i++){// System.out.println("Start search_word:" + KEY[i]);if (search_word(KEY[i], unused1, unused2))// 从idx文件中搜索由word指定的单词,并保存相应的偏移和长度信息{p[i] = STREAM_LOCAL; // 返回当前文件位置// String tempString = Long.toString(STREAM_LOCAL);// System.out.println(KEY[i] + "'s local is:" + tempString);System.out.println(KEY[i] + "'s local is:" + STREAM_LOCAL+ " offset:" + unused1[0] + "length:" + unused2[0]);} elsep[i] = 0;}// isidx.reset();}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 = 0;int pre = 0;String tempString = word.toLowerCase();while (i < MAX_KEYS && KEY[i].charAt(0) < tempString.charAt(0)){pre = i;++i;}if (tempString.charAt(0) == '-'){pre = 0;}System.out.println("Now word's locate is:" + idx_cache[pre]);return idx_cache[pre];}/*** 主要查询函数*/public static void consult(){byte data[] = null;// 释义数据,UTF-8数据long idx[] = new long[MAX_KEYS];// 26个字母孤立单词+"-"开头的后缀对应的索引缓冲int offset[] = new int[1];int length[] = new int[1];System.out.println("Start cache_idx....!");try{System.out.println("Open files....!");// 读取字典索引文件isidx = new BufferedInputStream(new FileInputStream(idxfileString));isidx.mark(isidx.available() + 1);if (!isidx.markSupported()){System.out.println("This stream do not support mark....!");}}catch (Exception e){System.out.println("Open files error!");e.printStackTrace();}cache_idx(idx);// 缓存KEYS在idx中的偏移信息,以便加快search_word的搜索速度try{isdict = new BufferedInputStream(new FileInputStream(dictfileString));isdict.mark(isdict.available() + 1);if (!isdict.markSupported()){System.out.println("This stream do not support mark....!");}}catch (Exception e){System.out.println("Open files error!");e.printStackTrace();}while (true){System.out.println("INPUT A WORD OR PHRASE: ");int count[] = new int[1];String word = get_input(MAX_WORD, count);long skips1, skips2;if (count[0] > 0)// 从控制台得到输入单词字符{try{// 从文件开头跳到单词大致索引所在位置// isidx.mark(0);isidx.reset();skips1 = locate_idx(word, idx);// skips2 = isidx.skip(skips1);skips2 = skipBytesFromStream(isidx, skips1);System.out.println("skips1:" + skips1 + " skips2:" + skips2);}catch (Exception e){System.out.println("locate_idx run error");e.printStackTrace();}if (search_word(word, offset, length)){data = get_data(offset, length);display_data(data, length);data = null;} elseSystem.out.println("SORRY " + word + " CANNOT BE FOUND!\n");System.out.println("\n----------------------------------------\n\n");} elsebreak;}}/*** 不区分大小写比较两个字符串** @param s1* @param s2* @return*/public static int strsEqualsIgnoreCase(String s1, String s2){int n1 = s1.length(), n2 = s2.length();for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++){char c1 = s1.charAt(i1);char c2 = s2.charAt(i2);if (c1 != c2){// 源字符串全部都转为大写字符串c1 = Character.toUpperCase(c1);c2 = Character.toUpperCase(c2);if (c1 != c2){// 源字符串全部都转为小写字符串c1 = Character.toLowerCase(c1);c2 = Character.toLowerCase(c2);if (c1 != c2){return c1 - c2;}}}}return n1 - n2;// 如果其中一个或者两个String都比较完了还没有同样的char的话,那就return两个String的长度差距}/*** 重写了Inpustream 中的skip(long n) 方法,将数据流中起始的n 个字节跳过* 参考:http://blog.csdn.net/ranxiedao/article/details/7787342* @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// skipBufferint SKIP_BUFFER_SIZE = 2048; // skipBuffer is initialized in// skip(long), if needed.byte[] skipBuffer = null;int nr = 0;if (skipBuffer == null){skipBuffer = new byte[SKIP_BUFFER_SIZE];}byte[] localSkipBuffer = skipBuffer;if (n <= 0){return 0;}while (remaining > 0){try{nr = inputStream.read(localSkipBuffer, 0, (int) Math.min(SKIP_BUFFER_SIZE, remaining));}catch (IOException e){e.printStackTrace();}if (nr < 0){break;}remaining -= nr;}return n - remaining;}/*** 主函数* @param args*/public static void main(String args[]){consult();try{isidx.close();isdict.close();}catch (Exception e){System.out.println("Close files error!");e.printStackTrace();}}}如果要在windows平台下编译http://blog.chinaunix.net/uid-20454005-id-1675913.html文章中的程序代码最好保存为cpp文件以C++项目编译执行,而且strcasecmp函数应该换为stricmp函数,并且上面作者原来的程序是在linux平台下的,字符编码本身就是UTF8的不需要进行编码转换,但在windows平台下中文为gb232编码,就需要进行编码的转换,下面为需要添加修改上的字符编码转换后的程序。//UTF-8到GB2312的转换char* U2G(const char* utf8){int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);wchar_t* wstr = new wchar_t[len+1];memset(wstr, 0, len+1);MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);char* str = new char[len+1];memset(str, 0, len+1);WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);if(wstr) delete[] wstr;return str;}//GB2312到UTF-8的转换char* G2U(const char* gb2312){int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);wchar_t* wstr = new wchar_t[len+1];memset(wstr, 0, len+1);MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);char* str = new char[len+1];memset(str, 0, len+1);WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);if(wstr) delete[] wstr;return str;}/** 显示data内容*/void display_data(char *data_buf, unsigned int data_length){fwrite(data_buf,data_length,1,stdout);char *data=(char *)malloc(data_length);memcpy(data,data_buf,data_length);char *p=U2G(data_buf);printf("%s\n",p);free(data);delete p;}以星际译王所支持的牛津英汉词典oxford-gb作为测试,词典格式为UTF8编码的单词字符串,然后是四个字节的int型数据表示该单词在dict释义文件中的起始偏移量,再后四个字节的int型数据表示dict文件中该单词释义总共的长度,如下图所示:
结果显示能够正确得到单词的释义,只是音标未能正确解码,如下图所示:
JAVA版StarDict星际译王简单实现的更多相关文章
- ubuntu安装StarDict星际译王
http://sixipiaoyang.blog.163.com/blog/static/6232358820144146386437/ 1,安装StarDict星际译王. 这是linux系统中最常用 ...
- Ubuntu系统安装stardict(星际译王)词典
直接命令行 sudo apt-get install stardict 安装完毕 启动方法: 1.命令行输入stardict回车 2.高速启动器----搜索stardict直接点击启动. 下载词库: ...
- Linux桌面词典 星际译王(StarDict)
星际译王(StarDict)是利用GTK(GIMP TOOLKIT)开发的国际化的.跨平台的自由的桌面字典软件.它并不包含字典档,使用者须自行下载配合使用.它可以运行于多种不同的平台,如Linux, ...
- centos6.2下安装星际译王stardict3.0
星际译王是一个Linux下很好的翻译软件. 我的系统是centos6.2 32位版.本来在http://code.google.com/p/stardict-3/downloads/list 上下的源 ...
- Ubuntu14.04安装配置星际译王词典
参考自:http://m.blog.csdn.net/blog/u014731529/25917149 平常总会遇到一些不认识的单词,汉字等等.一直使用Chrome 浏览器的翻译插件,不过插件的翻译总 ...
- stardict词典(星际译王)
sudo apt-get install stardict 下载词库: http://abloz.com/huzheng/stardict-dic/zh_CN/ 把下载的压缩包解压,以a为例cd /u ...
- Ubuntu 14.04 安装配置强大的星际译王(stardict)词典
转载http://blog.csdn.net/huyisu/article/details/53437931
- SWFUpload简单使用样例 Java版(JSP)
SWFUpload官方的样例都是PHP的,在这里提供一个Java版的最简单的使用样例,使用JSP页面完毕全部操作. 实现上传,分为三步: 1.JavaScript设置SWFUpload部分(与官方样例 ...
- 相当牛X的java版星际游戏
分享一款牛人用java写的经典游戏,目录结构如下: 虽然只能算一个Demo,但是用到了很多Java基础技术和算法: Java2D,双缓冲,A星寻路,粒子系统,动画效果,处理图片,Swing ui ,U ...
随机推荐
- 在tomcat上部署项目
1.部署项目的第一种方法(项目直接放入 webapps 目录中) 2.部署项目的第二种方法(修改 conf/server.xml 文件 ) 3.部署项目的第三种方法(apache-tomcat-7.0 ...
- Android学习01
一.线性布局(Linerlayout)和相对布局(Relativelayout) 线性布局可以分为水平线性布局(vertical)和垂直线性布局(horizontal)这两种布局方式weight权重按 ...
- 聊聊Redis的持久化
两种持久化策略 1.AOF:记录每一次的写操作到日志上,重启时重放日志以重建数据2.RDB:每隔一段时间保存一次当前时间点上的数据快照 快照就是一次又一次地从头开始创造一切3.可以关闭持久化4. ...
- python 爬取图片
使用python的requests库爬取网页时,获取文本一般使用text方法,如果要获取图片并保存要用content 举个栗子,爬煎蛋网的图: #!/usr/bin/env python #-*- c ...
- IIS 部署 web service
1.在控制台检查 IIS 功能是否已经全部启用 2.重新注册IIS 3.设定程序池的正确版本
- ntpdate 设置时区(注意本地时区要设置正确)
修改timezone sudo cp -a /usr/share/zoneinfo/Etc/GMT-8 /etc/localtime date -R == 展示当前的timezone ntpda ...
- docker aufs存储驱动文件系统
Docker aufs存储驱动layer.diff.mnt目录的区别 /var/lib/docker/aufs layer子目录: 镜像.镜像历史列表.容器.容器INIT分别有对应的文件.文件名和di ...
- UIView的API
- (instancetype)initWithFrame:(CGRect)frame; 使用指定的框架矩形初始化并返回新分配的视图对象. - (instancetype)initWithCoder: ...
- Manjaro 安装 ibus-rime 输入法
Manjaro 安装 ibus-rime 输入法 安装软件包: sudo pacman -S ibus ibus-rime yay -S ibus-qt 编辑/添加配置文件~/.xprofile: e ...
- 【快学Docker】快速创建容器,容器常用命令
前言 容器是Docker的三大核心概念之一.简单地说,容器是独立运行的一个或一组应用,以及它们的运行态环境.对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面 ...