Dart 中读取 mdict 词典教程

在 Dart 中用 dict_reader 读取 mdict 词典

Dict_reader 是一个用于读取 mdict 词典的 Dart 语言库,支持 MDX/MDD 格式。

README 提供了几个示例,本文提供更适合生产环境的示例。

安装

1
dart pub add dict_reader

使用

在生产环境中,会用到搜索单词、查看单词的功能,而且必须要高效,这时候轮到 SQLite 数据库出场了。在第一次读取词典时,用 Drift 存储少量数据,用于之后快速地搜索和查看。

安装 Drift

1
dart pub add drift drift_flutter dev:drift_dev dev:build_runner

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// database.dart
import "package:dict_reader/dict_reader.dart";
import "package:drift/drift.dart";
import "package:drift/native.dart";
import "dart:io";

part 'database.g.dart';

@TableIndex(name: 'idx_keyText', columns: {#keyText})
class Dictionary extends Table {
  TextColumn get keyText => text()();
  IntColumn get recordBlockOffset => integer()();
  IntColumn get startOffset => integer()();
  IntColumn get endOffset => integer()();
  IntColumn get compressedSize => integer()();
}

@DriftDatabase(tables: [Dictionary])
class AppDatabase extends _$AppDatabase {
  // After generating code, this class needs to define a `schemaVersion` getter
  // and a constructor telling drift where the database should be stored.
  // These are described in the getting started guide: https://drift.simonbinder.eu/getting-started/#open
  AppDatabase() : super(_openConnection());

  @override
  int get schemaVersion => 1;

  Future<void> insertUsers(List<DictionaryCompanion> dictionary) async {
    await batch((batch) {
      batch.insertAll(this.dictionary, dictionary);
    });
  }

  Future<List<DictionaryData>> searchWord(String word) {
    return (select(dictionary)..where((u) => u.keyText.like('$word%'))).get();
  }

  static QueryExecutor _openConnection() {
    return NativeDatabase(File('dictionary.db'));
  }
}

void main() async {
  final database = AppDatabase();
  final dictReader = DictReader("MDX FILE PATH");

  // 不用获取 keyText 和 offset 存入数据库时,可以传入 false 参数
  await dictReader.init();

  // 将 keyText 和 offset 存入数据库,只需一次
  var queue = <DictionaryCompanion>[];
  await for (final (
        keyText,
        (recordBlockOffset, startOffset, endOffset, compressedSize)
      ) in dictReader.read()) {
    queue.add(DictionaryCompanion(
        keyText: Value(keyText),
        recordBlockOffset: Value(recordBlockOffset),
        startOffset: Value(startOffset),
        endOffset: Value(endOffset),
        compressedSize: Value(compressedSize)));
  }

  await database.insertUsers(queue);

  // 通过数据库搜索单词
  final result = (await database.searchWord("go"))[0];
  // 获取单词数据
  print(await dictReader.readOne(result.recordBlockOffset, result.startOffset,
      result.endOffset, result.compressedSize));

  await database.close();
}

然后生成 database.g.dart 文件:

1
dart run build_runner build

这个示例不适合直接放到生产环境中,稍微改一下就可以了。

结语

Dict_reader 主要是翻译 mdict-analysis。我对 Dart 不是很熟,在翻译过程中经常去问 gpt-4o-mini 以及 SearchGPTool,有帮助也有捣乱,最终还是花了四天时间完成。

Dict_reader 并没有完全翻译,而且挑选了最重要、有意义的部分翻译,也基于我自身考虑(我手头没有 mdict 格式 3.0 版本的词库),例如没有校验、不支持 lzo 压缩、不支持 mdict 格式的 3.0 版本,但不影响一般使用。