[Java] 52 JavaFX 10 文字コード変換ツール その3 完成

一応完成しました。

昨年2020年9月にGUIアプリではないですがPython&Bashで同じことができるコードを書いています。nkfコマンドを使えばファイルの文字コード変換は楽勝です。

こちらの方の難易度は桁違いに高かったです。

あとはjar実行ファイルに変換して普段使いできるようにします。

先月下旬にVScodeでのJavaツール開発を諦めてEclipseへ移行してから1ヶ月余り。ようやくVScodeでも開発できるようになりました。XcodeやEclipseのようなIDEはなにか窮屈で書かされている感もあり苦手です。

package javafx_conv;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import java.util.regex.Pattern;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class GuiController {
    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private TextField text1;
    @FXML
    private TextField text2;

    String path;
    String path2;
    String code;
    String code2;

    @FXML
    void onButton1Action(ActionEvent event) {
        code2 = text2.getText();
        System.out.println(code2);

        path2 = text1.getText();
        System.out.println(path2);

        if(code2.matches("UTF-8")){
            // 変換後のファイル名を作成
            StringBuffer buf = new StringBuffer();
            buf.append(path2.split(Pattern.quote("."))[0]);
            buf.append("_s.");
            buf.append(path2.split(Pattern.quote("."))[1]); // 拡張子
            String s = buf.toString();
            System.out.println(s);

            // ファイルの文字コードをUTF-8からシフトJISに変換
            File file = new File(path2);
            File file2 = new File(s);
            try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file2),"Shift-JIS"))){
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(line);

                    bw.write(line);
                    bw.newLine();
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        else{
            if(code2.matches("SHIFT_JIS")){
                // 変換後のファイル名を作成
                StringBuffer buf = new StringBuffer();
                buf.append(path2.split(Pattern.quote("."))[0]);
                buf.append("_u.");
                buf.append(path2.split(Pattern.quote("."))[1]); // 拡張子
                String s = buf.toString();
                System.out.println(s);

                // ファイルの文字コードをシフトJISからUTF-8に変換
                File file = new File(path2);
                File file2 = new File(s);
                try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file),"Shift-JIS"));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file2),"UTF-8"))){
                    String line;
                    while ((line = br.readLine()) != null) {
                        System.out.println(line);

                        bw.write(line);
                        bw.newLine();
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            else{
                text2.setText("");
                text2.setText("このツールでは不可");
            }
        }
    }

    @FXML
    void onButton2Action(ActionEvent event) {
        path = text1.getText();
        FileCharDetector fd = new FileCharDetector(path);

        try{
            code = fd.detector();
        }
        catch (Exception e) {
            System.out.println(e);
        }

        if (code != null){
            text2.setText(code);
        }
        else{
            text2.setText("判定不可");
        }

    }
}

[Java] 51 JavaFX 09 文字コード変換ツール その2

前回の続きです。

文字コードの判定でもしんどかったですが、ファイルの文字コード変換は輪をかけて手ごわかったです。

区切り文字をドット(.)にして文字列をすんなり分割できないというのにはまいりました。まさかsplitで足止めを喰らうとは思いませんでした。

Pythonの感覚で取り組むとひどい目にあいますね。数行書いてビルドするのではなく、1行あるいは1箇所書き足す度に確認しないと今の私には手に負えないです。緻密さを養うには最適な言語ではないでしょうか。

これだけ苦労しても嫌いにならないのは、ドキュメントなりネットなりどこかに答えが必ずあるという安心感と泥臭く取り組んできた思い入れゆえでしょうか。情報の少ないSwiftだったらとっくに投げ出していたでしょう。

