Android Smart Dict - 单词导入 - 词汇表解析


有了一个可以用的文件浏览器之后,我们就可以选择并且得到要加载的词汇表文件。当用户点击了某一个词汇表文件之后,浏览器把此文件返回给词汇表解析器,解析出每一个单词,然后插入数据表。

如何解析?为了不在这一部分花费掉太多的时间,我只需要做简单的格式定制和解析就好,后面再做性能上面的优化工作。

在其他地方kiang到一个词汇表,格式很简单,就像这样:

-------------------------------------------------------------------------

ability  [4'biliti]
n. (U)能力,本领;(C)才能,才智

-------------------------------------------------------------------------

有人一定发现了,音标没显示对,是的,我们后面再来处理这些细节问题。

我现在只需要提取三部分出来就可以了,本来也就只有三部分:单词,音标,解释。了便于显示信息给用户,我在文件前面加入一个header,来描述一下这个词汇表的名称和单词数量。用两个关键字来定义开始和结束。类似xml文件的方式,注意是类似,并不完全一致。在这里,我只是为了解析的方便。

-------------------------------------------------------------------------

<header>
三个英语词汇
3
</header>

<words>

ability  [4'biliti]
n. (U)能力,本领;(C)才能,才智


ability  [4'biliti]
n. (U)能力,本领;(C)才能,才智

able  ['eib4l]
adj. 能够…的,有能力的

</words>

-------------------------------------------------------------------------

header的名称叫“三个英语词汇”,单词数量是3个,单词部分用<words>和</words>包围起来,单词之间用空行隔开。这样够简单了吧,哈哈哈!

我定义一个叫BookParser的类,就负责解析这词库文件,它有一个公开的方法叫getNextWord()返回解析出来的单词对象。

代码如下:

  1. public class BookParser {   
  2.     private static final String TAG = "BookParser";   
  3.     private BookHeader mBookHeader;   
  4.     private BufferedReader mReader;   
  5.     public BookParser(File file) {   
  6.         try {   
  7.             InputStream in = new FileInputStream(file);   
  8.             mReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));   
  9.             // Parse book header;   
  10.             String line = mReader.readLine();   
  11.             if(line.startsWith(ParseScheme.HEADER_TOKEN_START)) {   
  12.                 mBookHeader = new BookHeader();   
  13.                 String name = mReader.readLine();   
  14.                 String number = mReader.readLine();   
  15.                 mBookHeader.setBookName(name);   
  16.                 mBookHeader.setWordNumber(Integer.valueOf(number));   
  17.             }   
  18.             // Move the cursor to the first word;   
  19.             while((line = mReader.readLine()) == null ||   
  20.                     !ParseScheme.WORD_TOKEN_START.equalsIgnoreCase(line.trim())) {   
  21.                 continue;   
  22.             }   
  23.         } catch (FileNotFoundException e) {   
  24.             Log.d(TAG, e.getLocalizedMessage());   
  25.         } catch (IOException e) {   
  26.             Log.d(TAG, e.getLocalizedMessage());   
  27.         }   
  28.     }   
  29.     public void close() {   
  30.         if(mReader != null) {   
  31.             try {   
  32.                 mReader.close();   
  33.                 mReader = null;   
  34.                 mBookHeader = null;   
  35.             } catch (IOException e) {   
  36.                 Log.d(TAG, e.getLocalizedMessage());   
  37.             }   
  38.         }   
  39.     }   
  40.     public BookHeader getBookHeader() {   
  41.         return mBookHeader;   
  42.     }   
  43.     /**  
  44.      * The format like:  
  45.      * word [symbol] translations  
  46.      */  
  47.     public Word getNextWord() throws IOException {   
  48.         Word word = new Word();   
  49.         String line = mReader.readLine().trim();   
  50.         if(!Utils.isEmpty(line) && !ParseScheme.WORD_TOKEN_END.equalsIgnoreCase(line)) {   
  51.             int offset = line.indexOf('[');   
  52.             if(offset < 0) {   
  53.                 offset = line.indexOf(" ");   
  54.                 if(offset < 0) {   
  55.                     offset = line.length();   
  56.                 }   
  57.             }   
  58.             word.setWord(line.substring(0, offset).trim());   
  59.             word.setSymbol(line.substring(offset).trim());   
  60.             line = mReader.readLine();   
  61.             String trans = line;   
  62.             while(!Utils.isEmpty(line)) {   
  63.                 trans = trans + "\n" + line;   
  64.                 line = mReader.readLine();   
  65.             }   
  66.             word.setTranslation(trans);   
  67.             return word;   
  68.         }   
  69.         return  

这个一定要注意一点,文件存储为上面格式,在构造Reader的时候就要相应地注明以什么格式读取,比如我的是UTF-8格式。

  1. InputStream in = new FileInputStream(file);   
  2.             mReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));  

接着我需要用一个类来驱动解析,一个叫BookLoader的类,继承Thread驱动整个解析过程。

它内部有一个Listener,提供一些有用的回调方法。

  1. public static interface OnBookLoadListener {   
  2.     public void onStart();   
  3.     public void onLoadHeader(BookHeader header);   
  4.     public void onComplete(int state);   
  5.     public void onLoadWord(Word word);   
  6. }  

onStart():就是在解析开始之前调用,这里面可以弹出一些dialog显示一些相关信息。

onLoadHeader():这个方法是当解析完header之后,返回给调用者关于词库的信息,可以反馈给用户。

onComplete():这个很明显,在整个load完成以后调用,参数state用来提示状态,成功,还是取消还是出错。

onLoadWord():这个方法在每次load一个单词的时候调用,可以用来即时反馈用户目前在load那个单词。

这些方法都是用在UI里边给用户提示相关信息。

代码如下:

  1. public class BookLoader extends Thread {   
  2.     public static final int LOAD_STATE_SUCCESS = 0;   
  3.     public static final int LOAD_STATE_ERROR = 1;   
  4.     public static final int LOAD_STATE_CANCEL = 3;   
  5.     private static final String TAG = "BookLoader";   
  6.     private boolean mCancel;   
  7.     private BookParser mParser;   
  8.     private File mBookFile;   
  9.     private OnBookLoadListener mOnBookLoadListener;   
  10.     private Context mContext;   
  11.     public static interface OnBookLoadListener {   
  12.         public void onStart();   
  13.         public void onLoadHeader(BookHeader header);   
  14.         public void onComplete(int state);   
  15.         public void onLoadWord(Word word);   
  16.     }   
  17.     public BookLoader(Context context) {   
  18.         mContext = context;   
  19.     }   
  20.     public void setOnBookLoadListener(OnBookLoadListener l) {   
  21.         mOnBookLoadListener = l;   
  22.     }   
  23.     public void startParse(File bookFile) {   
  24.         if(mOnBookLoadListener == null) {   
  25.             throw new RuntimeException("The OnBookLoadListener is null, " +   
  26.                     "you have to call setOnBookLoadListener() method first!");   
  27.         }   
  28.         mBookFile = bookFile;   
  29.         start();   
  30.         mOnBookLoadListener.onStart();   
  31.     }   
  32.     @Override  
  33.     public void run() {   
  34.         mParser = new BookParser(mBookFile);   
  35.         mOnBookLoadListener.onLoadHeader(mParser.getBookHeader());   
  36.         final DatabaseHelper mDatabaseHelper = DatabaseHelper.getInstance(mContext);   
  37.         int counter = 0;   
  38.         try {   
  39.             while (true) {   
  40.                 if(mCancel) {   
  41.                     mOnBookLoadListener.onComplete(LOAD_STATE_CANCEL);   
  42.                     return;   
  43.                 }   
  44.                 Word word = mParser.getNextWord();   
  45.                 // If word is null, load finished.   
  46.                 if(word == null) {   
  47.                     if(mParser.getBookHeader().getWordNumber() == counter) {   
  48.                         mOnBookLoadListener.onComplete(LOAD_STATE_SUCCESS);   
  49.                     } else {   
  50.                         mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);   
  51.                     }   
  52.                     return;   
  53.                 } else {   
  54.                     mOnBookLoadListener.onLoadWord(word);   
  55.                     counter++;   
  56.                     // Insert word to database;   
  57.                     mDatabaseHelper.insertWord(word);   
  58.                 }   
  59.                 // Give chance for UI thread change its state;   
  60.                 Thread.sleep(10);   
  61.             }   
  62.         } catch (IOException e) {   
  63.             Log.d(TAG, e.getLocalizedMessage());   
  64.             mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);   
  65.         } catch (InterruptedException e) {   
  66.             Log.d(TAG, e.getLocalizedMessage());   
  67.             mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);   
  68.         } finally {   
  69.             if(mParser != null) {   
  70.                 mParser.close();   
  71.                 mParser = null;   
  72.             }   
  73.         }   
  74.     }   
  75. }  

