23689


環境変数 `LANC=C` で 日本語ファイル名を扱いたいと思っています。

# 内容

しかし、以下の様な Java のプログラムで日本語ファイル名を読もうとすると  
`LANG` の設定によってはうまくいきません。

[code]

    import java.io.File;
    class Test {
        static public void main(String args[]){
            File dir = new File("somedir");
            for (String f : dir.list())
                System.out.println(f);
        }
    }
    
[/code]

実行すると、こんな感じになります。

[code]

    % echo $LANG
    ja_JP.UTF-8
    % java Test
    い
    う
    あ
    
    % LANG=C java Test
    ???
    ???
    ???
    
[/code]

## 前提条件

日本語ファイル名のファイルは事前に以下の様に作りました。

[code]

    % mkdir somedir
    % touch somedir/あ
    % touch somedir/い
    % touch somedir/う
    
[/code]

## Javaのプロパティ

関係ありそうだなと思ったので、  
`LANG` が `ja_JP.UTF-8` と `C` の場合の  
`System.getProperties()` の差を調べると  
以下の4点が違う様でした  
\- user.country  
\- user.language  
\- sun.jnu.encoding  
\- file.encoding

以下を `C` と `ja_JP.UTF-8` で実行してみて `diff` で調べました。

[code]

    class Prop {
        static public void main(String args[]){
            java.util.Properties p = System.getProperties();
            for (Object k : p.keySet())
                System.out.println(k + ": " + p.get(k));
        }
    }
    
[/code]

それで以下の様に `LANG=C` にして、  
上の4つを `ja_JP.UTF-8` のときと同じになる様に実行してみましたが  
やはりうまく表示されません。

[code]

    % LANG=C java -Dfile.encoding=UTF-8 \
                  -Dsun.jnu.encoding=UTF-8 \
                  -Duser.country=JP \
                  -Duser.language=ja \
                  Test
    
[/code]

でも、最初に `LANG=C java Test` で実行したときとは  
違う文字が出ている様でした。  
どちらにしろ読めないですが

# 出力まとめ

出力はまとめると以下の様になります

[code]

    # うまく表示される
    % java Test | hexdump -C
    00000000  e3 81 84 0a e3 81 86 0a  e3 81 82 0a              |............|
    0000000c
    
    # うまく表示されない
    % LANG=C java Test | hexdump -C
    00000000  3f 3f 3f 0a 3f 3f 3f 0a  3f 3f 3f 0a              |???.???.???.|
    0000000c
    
    # うまく表示されない    
    % LANG=C java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Duser.country=JP -Duser.language=ja Test | hexdump -C
    00000000  ef bf bd ef bf bd ef bf  bd 0a ef bf bd ef bf bd  |................|
    00000010  ef bf bd 0a ef bf bd ef  bf bd ef bf bd 0a        |..............|
    0000001e
    
[/code]

# 環境

OS は Ubuntu です

[code]

    % cat /etc/issue
    Ubuntu 14.04.4 LTS \n \l
    % uname -a
    Linux ub 3.13.0-74-generic #118-Ubuntu SMP Thu Dec 17 22:52:10 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
    % java -version
    java version "1.8.0_77"
    Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
    Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
    
[/code]

なにかヒントが頂けるとうれしいです。  
また、ここを調べた方がいいというポイントがあれば、教えて下さい。  
よろしくお願いします。


CC BY-SA 3.0
2016-04-02T01:05:41.730
23675
2016-04-05T14:56:15.353
2016-04-05T14:52:30.507
15007
15007
question
java 日本語
Java 環境変数 LANG=C だと 日本語ファイル名がうまく扱えない


私はMac OS Xですが、

[code]

    $ LANG=C java -Dfile.encoding=UTF-8 \
                  -Dsun.jnu.encoding=UTF-8 \
                  -Duser.country=JP \
                  -Duser.language=ja \
                  Test
    
[/code]

で日本語出ましたよ。

また、求めている事とは違うかも知れないですが、エンコードを指定して出力すれば、LANG=Cでも日本語で出力されます。

[code]

    import java.io.File;
    import java.io.PrintStream;
        class Test {
        static public void main(String args[]){
            File dir = new File("somedir");
            try {
                PrintStream out = new PrintStream(System.out, true, "UTF-8");
                for (String f : dir.list())
                    out.println(f);
            } catch(Exception e) {
                ;
            }
        }
    }
    
[/code]

結果

[code]

    $ LANG=C java Test
    あ
    い
    う
    
[/code]


CC BY-SA 3.0
2016-04-02T02:28:53.553
23676
2016-04-02T02:28:53.553
8621
23675
answer