package javafx_conv;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import java.util.regex.Pattern;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class GuiController {
    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private TextField text1;
    @FXML
    private TextField text2;

    String path;
    String path2;
    String code;
    String code2;

    @FXML
    void onButton1Action(ActionEvent event) {
        code2 = text2.getText();
        System.out.println(code2);

        path2 = text1.getText();
        System.out.println(path2);

        if(code2.matches("UTF-8")){
            // 変換後のファイル名を作成
            StringBuffer buf = new StringBuffer();
            buf.append(path2.split(Pattern.quote("."))[0]);
            buf.append("_s.csv");
            String s = buf.toString();
            System.out.println(s);

            // ファイルの文字コードをUTF-8からシフトJISに変換
            File file = new File(path2);
            File file2 = new File(s);
            try (BufferedReader br = new BufferedReader(new InputStreamReader\
(new FileInputStream(file),"UTF-8"));BufferedWriter bw = new BufferedWriter\
(new OutputStreamWriter(new FileOutputStream(file2),"Shift-JIS"))){
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(line);

                    bw.write(line);
                    bw.newLine();
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        else{
            if(code2.matches("SHIFT_JIS")){
                System.out.println("省略");
            }
            else{
                System.out.println("該当なし");
            }
        }
    }

    @FXML
    void onButton2Action(ActionEvent event) {
        path = text1.getText();
        FileCharDetector fd = new FileCharDetector(path);

        try{
            code = fd.detector();
        }
        catch (Exception e) {
            System.out.println(e);
        }

        if (code != null){
            text2.setText(code);
        }
        else{
            text2.setText("判定不可");
        }

    }
}

[Java] 50 JavaFX 08 文字コード変換ツール

GUI作成についてはPythonのtkinterからJavaFXへの移行を目指しています。

やはりScene Builderを使ってGUIをデザインするのが楽ですから。

今は文字コード変換ツールを作成しています。

文字コード判定のところまで書きました。文字コード判定クラスFileCharDetectorはネットから拝借して私なりに手を入れました。

早速TextAreaの高さをTextFieldに合わせられない不具合が発生していますが後日サイズ調整します。

package javafx_conv;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.event.ActionEvent;

public class GuiController {

    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private TextField text1;
    @FXML
    private TextArea textarea1;

    String path;
    String code;

    @FXML
    void onButton1Action(ActionEvent event) {
        System.out.println("変換ボタンを押しました!");
        // 作成中
    }

    @FXML
    void onButton2Action(ActionEvent event) {
        path = text1.getText();
        FileCharDetector fd = new FileCharDetector(path);

        try{
            code = fd.detector();
        }
        catch (Exception e) {
            System.out.println(e);
        }

        if (code != null){
            textarea1.setText(code);
        }
        else{
            textarea1.setText("判定不可");
        }

    }
}

参考サイト

[Java] 49 JavaFX 07 JDK11.0.11で文字化け

[macOS 10.15.7 Catalina]

Javaがらみではちょっとしたことでトラブルに巻き込まれるので、都度記録しておきます。

今回はJDKのバージョンによるJavaFXの文字化けです。

VScodeのsettings.jsonでJDKのバージョンを以下のように設定していました。

"java.home": "/Library/Java/JavaVirtualMachines/jdk-11.0.8.jdk/Contents/Home"

jdk-11.0.8.jdkからjdk-11.0.11.jdkに変更したところ文字化けが発生しました。ちなみにjdk13ではこのようなトラブルはありません。

私の環境だけかもしれませんが、今後も似たようなトラブルになった時のためにメモしておきます。前にもあったmacOS 10.15 Catalinaの問題のような気もします。

Javaは開発環境構築とトラブル対応のコストがかかり過ぎるがつくづく残念です。この傾向はEclipseのようなIDEでも、VScodeのようなエディタでも変わりません。

以前も書きましたが、VScodeを使った軽量環境構築はJava用プロジェクト管理ツールのMavenに出会うまでは本当に泥沼でした。

[Java] 48 Build failed, do you want to continue?

VScodeでJavaコードをビルドしようとするとタイトルのエラーが出ました。

Proceedボタンで先に進むことはできますが、どうやってもこのエラーを消せなくて困りました。

ネット検索すると、workspaceStorageフォルダを空にすればよいとか解決策が紹介されていたので試してみるもうまくいかず。

コマンドパレットから”Java: Open Java Language Server log file”でログを確認すると以下の詳細メッセージがありました。

ENTRY org.eclipse.jdt.ls.core 4 0 2021-07-30 08:47:22.370
!MESSAGE Error occured while building workspace. Details: 
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._AppTest.java; line: 1
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._Main.java; line: 1
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._SampleController.java; line: 1

原因となっている3つのファイルを削除するとあっさり直りました。

同じエラーメッセージでも原因は様々なので、むやみにググらずログを確認して対処するべきでした。今回はざっくりとしすぎたメッセージゆえ尚更でしたね。

[Python] 293 引数が複数あるC言語モジュール

以前うまくいかなかったC言語モジュールを完成させました。

input(引数)とoutput(戻り値)を自在に設定でき、C言語実行ファイルに必須の処理終了検知が不要なので、こちらを常用することになりそうです。

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "fnv_function.c"

static PyObject* horse_id(PyObject* self, PyObject* args)
{
    const char* path;
    const char* name;
    uint32_t id;

    if (!PyArg_ParseTuple(args,"ss",&path,&name)){
        return NULL;
    }
    else{
        FILE *fp; // horse_listファイル
        int horseID[10]; // 1 horseID
        uint32_t horse_hash[20]; // 2 馬名ハッシュ
        char horse_name[100]; // 3 検索馬名
        char horse_name0[100]; // 4 馬名
        char status[10]; // 5 稼働
        char gender[10]; // 6 性別
        char hair[10]; // 7 毛色
        char birthday[100]; // 8 生年月日
        char trainer[100]; // 9 調教師
        char owner[100]; // 10 馬主
        char info[100]; // 11 募集情報
        char breeder[100]; // 12 生産者
        char area[50]; // 13 産地
        char price[50]; // 14 セリ取引価格
        char prize_money[50]; // 15 獲得賞金
        char result[50]; // 16 通算成績
        char wining_race[200]; // 17 主な勝鞍
        char relatives[200]; // 18 近親馬

        uint32_t horse_hash_input_int;
        uint32_t horse_hash_int;

        char buf[2000]; // fgets用

        int i=0; // 行番号
        int b=0; // 検索結果有無の識別

        fp = fopen(path, "r");

        while(fgets(buf,2000,fp ) != NULL ) {
            if (i != 0){
                sscanf(buf, " %[^,],%[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %s",horseID,horse_hash,horse_name,horse_name0,status,gender,hair,birthday,trainer,owner,info,breeder,area,price,prize_money,result,wining_race,relatives) ;

                horse_hash_int = atoi(horse_hash);
                horse_hash_input_int = fnv_1_hash_32(name);

                if(horse_hash_int - horse_hash_input_int == 0){
                    id = atoi(horseID);
                    b ++;
                    break;
                }
            }
            i ++ ;
        }
        if (b == 0){
            id = 100000000;
        }
        fclose(fp);

        return Py_BuildValue("I", id);
    }
}

static PyMethodDef Horseidmethods[] = {
    {"horse_id", (PyCFunction)horse_id, METH_VARARGS},
    {NULL,NULL,0}
};

static struct PyModuleDef horseid = {
    PyModuleDef_HEAD_INIT,
    "horseid",
    "Python3 C API Module(Sample 1)",
    -1,
    Horseidmethods
};

PyMODINIT_FUNC PyInit_horseid(void)
{
    return PyModule_Create(&horseid);
}

[Python] 292 CSVファイルの文字コード変換

とっくに記事にしていると思い込んでました。

今後も重宝するでしょう。

import pandas as pd

filename = 'horse.csv'
filename_new = 'horse_new.csv'

df = pd.read_csv(filename,encoding='shift_JIS')

with open(filename_new,mode = 'w',encoding='UTF-8') as f:
    df.to_csv(f,index=False)

[C言語] 07 数字の文字列をuint64_tに変換する

すぐに忘れそうなのでメモ書きしておきます。

第2引数&endの意味がよく分かりません。適当なポインタを入れておけば良いのでしょうか。

IBMやMicrosoftの資料によると、文字列の読み取りを停止した文字(ヌル文字など)へのポインタを&endに格納するとのことです。読み取り不可の場合は文字列をそこに格納します。char* end = NULLでも問題ないみたいです。

#include <stdlib>

uint64_t numA;
uint64_t numB;
char* end;

<中略>

// sscanfで文字列として読み込んだnumAを16進数のuint64_tへ変換する
numB = strtoull(numA,&end,16);

[Python] 291 C言語実行ファイルの併用 その5 ハッシュ関数 FNV 64bit

ハッシュ関数fnvの32bitで競走馬名ハッシュ値の衝突が生じたため、64bitハッシュ値での検索処理時間を計測してみました。

やはり32bitの55秒に対し77秒と遅くなりました。

ハッシュ値検索にこだわるならばFNVでは32bitで運用するしかないですが、同一誕生年内での衝突はないので私の使い方では今のところトラブルになることはありません。

ところで、Excelで表をまとめていて64bit以上の整数を正確に表示できない問題に遭遇しました。仕方ないので文字列扱いにして解決しました。

他にも小数点数と整数の区別ができない、UTF-8のCSVファイルが基本文字化けする、数字とハイフンがあると勝手に日付と解釈する、など何かとプログラマ泣かせのソフトです。

macOSのNumbersでは64bit整数でも問題なく表示できます。今はなきLotus1-2-3はどうだったのか少し気になります。

情報科学においてunsigned long long integerを”符号なし長長整数”などといった日本語に翻訳していないのが不思議です。

[Python] 290 引数ありのC言語モジュール ハッシュ関数 FNV 64bit

ハッシュ関数FNVの32bitで数件衝突が発生したため、64bitでも生成できるようにしました。

PythonのドキュメントにPy_BuildValueの引数について解説があり、unsigned long long intのフォーマットがKであることが分かりました。

unsigned intのフォーマットはiではなく大文字のIなので、32bitの方も修正しました。これでPython側での変換が不要になります。

#define PY_SSIZE_T_CLEAN
#include <Python.h>

extern uint32_t fnv_1_hash_32(const char*);
extern uint64_t fnv_1_hash_64(const char*);

static PyObject* fnv_32(PyObject* self, PyObject* args)
{
    const char* s;
    unsigned int hash=2166136261U;

    if (!PyArg_ParseTuple(args, "s", &s)){
        return NULL;
    }
    else{
        while (*s) {
        hash*=16777619U;
        hash^=*(s++);
        }

        return Py_BuildValue("I", hash);
    }
}

static PyObject* fnv_64(PyObject* self, PyObject* args)
{
    const char* s;
    unsigned long long hash=14695981039346656037U;

    if (!PyArg_ParseTuple(args, "s", &s)){
        return NULL;
    }
    else{
        while (*s) {
        hash*=1099511628211LLU;
        hash^=*(s++);
        }

        return Py_BuildValue("K", hash);
    }
}

static PyMethodDef fnvmethods[] = {
    {"fnv_1_hash_32", fnv_32, METH_VARARGS},
    {"fnv_1_hash_64", fnv_64, METH_VARARGS},
    {NULL,NULL,0}
};

static struct PyModuleDef fnv = {
    PyModuleDef_HEAD_INIT,
    "fnv",
    "Python3 C API Module(Sample 1)",
    -1,
    fnvmethods
};

PyMODINIT_FUNC PyInit_fnv(void)
{
    return PyModule_Create(&fnv);
}
from c_module import fnv

name_list = ['シャフリヤール']

for name in name_list:
    hash = fnv.fnv_1_hash_64(name)
    print(hash)
--------------------------------------------------

出力
--------------------------------------------------
7203286604922561048
#include <stdio.h>
#include <stdint.h>

uint32_t fnv_1_hash_32(char *s)
{
    unsigned int hash=2166136261U;

    while (*s) {
        hash*=16777619U;
        hash^=*(s++);
    }
    return hash;
}

uint64_t fnv_1_hash_64(char *s)
{
    unsigned long long hash=14695981039346656037U;

    while (*s) {
        hash*=1099511628211LLU;
        hash^=*(s++);
    }
    return hash;
}
from distutils.core import setup, Extension

setup(name='fnv',
    version='1.0',
    ext_modules=[Extension('fnv', sources = ['fnv.c','fnv_function.c'])]
)
<セットアップコマンド>

・自作ライブラリに配置するsoファイルを作成するコマンド "from c_module import fnv"
python setup.py build_ext -i

・既存のライブラリにインストールするコマンド "import fnv"
python setup.py install