为了让UI线程能够即时地显示最新状态,必须在while循环里面每次sleep几毫秒,不然UI的显示就会stutter。

现在来看看我在Activity里边做了什么。

主要就是实现OnBookLoadListener这个接口,开始load的时候弹出dialog,当header解析出来以后,显示词库的名称,单词数量,每次load一个单词就相应地显示到dialog里面,完成load以后显示结果信息。

代码如下:

  1. private void loadBook(File bookFile) {   
  2.     BookLoader loader = new BookLoader(this);   
  3.     loader.setOnBookLoadListener(new OnBookLoadListener() {   
  4.         private ProgressDialog dialog;   
  5.         @Override  
  6.         public void onStart() {   
  7.             FileLoaderActivity.this.runOnUiThread(new Runnable() {   
  8.                 public void run() {   
  9.                     dialog = new ProgressDialog(FileLoaderActivity.this);   
  10.                     dialog.setIcon(R.drawable.file_book);   
  11.                     dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);   
  12.                     dialog.setCancelable(false);   
  13.                     dialog.setMessage("");   
  14.                     dialog.setTitle(getText(R.string.loading_word).toString().   
  15.                             replace("%s"""));   
  16.                     dialog.setButton(ProgressDialog.BUTTON_NEUTRAL, getText(R.string.cancel),   
  17.                             new DialogInterface.OnClickListener() {   
  18.                         public void onClick(DialogInterface dialogInterface, int whichButton) {   
  19.                             final CharSequence buttonTxt = dialog.getButton(   
  20.                                     ProgressDialog.BUTTON_NEUTRAL).getText();   
  21.                             if (buttonTxt.equals(getText(R.string.cancel))) {   
  22.                                 // TODO: Cancel loading;   
  23.                             } else if (buttonTxt.equals(getText(R.string.ok))) {   
  24.                                 dialog.dismiss();   
  25.                             }   
  26.                         }   
  27.                     });   
  28.                     dialog.show();   
  29.                 }   
  30.             });   
  31.         }   
  32.         @Override  
  33.         public void onLoadWord(final Word word) {   
  34.               mDatabaseHelper.insertWord(word);   
  35.             FileLoaderActivity.this.runOnUiThread(new Runnable() {   
  36.                 public void run() {   
  37.                     dialog.setMessage(word.getWord() + ' ' + word.getSymbol());   
  38.                     dialog.incrementProgressBy(1);   
  39.                 }   
  40.             });   
  41.         }   
  42.         @Override  
  43.         public void onLoadHeader(final BookHeader header) {   
  44.             FileLoaderActivity.this.runOnUiThread(new Runnable() {   
  45.                 public void run() {   
  46.                     dialog.setTitle(header.getBookName());   
  47.                     dialog.setMax(header.getWordNumber());   
  48.                 }   
  49.             });   
  50.         }   
  51.         @Override  
  52.         public void onComplete(int state) {   
  53.             if(state == BookLoader.LOAD_STATE_SUCCESS) {   
  54.                 FileLoaderActivity.this.runOnUiThread(new Runnable() {   
  55.                     public void run() {   
  56.                         dialog.setMessage(getText(R.string.load_word_success));   
  57.                         dialog.getButton(ProgressDialog.BUTTON_NEUTRAL).setText(getText(R.string.ok));   
  58.                     }   
  59.                 });   
  60.             }   
  61.         }   
  62.     });   
  63.     loader.startParse(bookFile);   
  64. }  

OK,来看看效果如何:

还行,只是load速度比较慢,因为我们是一条一条地insert所以会很慢,后面我会考虑用transaction来做,这样整个过程就会快很多。

相关内容