質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.23%

Javaでtxtファイル(ANSI)をUTF-8として取得したい。

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 4,504

lereve

score 12

 前提・実現したいこと

[前提]
趣味でプログラムを触ったことがある程度の初心者です。
今回、会社で初めてJavaを用いたPCソフトを作ることになりました。
質問内容はあまりに根本的な内容になるかとは思いますが、
参考本やHPに書かれている内容ではなかなか理解できず困っています。
是非、ご教授いただきたく思います。

[実現したいこと]
別のソフトからtxtファイル形式で出力されるデータを読み込み、
データベースに格納するといった処理を行いたいと思っています。

 発生している課題・エラーメッセージ

[課題]
文字コードの変換方法がわかりません。
自己調査では.getByte()を使って行うのかなと思ったのですが、
格納後にチェックしてみると日本語が文字化けしてしまいます。

[その他情報]
・データベースやその他周辺のプログラムの文字コード指定は問題ないか
→txtファイルをUTF-8指定で保存、それをデータベースに格納すると問題なく表示されることから問題は今回のプログラムだと思っています。

そこで質問なのですが、
1.txtファイル(ANSI)をUTF-8で取得するにはどのような方法を用いれば良いのか。
(txtファイルは外部のプログラムからの出力なのでUTF-8指定で保存、は難しいと思っています。)
2..getbyte()を使う場合、https://www.sejuku.net/blog/19673 の内容を吟味しながら試してみたのですが、
byte[] bytes1 = str.getBytes("ANSI");
を行ってbyte型にしてから
String newStr1 = new String(bytes1, "UTF-8");
をすることでANSIの文字列がUTF-8に変換される、といった解釈で良いでしょうか。

よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

1.txtファイル(ANSI)をUTF-8で取得するにはどのような方法を用いれば良いのか。

y_waiwaiさん回答のように外部(テキストファイル)からJavaの世界(UTF16/UTF32)へ正しく読み込むと考えます。

ANSIの読み込みですが、Charset.forName("MS932")を用いるとよいと思います。"Shift_JIS"という文字セット名でも概ねうまくはいくのですが、それはMS932(Windows日本語版のMicrosoft独自Shift JIS)とは「厳密には違う文字セット」ですのでMS932を使うのが普通は無難と思います。メモ帳で見える文字と"Shift_JIS"で読み込んだ結果が不一致になるといったことが有り得ますので。

void boo() throws IOException {
    try (InputStream is = new FileInputStream("xx");
         InputStreamReader isr = new InputStreamReader(is, Charset.forName("MS932"));
         BufferedReader br = new BufferedReader(isr)) {
        String line;
        while ((line = br.readLine()) != null) {
                  // Javaとしての正しい文字列になっているはず
        }
    }
}

2..getbyte()を使う場合

多くの場合getBytesで「UTF-8のバイト列を自分で生成」する必要はありません。ファイルなりDBなりへ文字列を出力する機能に「しかるべき文字セットを指定する方法」があるからです。

今のプログラムでも「DBへ期待通りの文字が格納できている」ケースがあるならば問題点は外部ファイル(MS932)からJava内部へ読み込むことだけと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/05 14:57

    KSwordOfHaste様

    たびたびお世話になります。
    まさしく今回の質問の内容はKSwordOfHaste様がおっしゃる通りの内容でした。
    「DBへ期待通りの文字が格納できている」という状況を読み解き、
    言葉足らずな質問文から正確な回答を導いていただき、ありがとうございました。

    新たな知識の獲得に繋がりました。
    私も限られた情報から正確に問題点を見つけ出せるレベルになりたいです。
    ありがとうございました。

    キャンセル

+2

まず、ANSIというコードセットは英数字記号だけ(俗に言う1バイト文字)の文字コードです
ですので、日本語がある、という時点で、それはANSIではないので、なんの文字コードか、というのを調べる必要があります

で、何らかの文字コードからUFT8に変換する方法ですが、
「Java UTF8 変換」でぐぐるといろいろ解説が出てきますので、みてみてください。

-- 追記 --
元文字コードがShift-JISということでしたので、「Java SJIS UTF8」でぐぐってみました
その中で参考になりそうなところは、
https://dev.classmethod.jp/server-side/java/csv_read_java_char_trans/
まさにSJISのファイルをUTF8にして読み込んでますね

