Dart 原生SDK不支持GBK编码,默认支持UTF-8编码,无论是File 还是HttpClient接口,默认提供的encoding 都是utf-8。这样在对接老版本的web接口时很容易产生乱码问题。

例如,file.dart 中,openWrite方法默认encoding就是utf8,这样生成的文件默认就是utf-8编码的。


 
  1. //file.dart
  2. IOSink openWrite({FileMode mode: FileMode.write, Encoding encoding: utf8});

本文提供了一个gbk codec, 提供GBK和Unicode之间的转换,兼容原生API的stream接口,读写文件和Http的 Stream API更为方便。

例如,使用stream api 生成一个gbk文件流程如下:


 
  1. import 'dart:io';
  2. import 'package:fast_gbk/fast_gbk.dart';
  3. void main() async {
  4. File output = File("gbk.txt");
  5. var stream = output.openWrite(encoding: gbk);
  6. stream.write("123");
  7. stream.writeln("456");
  8. stream.writeCharCode(0x41);
  9. await stream.close();
  10. }

使用方法

  • 添加依赖

pubspec.yaml 中添加依赖

dependencies: fast_gbk: ^1.0.0-rc(原文章的版本过低 这里是最新的版本 支持非空安全)
  • 更新pubcache
flutter pub get
  • dart文件中添加引用
import 'package:fast_gbk/fast_gbk.dart';

Demo

  • 最简单的使用场景,直接编码解码String:

 
  1. import 'package:fast_gbk/fast_gbk.dart';
  2. void main() async {
  3. var encoded = gbk.encode("白日依山尽,黄河入海流");
  4. var decoded = gbk.decode([176, 215, 200, 213, 210, 192, 201, 189, 190, 161, 163,
  5. 172, 187, 198, 186, 211, 200, 235, 186, 163, 193, 247]);
  6. }
  • 读GBK编码的文件

 
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:fast_gbk/fast_gbk.dart';
  4. void main() {
  5. File gbkFile = File("gbkFile.txt");
  6. var stream = gbkFile.openRead();
  7. stream.transform(gbk.decoder)
  8. .transform(const LineSplitter())
  9. .listen((line) {
  10. stdout.writeln(line);
  11. });
  12. }
  • 写GBK编码的文件

 
  1. import 'dart:io';
  2. import 'package:fast_gbk/fast_gbk.dart';
  3. void main() async {
  4. File output = File("gbk.txt");
  5. var stream = output.openWrite(encoding: gbk);
  6. stream.write("123");
  7. stream.writeln("456");
  8. stream.writeCharCode(0x41);
  9. await stream.close();
  10. }
  • 解码GBK的 HttpClient response

 
  1. import 'dart:io';
  2. import 'package:fast_gbk/fast_gbk.dart';
  3. void main() async {
  4. var gbkWebUrl = "http://www.newsmth.net/nForum/#!mainpage";
  5. var httpClient = HttpClient();
  6. HttpClientRequest request = await httpClient.getUrl(Uri.parse(gbkWebUrl));
  7. HttpClientResponse response = await request.close();
  8. var responseBody = await response.transform(gbk.decoder).join();
  9. print(responseBody);
  10. httpClient.close();
  11. }
  • DIO 中使用 Gbk Codec 解码 response:

options 设定responseDecoder


 
  1. BaseOptions options = BaseOptions();
  2. options.responseDecoder = gbkDecoder;
  3. _client = Dio(options);

定义gbk Decoder


 
  1. String gbkDecoder (List<int> responseBytes, RequestOptions options,
  2. ResponseBody responseBody) {
  3. String result = gbk.decode(responseBytes);
  4. return result;
  5. }

Q&A

  • Q:又造了一个轮子?和现有的gbk codec有什么区别?
  • A:在功能方面,现有的gbk_codec和gbk2utf8都可以支持encode和decode接口,但不支持Stream接口。

例如:response.transform(gbk.decoder), gbk_codec和gbk2utf8不能这样使用。

在效率方面,本文作者在使用 gbk_codec 和 gbk2utf8 解析HttpResponse时都产生了卡顿,因此才重写了这个模块:

gbk_codec 在解码时大量使用了 String+= 拼接字符,使得界面刷新产生了卡顿,如下火焰图显示刷新界面主页时解码耗时了3.8s:

fast_gbk 测试结果如下,同样界面耗时150ms,是三个codec中解码最快速的,界面明显不再卡顿。

由于fast_gbk支持Stream接口,在大量数据编解码时比直接encode,decode效率会更高些,使用也更方便。

  • Q:稳定么?有没有bug?
  • A:目前测试用例已经覆盖了所有utf-8字符和GBK字符,如果发现有bug,代码已经在Github上开源,欢迎提交 issue 和 pull request。pub链接为: pub.dev