Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@manymanyuni

Flutter(Dart) で Shift_JIS/EUC-JP をデコード

Dart で文字コードを変換するときの個人的におすすめなライブラリがこちら。
https://pub.dev/packages/charset_converter
iOS だと iOS SDK, Android だと Java 組み込みの機能をそのまま使って変換しているので幅広い文字コードに対応しており、バグも恐らく少なくそれでいて高速であるため。
逆に、これ以外のプラットフォームでは現状動作しない(macOS とか Windows)ので、現時点であまりいないとは思われますが、デスクトップアプリもサポートしたい場合には使えません。
そういう人向けには、代わりに以下が使えます。
https://pub.dev/packages/euc
ただこのライブラリを使うと、なぜか改行が無視されてしまう・・。(改行ありの euc-jp や shift_jis をデコードすると、改行なしになる)
なぜなのか気になってコードを見てみると、

class JISDecoder extends Converter<List<int>, String> {
  @override
  convert(input) {
    List<int> result = [];
    for (int i = 0; i < input.length; i++) {
      var c1 = input[i];
      if (c1 <= 0x7F) {
        // ASCII Compatible
        result.addAll(EUC_TABLE[c1] ?? []);
      } else if ...
    return utf8.decode(result);
  }
}

// ASCII Compatible のところでLF(0x0a)もちゃんと入れているように見える・・。なんで動いてないんだろう?と思って EUC_TABLE を見ると、

const EUC_TABLE = {
  0x00: [0],
  0x10: [16],
  0x20: [32],
  ...

こんな感じで、LF(0x0a)がテーブルに登録されてなかった・・。結果、0x0a は単純に捨てられていた。
EUC-JP だけじゃなくて Shift_JIS も同様にテーブルに登録されていないので改行が変換できていなかった。
結局どうしたかというと、以下のように // ASCII Compatible の処理を微妙に書き換えた新しいクラスを作ってそれの convert を直接呼び出してお茶を濁しました。

import 'package:euc/euc-table.dart';
import 'package:euc/jis-table.dart';

class MyJISDecoder extends Converter<List<int>, String> {
  @override
  convert(input) {
    List<int> result = [];
    for (int i = 0; i < input.length; i++) {
      var c1 = input[i];
      if (c1 <= 0x7F) {
        // ASCII Compatible (partially)
        result.add(c1);
      } else if (c1 >= 0xa1 && c1 <= 0xdf) {
        // Half-width Hiragana
        result.addAll(JIS_TABLE[c1] ?? []);
      } else if (c1 >= 0x81 && c1 <= 0x9f) {
        // JIS X 0208
        var c2 = input[++i];
        result.addAll(JIS_TABLE[(c1 << 8) + c2] ?? []);
      } else if (c1 >= 0xe0 && c1 <= 0xef) {
        // JIS X 0208
        var c2 = input[++i];
        result.addAll(JIS_TABLE[(c1 << 8) + c2] ?? []);
      } else {
        // Unknown
        result.addAll([]);
      }
    }
    return utf8.decode(result);
  }
}

class MyEucJPDecoder extends Converter<List<int>, String> {
  @override
  convert(input) {
    List<int> result = [];
    for (int i = 0; i < input.length; i++) {
      var c1 = input[i];
      if (c1 <= 0x7E) {
        // ASCII Compatible
        result.add(c1);
      } else if (c1 == 0x8e) {
        // Hiragana
        var c2 = input[++i];
        result.addAll(EUC_TABLE[(c1 << 8) + c2] ?? []);
      } else if (c1 == 0x8f) {
        // JIS X 0212
        var c2 = input[++i];
        var c3 = input[++i];
        result.addAll(EUC_TABLE[(c1 << 16) + (c2 << 8) + c3] ?? []);
      } else {
        // JIS X 0208
        var c2 = input[++i];
        result.addAll(EUC_TABLE[(c1 << 8) + c2] ?? []);
      }
    }
    return utf8.decode(result);
  }
}

// iOS/Android では CharsetConverter が使えるのでこれを使います(高速だし高機能だしバグも少ない
// それ以外の環境だと euc パッケージを使います(ただし処理が遅い)
// CharsetConverter は変な文字が入っていると変換に失敗して null を返してくるので、その場合も euc パッケージを利用します(デコードの場合の話)
Future<Uint8List> encodeJIS(String input) async {
  if (input == null) {
    return null;
  }

  if (Platform.isIOS || Platform.isAndroid) {
    return await CharsetConverter.encode('cp932', input);
  } else {
    return Uint8List.fromList(ShiftJIS().encode(input));
  }
}

Future<Uint8List> encodeEucJP(String input) async {
  if (input == null) {
    return null;
  }

  if (Platform.isIOS || Platform.isAndroid) {
    return await CharsetConverter.encode('euc-jp', input);
  } else {
    return Uint8List.fromList(EucJP().encode(input));
  }
}

Future<String> decodeJIS(Uint8List input) async {
  if (input == null) {
    return null;
  }

  if (Platform.isIOS || Platform.isAndroid) {
    var result = await CharsetConverter.decode('cp932', input);

    if (result == null) {
      return MyJISDecoder().convert(input.toList(growable: false));
    } else {
      return result;
    }
  } else {
    return MyJISDecoder().convert(input.toList(growable: false));
  }
}

Future<String> decodeEucJP(Uint8List input) async {
  if (input == null) {
    return null;
  }

  if (Platform.isIOS || Platform.isAndroid) {
    var result = await CharsetConverter.decode('euc-jp', input);

    if (result == null) {
      return MyEucJPDecoder().convert(input.toList(growable: false));
    } else {
      return result;
    }
  }else {
    return MyEucJPDecoder().convert(input.toList(growable: false));
  }
}

ということで、charset_converter がおすすめっていう紹介と、charset_converter が使えない環境では euc パッケージを使わざるを得ないけど改行が無視されるバグがあるので上記の対応が必要、というお話でした。

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?