んで、気をつけないといけないところは、Java の標準の文字コードはUTF16だ、ということです
まず、ファイルの読み込みをShiftJISとして読み込む(ということはプログラム内部でUTF16で保存される)
そして、データベースに保存するときには、(UTF16から)UTF8に変換しなければいけない、ということですね

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/05 14:30

    > MS的にはミスではなく意図的に記述をANSIに統一
    自分もそう解釈しました。wikiみてようやく何を意図していそうかがわかった気がします。質問意図とちょっと外れた話してしまいすみませんでした。

    キャンセル

  • 2018/03/05 14:49

    KSwordOfHaste様
    fuzzball様
    otn様

    ご回答いただきありがとうございます。
    内容のレベルに私の知識が追い付いておらず、ポカーンとなってしまいましたが確かにデータベースの設計をしている時はANSIではなくCP932を使いますね…日本語って難しい。という感じでした。

    いずれにしろ知識の獲得へと繋がりました。
    ありがとうございました。

    キャンセル

  • 2018/03/05 15:00

    y_waiwai様

    追記にてURLをいただきありがとうございました。
    該当のページも流し読みしていたのですが、csvファイルであることから
    「かんけーないわ」と思ってしまっていたようです。
    文字コードの話ですから拡張子は何でも良かったですよね。
    エラーと締切に追いかけられて目が回っていたようです。

    大変参考になりました。
    また、基礎を固めるいい機会になりました。
    ありがとうございました。

    キャンセル

+1

コードのサンプルを書いてみました。(java8)

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * Shift_JISのファイルをUTF-8に変換
 */
public class Q116094 {
    /**
     * @param args 変換前ファイル、変換後ファイル
     */
    public static void main(String[] args) {
        if (args.length < 2) {
            System.err.println("変換前ファイル、変換後ファイルを指定して実行してください ");
            System.exit(1);
        }
        Path from = Paths.get(args[0]); // 変換前ファイル
        if (!Files.exists(from)) {
            System.err.println("変換前ファイルが見つかりません: " + from.toAbsolutePath());
            System.exit(2);
        }
        Path to = Paths.get(args[1]); // 変換後ファイル
        try (Stream<String> lines = Files.lines(from, Charset.forName("MS932")); // Shift_JISで読込
                PrintWriter writer = new PrintWriter(Files.newBufferedWriter(to, StandardCharsets.UTF_8))) { // UTF-8で出力
            lines.forEach(writer::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/05 14:52

    nakazawaken1様

    ご回答ありがとうございます。
    まず、申し訳ありません。
    私の質問が言葉足らずだった部分があり、誤解を招く形になっていたようです。
    今回はtxtファイル(ANSI)から読み出し、UTF8でデータベースに格納する
    といった処理を考えていたので、ファイルの変換ではありませんでした。
    しかしながらコードの書き方の部分で大変参考になりました。
    私も綺麗なコードが書けるようになりたいものです。

    ありがとうございました。

    キャンセル

+1

変換したいものがファイルである場合、javaのファイル入出力には文字コードを変換する機能がありますので、それを使います。

具体的にはnakazawaken1さんがコード記述されているので、ここでは省略します。

動作は次のようになります。

・ファイルから読むのと同時に、文字コード変換(シフトJis→javaの内部形式(UTF-16))して文字列として取得

・文字列(UTF-16)を文字コード変換(UTF-16→UTF-8)しながらファイルに書き出す


さて、質問者さんが調べた、

byte[] bytes1 = str.getBytes("ANSI");
String newStr1 = new String(bytes1, "UTF-8");

ですが、変換前後の文字列の使用をメモリ内だけで完結するようなケースで使います。
今回は、変換した文字列はファイルに書き出すだけなので、使用する必要はありません。
入出力処理の内部的に使っているので、あえて自分のプログラムでは使わなくてもいいと考えていいと思います。

それと、一応言っておくと、この2つの使い方が逆です。

byte[] bytes1 = str.getBytes("MS932");
文字列(内部コード)をMS932に変換したバイナリデータにする

String newStr1 = new String(bytes1, "UTF-8");
バイナリデータをUTF-8と判断して、文字列化(UTF-16)にする

という意味になりますので。

ちなみに、これらをどうしても使うなら、

1.テキストファイル(MS932)をバイナリデータとしてバイト配列(byte1)に読み込む
2.読み込んだbyte配列を、new String(bytes1, "MS932");で文字列化(str)する
3.文字列(str)をstr.getByte("UTF-8")でutf-8のバイト配列(byte2)に変換する
4.byte2をバイナリデータとしてファイルに出力する

となります。

「ファイルの入出力で行う」というのは、入力処理で1と2を、出力処理で3と4を同時に行っているということになります。

ちょっと、説明がグダグダになったような気もしますが、参考までに。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/05 15:05

    ngsvx様

    なるほど…
    byte[] bytes1 = str.getBytes("ANSI");
    String newStr1 = new String(bytes1, "UTF-8");
    の2行はメモリの中だけの処理になるのですね。

    参照したURLはUTF16への変換についてのHPだった為、
    処理の順番がおかしくなってしまったいたようです。

    何気なく動かしているファイル入出力の中ではこんなことがおこっていたのですね。
    また新たな知識を獲得することができました。
    それぞれの行の詳細な動きも説明していただきありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る