各種プロパティを手動で設定したときに出力される `ef bf bd`
は、Unicodeで表現できない文字を表す[「置換文字」](http://smdn.jp/programming/netfx/tips/encoding_fallback/)のUTF-8表現です。Javaは内部的に文字列をUTF-16で扱っているようなので、
`dir.list()` の時点で前述の置換文字に化けていて、それを `3f` で出すか `ef bf bd`
で出すかが切り替わっているだけなのではないでしょうか。

調べてみると、`java.io.File`
でのファイル名の扱いはロケール設定やOSによって変化するという話がちらほら見つかります。Macでは動いたというのはこのあたりの絡みかもしれません。

  * [java read write unicode / UTF-8 filenames (not contents) - Stack Overflow](https://stackoverflow.com/q/14171565/2818869)
  * [How can I open files containing accents in Java? - Stack Overflow](https://stackoverflow.com/q/3072376/2818869)

どちらの質問にも `java.nio.Files`
で解決したという情報が寄せられているので、ひょっとしたらこれで解決するかもしれません。（標準出力へ書き出す際のエンコーディングは別途考慮が必要だと思いますが）


CC BY-SA 3.0
2016-04-02T13:50:32.587
23689
2016-04-02T13:50:32.587
2017-05-23T12:38:55.307
-1
8000
23675
answer


`LANG`は **使用言語を表す** 環境変数で、 **文字コードにも影響します**
。`LANG=C`の場合、使用される文字コードはASCIIです。ASCIIに日本語の文字は **定義せれていません**
。ロケールを英語にしたいのであれば`LANG=C`ではなく、`LANG=en_US.UTF-8`を指定してみましょう。


CC BY-SA 3.0
2016-04-04T10:55:18.090
23738
2016-04-04T10:55:18.090
8215
23675
answer


# java.nio で対応する

java.nio でうまくらしいと、教え頂いたので試してみました。  
結果としては、うまくいきました。

プログラムは以下です

[code]

    import java.io.IOException;
    import java.nio.file.*;
    
    class Test {
        public static void main(String args[]){
            try(DirectoryStream<Path> dir = Files.newDirectoryStream(Paths.get("somedir"))){
                for(Path file : dir)
                    System.out.println(file.getFileName());
            } catch(IOException | DirectoryIteratorException ex) {
                ex.printStackTrace();
            }
        }
    }
    
[/code]

これを以下の様に実行すると大丈夫でした。

[code]

    LANG=C java -Dfile.encoding=UTF-8 \
                -Dsun.jnu.encoding=UTF-8 \
                Test
    
[/code]

file.encoding, sun.jnu.encoding 両方指定しないとダメでした。

## この対応のいいところや特徴

この方法は Java で完結するので、  
OSとかの依存をなくしたい場合はこっちの方法がいいと思います。

でも普通はなくしたいので、  
maven とかで入れたりした依存ライブラリの中で  
java.io を使っていて java.nio に変えられない  
とかでなければ、こっちを使った方がいいのかなと思います。


CC BY-SA 3.0
2016-04-05T14:53:55.477
23767
2016-04-05T14:53:55.477
15007
23675
answer


# 環境変数で対応する

環境変数で対応する方法を2つ教えて頂いたので、それに関して追記します。

## LANG と LC_CTYPE を設定する方法

[code]

    LC_CTYPE=ja_JP.UTF-8 LANG=C java ....
    
[/code]

などとやる。  
他にも `LC_なんとか` という名前の環境変数があって、  
それを設定する事で、`LC_TIME` なら地域毎の時間の表示方法とかを変えられるみたいです。

<https://docs.oracle.com/cd/E26924_01/html/E27144/glmbx.html#glmbc>

に他のものもいくつか載っています。

`LANG` も一緒に設定する事で、`LC_CTYPE` 以外は `C` に設定という風になるみたいです。  
`LANG` < `LC_なんとか` という優先順位になっているからみたいです。

もうちょっと詳しく書くと  
`LANG` < `LC_なんとか` < `LC_ALL`  
という優先順位みたいです

## LANG だけ設定する方法

[code]

    LANG=en_US.UTF-8 java ...
    
[/code]

などとやる。  
これで、英語で UTF-8 を使うという設定になるみたいです。  
`LANG=C` だと、UTF-8 じゃなくて ASCII になるそうです。

LANG に設定できる値は、

[code]

    locale -a
    
[/code]

で調べられます(OSに追加すれば増えます)

書式は <https://docs.oracle.com/cd/E26924_01/html/E27144/glmbx.html> によると

[code]

    language[_territory][.codeset][@modifier]
    
[/code]

になっているみたいです

なので、`ja_JP.UTF-8` なら  
\- language => ja  
\- territory => JP  
\- codeset => UTF-8  
という事みたいです。

## この対応のいいところや特徴

プログラム本体の書換えがいらない

環境変数を使っているので OS に依存してそう。  
もちろん、全OSを検証したわけじゃないですが、  
Macだと大丈夫だったという報告も頂いたので、  
少なからずそういう部分があるのかなと思います。

もしかすると、JVMの実装で違う結果になっている可能性もありますが。


CC BY-SA 3.0
2016-04-05T14:56:15.353
23768
2016-04-05T14:56:15.353
15007
23675
answer