この記事では、指定されたURIからファイルを読み出して文字コードを判定する方法について書いていきます。

文字コード判定をするために、UniversalDetectorのライブラリを利用します。

このライブラリで検出できる文字コードはこちらの記事に載っていますが、日本語に関連する文字コードで言えば

・ISO-2022-JP

・Shift_JIS

・EUC-JP

・Unicode

・UTF-8

・UTF-16BE / UTF-16LE

・UTF-32BE / UTF-32LE / X-ISO-10646-UCS-4-3412 / X-ISO-10646-UCS-4-2143

これだけ判定できます。ちなみにUTF-8のBOM付きは判定できないようです。

0.はじめに

指定する情報をファイルパスではなく、URIにした理由は以下2つ。

Storage Access Frameworkを利用して取得できる情報がURIだから

以前の記事[Java]Storage Access Frameworkを利用して好きな場所にファイルを保存でSAFを利用する方法を記載しましたが、これを利用し好きな場所からファイルを取得する事もできます。で、このSAFを利用すると任意の場所のファイル情報が簡単にURIで取得できます。

ファイルパス指定だとファイルを読み出す際に追加でユーザー許可を求める必要がある

最初にこちらの記事を参考にURIからファイルパスに変換して、利用しようとしていましたが、Uriからファイルパスを取得するをする処理を実行しようとすると

Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=27696, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()

というExceptionが発生してしまい、これを解決するためには、ActivityCompat.requestPermissionsメソッドを利用して一時的にユーザーの許可を得る実装をする必要がありました。
という事で、次章から実装方法について書いていきます。

1.build.gradleにimplementationを追加

UniversalDetectorライブラリを利用するために以下を追加します。

dependencies {
    ...
    implementation 'com.github.albfernandez:juniversalchardet:2.3.2'
}

2.UniversalDetectorを利用した文字コード判定クラスの実装

以下のようにContextとUriをもらって、判定文字コードをStringで返却するクラスを実装します。

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import org.mozilla.universalchardet.UniversalDetector;

import java.io.InputStream;

public class FileCharDetector {

    private Context mContext;
    private Uri mUri;

    public FileCharDetector(Context context, Uri uri) {
        this.mContext = context;
        this.mUri = uri;
    }

    public String detect() throws java.io.IOException {
        byte[] buf = new byte[4096];
        InputStream inputStream = mContext.getContentResolver().openInputStream(mUri);

        // UniversalDetectorを利用
        UniversalDetector detector = new UniversalDetector(null);

        // 判定開始
        int nread;
        while ((nread = inputStream.read(buf)) > 0 && !detector.isDone()) {
            detector.handleData(buf, 0, nread);
        }
        detector.dataEnd();

        // 判定文字コードを取得
        String encType = detector.getDetectedCharset();
        if (encType != null) {
            Log.d("DETECTOR", "文字コード = " + encType);
        } else {
            Log.d("DETECTOR", "文字コードを判定できませんでした");
        }

        // 判定情報のリセット
        detector.reset();

        return encType;
    }
}

3.判定された文字コードでファイル読み込みを行う実装

上記クラスを利用して判定された文字コードを利用してファイル読み込みを行う実装を以下のようにします。

public void readTextFile(final Uri uri)
{
    InputStream inputStream = null;
    try {
        // 文字コードを判定
        FileCharDetector fd = new FileCharDetector(getContext(), uri);
        String encType = fd.detect();

        // 判定された文字コードを指定してファイル読み込みを行う
        inputStream = context.getContentResolver().openInputStream(uri);
        BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream), encType));
        String line;
        while ((line = reader.readLine()) != null) {
            // 1行ずつの処理を行う
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

以上の実装で文字コードを判定してテキストファイルを読み込みができるようになります。
 
このライブラリを利用して、パスワードの管理ツール「パスワードメモ」のアプリでは、任意の文字コードのCSVファイルの取込みが行えるようになりました。PC等で編集したCSVファイルを文字コードを気にする事なく取り込む事ができます。
ぜひこちらも利用してみて下さい!