java + gradle コンパイルエラー”unmappable character (0x81) for encoding windows-1252”

起きたこと

以下のように日本語(ひらがな)のcharを表示してみようとしたら、タイトルにあるコンパイルエラーが発生した。

System.out.print('あ');

どうやら自分が使っているマシンが英語版のWindowsのため、文字コード(で言葉合ってるのかいつもわらかなくなる)がwindows-1252らしい(Latain-1の拡張的な?)。これに起因したエラーのよう。 テーブル見てみると0x81 はたしかに定義されてないかった。こんな歯抜けあるだなぁ。

起きていることは、本当はUTF-8エンコーディングしたファイルなのに、コンパイル環境(今回はintelliJ + Gradle)がwindows-1252でコンパイルしようとしている?みたいな感じだと思われる。

Complete Character List for windows-1252

やったこと

javacでコンパイルしてみる

とりあえず、原因切り分けのため、windowsターミナル上でjavacコマンドでコンパイルしてみた。

この場合はエラーが発生せずにコンパイル成功してしまった。実行もできて、まごうことなき「あ」が表示された。

以下のstackoverflowの回答にある通りに、 encoding オプションをつけたら通るようになったりするのかなと思ったが、何もつけずに通ってしまった。

"unmappable character for encoding" warning in Java

でもよく考えたら、Window terminalといってもUbuntu環境だったので、デフォルトのencodingがutf8だったので通っていた。-encodingつけない場合はプラットフォームに依存するようなので、まあそのとおりの結果だった。

❯ echo $LANG
C.UTF-8
-encoding encoding
Specifies character encoding used by source files, such as EUC-JP and UTF-8. If the -encoding option is not specified, then the platform default converter is used.

intelliJでjavacコンパイルオプションつけてみる 🙅🏻

設定でJavac Options欄のAdditional command line parametersに追加してみたが、結果変わらず。

どうやらGradleビルドのコンパイルにはこの設定は反映されないようだった。残念。

build.gradleにコンパイルオプションつけてみる 🙆🏻‍♂️

以下のissueに書いてある方法でutf8を指定するとコンパイルエラーは解消されましたとさ。

ただ、実行するとintelliJのターミナルで「あ」が表示されないので、あとはintelliJも何かしらの設定が必要みたい。だるい。

https://github.com/shekhargulati/strman-java/issues/99

IntelliJの設定変更 🙆🏻‍♂️

以下の記事のとおり、Dfile.encoding=UTF-8を設定し再起動後にもう一度実行すると、「あ」が表示された。

https://www.purin-it.com/spring-boot-crud-3#:~:text=解決方法,起動すれば解決できる。

文字コード系っていろんなところで設定されていて、どこが効いているのかよくわからないから、だるいです。

nil interfaceメモ

nil interface

typeとvalueがnilの場合のみ、interface型の変数はnilと判定される(nil interface)

実験

var s *string // #1
fmt.Printf("s -> Type:[%T] Value:[%v] 
", s, s)
fmt.Println(s == nil)

var i interface{} // #2
fmt.Printf("i -> Type:[%T] Value:[%v] 
", i, i)
fmt.Println(i == nil)

i = s // #3
fmt.Printf("i -> Type:[%T] Value:[%v] 
", i, i)
fmt.Println(i == nil)
# type value nil判定結果
1 *string nil true
2 nil nil true
3 *string nil false
  • たしかに2は、typeとvalueが両方nilなので、nilと判定されている
  • 3はvaluenilにも関わらず、non nilと判定されている

ポイント

  • interface型の変数がnilであることは、その変数のvaluenilかどうかを示しているわけではない
  • もしinterface型の変数の値がnilかどうか判定したい場合はReflectionを使わなければならない

メモ

ToDo

  • interface型の変数にconcrete typeの値を代入した場合、underlying typeとunderlying valueはconcrete typeの値のtypeとvalueで合っているか確認

参考

Understanding nil Interfaces and Interfaces with nil Values in Go

Antで学ぶJavaビルド

自分の会社のJavaプロジェクトでAntが使用されていたので、プロジェクトをビルドするためにAntをざっと勉強した際のメモ。 Javaの3大ビルドツール Ant, Maven, Gradleのうちの1つだが、最近では理由がない限り使われないと思う。 ただ、ビルドツールを理解するうえでは、Antを勉強するだけでも十分基本概念を勉強できると思う。 実務ではビルドツールをある程度理解していることは必須知識になるので、Javaの簡単なビルドと同じことをビルドツールで再現してみることで、一般的なビルドの流れを把握しておくことはとても有益だと思う。

ドキュメント

公式ドキュメント

Apache Ant™ User Manual

体系的なチュートリアル

Apache ANT Tutorial

参考記事

【超初心者向け】Ant超入門 - Qiita

Antについてメモ

  • makeファイルのようなもの

    Apache Ant is a Java-based build tool. In theory, it is kind of like make , without make 's wrinkles.

  • makeファイルとは異なりshellコマンドベースではなく、xmlベース
  • target treeを呼び出して実行される

インストール

  • 環境
  • JDKインストール
    1. sudo apt update
    2. sudo apt install openjdk-11-jdk
    3. java -version
    4. .zshrcにJAVA_HOMEを追加

        # Java
        export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
      
    5. echo $JAVA_HOME

  • Antインストール

    前提:JDKをインストール済みで、JAVA_HOMEが設定済み

    Apache Ant™ User Manual

    1. Antのバイナリをインストールする

      Binary Distributions

    2. ダウンロードした圧縮フォルダを解凍し、任意のディレクトリに格納する

    3. 2.のディレクトリのパスでANT_HOMEを追加

        export ANT_HOME="${HOME_DIR}/apache-ant-1.10.12-bin/apache-ant-1.10.12/"
      
    4. さらにPATHに追加

        export PATH=$PATH:$ANT_HOME/bin
      
    5. Ant task実行に必要なライブラリをインストールしておく。ANT_HOME/lib配下に格納される。(systemを指定した場合)

        cd $ANT_HOME
        ant -f fetch.xml -Ddest=system
      

Buildファイル(build.xlm)

以下のUsing Apache Antのページ

Apache Ant™ User Manual

  • Projects

    • name default basedir の3つのプロパティを持つ
      • name :プロジェクト名
      • default :target指定ない時に実行されるデフォルトタスク
      • basedir相対パスのルートとなるディレクトリ。指定しない場合はbuildファイルがあるディレクト
  • Targets

    • tasksとdatatypesのコンテナ
    • taskは他のtaskに依存し、antは現在のtaskの前に依存taskが実行されることを保証する
    • 依存チェーンで重複で依存している場合でも、一回しか呼び出されないようになっている。
      • A → B → C → Dの場合は、Dを実行するためにCを実行し、その際にCの依存関係としてB,Aが実行されて、Dの依存関係に戻るが、さらにそこからB,Aを実行するということはない。

          A → B → C → D
          A → B → C
          A → B
          A
        
  • Tasks

    ↑のマニュアルの「Ant Tasks」のページ → 各タスクのドキュメント

    <task名 attribute1="*value1*" attribute2="*value2*" ... />

    ユニークな任意のidを割り当てて、参照することもできる

    <*taskname* id="*taskID*" ... />

  • Properties

    • カスタムフィールド

チュートリアル

Hello World with Apache Ant

まず手動でコンパイルしてクラスファイル作成し、実行してみる。この流れをantのbuild.xlmに定義してantのビルドで同じことを再現してみる。

Javaコマンドで実施

  1. 以下のディレクトリ構造を用意し、javaファイルを用意する

     .
     ├── build
     │   ├── classes
     │   └── jar
     └── src
         └── oata
             └── HelloWorld.java
    
  2. javaファイルを適当に用意

     package oata;
    
     public class HelloWorld {
         public static void main(String[] args) {
             System.out.println("Hello World");
         }
     }
    
  3. コンパイルしてclassファイルを作成

     javac -sourcepath src -d build/classes src/oata/HelloWorld.java
    
  4. 実行してみる

     java -cp build/classes oata.HelloWorld
    
     Hello World
    
  5. さらにjarファイルも作成してみる。まずはmanifestファイルを用意。

     echo Main-Class: oata.HelloWorld>myManifest
    
  6. jarファイルを作成

     jar cfm build/jar/HelloWorld.jar myManifest -C build/classes .
    
  7. jarファイル実行

     java -jar build/jar/HelloWorld.jar
    

    出力

     Hello World
    

ここまでのディレクトリ構成

.
├── build
│   ├── classes
│   │   └── oata
│   │       └── HelloWorld.class
│   └── jar
│       └── HelloWorld.jar
├── myManifest
└── src
    └── oata
        └── HelloWorld.java

Antで実施

  1. ルートにbuild.xlmを用意

     touch build.xml
    
  2. build.xlmにproject, targe, taskを定義

    target

    • clean
    • compile
    • jar
    • run
     <project>
    
         <target name="clean">
             <delete dir="build"/>
         </target>
    
         <target name="compile">
             <mkdir dir="build/classes"/>
             <javac srcdir="src" destdir="build/classes"/>
         </target>
    
         <target name="jar">
             <mkdir dir="build/jar"/>
             <jar destfile="build/jar/HelloWorld.jar" basedir="build/classes">
                 <manifest>
                     <attribute name="Main-Class" value="oata.HelloWorld"/>
                 </manifest>
             </jar>
         </target>
    
         <target name="run">
             <java jar="build/jar/HelloWorld.jar" fork="true"/>
         </target>
    
     </project>
    
  3. antのtaskを実行する

     ant compile
     ant jar
     ant run
    
  4. 一括で実行もできる

     ant compile jar run
    

build.xmlを修正する

いくつかの問題を改善する

  1. 同じディレクトリ名(src, build, build/classes)を複数回使っている

     <!-- 複数回参照するディレクトリ名をプロパティ化 -->
     <property name="src.dir" value="src"/>
     <property name="build.dir" value="build"/>
     <property name="classes.dir" value="${build.dir}/classes"/>
     <property name="jar.dir" value="${build.dir}/jar"/>
    
  2. main class, jarファイル名がハードコーディング

     <!-- project名をjarファイル名と合わせるようにすると便利 -->
     <project name="HelloWorld" basedir="." default="main">
    
     <!-- main class名、jarファイル名のハードコーディングを避けるためプロパティ化 -->
     <property name="main-class" value="oata.HelloWorld"/>
    
  3. ターゲットの実行順序を覚えていないといけない(ant compile jar run)

    main: clean → (compile → jar → run)

     <!-- ディレクトリ名をプロパティ参照に変更 -->
     <target name="compile">
         <mkdir dir="${classes.dir}"/>
         <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
     </target>
    
     <!-- ディレクトリ名をプロパティ参照に変更 -->
     <!-- dependsで依存関係を追加 -->
     <target name="jar" depends="compile">
         <mkdir dir="${jar.dir}"/>
         <jar destfile="${jar.dir}/${ant.project.name}" basedir="${classes.dir}">
             <manifest>
                 <attribute name="Main-Class" value="${main-class}"/>
             </manifest>
         </jar>
     </target>
    
     <!-- ディレクトリ名をプロパティ参照に変更 -->
     <!-- dependsで依存関係を追加 -->
     <target name="run" depends="jar">
         <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
     </target>
    
     <target name="clean-build" depends="clean,jar"/>
    
     <!-- antのデフォルトtarget -->
     <target name="main" depends="clean,run"/>
    

以上の修正を行うことで、ant を実行するだけでデフォルトのmainが実行され、compile jar runの順序でtargetが実行される

antコマンド

ant [options] [target [target2 [target3] ...]]
Options:
  -help, -h              print this message and exit
  -projecthelp, -p       print project help information and exit
  -version               print the version information and exit
  -diagnostics           print information that might be helpful to
                         diagnose or report problems and exit
  -quiet, -q             be extra quiet
  -silent, -S            print nothing but task outputs and build failures
  -verbose, -v           be extra verbose
  -debug, -d             print debugging information
  -emacs, -e             produce logging information without adornments
  -lib <path>            specifies a path to search for jars and classes
  -logfile <file>        use given file for log
    -l     <file>                ''
  -logger <classname>    the class which is to perform logging
  -listener <classname>  add an instance of class as a project listener
  -noinput               do not allow interactive input
  -buildfile <file>      use given buildfile
    -file    <file>              ''
    -f       <file>              ''
  -D<property>=<value>   use value for given property
  -keep-going, -k        execute all targets that do not depend
                         on failed target(s)
  -propertyfile <name>   load all properties from file with -D
                         properties taking precedence
  -inputhandler <class>  the class which will handle input requests
  -find <file>           (s)earch for buildfile towards the root of
    -s  <file>           the filesystem and use it
  -nice  number          A niceness value for the main thread:
                         1 (lowest) to 10 (highest); 5 is the default
  -nouserlib             Run ant without using the jar files from
                         ${user.home}/.ant/lib
  -noclasspath           Run ant without using CLASSPATH
  -autoproxy             Java1.5+: use the OS proxy settings
  -main <class>          override Ant's normal entry point

桁の扱いメモ

n桁目の取得などのメモ

数値 → 文字列に変換して扱う

string s = to_string(num);

桁数の取得

string s = "1234";
int n = s.length();
cout<< n; // 4

n桁目を取得

charの演算を利用(asciiで0からの差分 + int変換に変換)

string s = "1234";
int n = s[0] - '0';
cout<< n; // 1

n桁目とm桁目をスワップ

swap関数を使ってスワップ。元の値をスワップ

string s = "1234";
swap(s[0], s[3]); 
int n = stoi(s); //必要であればintに変換
cout<< n; // 4231

数値のまま扱う

桁数の取得

基数で割っていく。商が0になるまで割る回数が桁数。

int x = 1234;

int numDigit;
while (x > 0)
  {
    x = x / 10;
    numDigit++;
  }
cout << numDigit; //4

n桁目を取得

digit_n = (x / 10n-1) % 10

右シフトして基数で割った余り

int x = 987654321;
int n = 9;

int shift = x / pow(10, n-1);
int digit = shift % 10;
cout << digit; //9

n桁目とm桁目をスワップ

引いて足す

int x = 1234;
int n = 1;
int m = 4;

//桁数
int numDigit = 4;
//n桁目
int a = 4;
//m桁目
int b = 1;

int y = x - b * pow(10, m-1) - a * pow(10, n-1) + a * pow(10, m-1) + b * pow(10, n-1);   
cout << y; //4231

C++単体テストGoogle Testのメモ

環境

  • Ubuntu 20.04@WSL2

      ❯ lsb_release -a
      No LSB modules are available.
      Distributor ID: Ubuntu
      Description:    Ubuntu 20.04.3 LTS
      Release:        20.04
      Codename:       focal
    
  • C++ compiler: g++

      ❯ g++ --version
      g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
      Copyright (C) 2019 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions.  There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

やること

Google Testを使ったC++単体テスト。(初心者はビルドツールはBazelを使うことを推奨している)

If you’re using GoogleTest for the first time or need a refresher, we recommend this tutorial as a starting point.

Quickstart: Building with Bazel

Bazelのインストール

Bazelを直接インストールのではなく、Bazeliskというラッパーをインストールする。go install, npm installらへんでインストールするのが楽

npm install -g @bazel/bazelisk

bazelisk/README.md at master · bazelbuild/bazelisk

npmでインストール後は、 bazel baseliskシンボリックリンクが追加されていた

~/.nvm/versions/node/v15.3.0/bin ※PATHがすでに通っている
bazel -> ../lib/node_modules/@bazel/bazelisk/bazelisk.js ※インストール後追加されていた
bazelisk -> ../lib/node_modules/@bazel/bazelisk/bazelisk.js ※インストール後追加されていた

Bazel workspaceの用意

テストコードを追加するローカルリポジトリをBazel workspaceとする

  1. まず、このworkspace内で使用するBazelのバージョンを指定するために、リポジトリのroot直下に .bazelversion ファイルを用意する。

     cd ${workspaceとするディレクトリのパス}
     echo '5.1.0' > .bazelversion
    
  2. Google Testの依存を追加するために、WORKSPACEファイルを追加する。

     load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
    
     http_archive(
       name = "com_google_googletest",
       urls = ["https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip"],
       strip_prefix = "googletest-609281088cfefc76f9d0ce82e1ff6c30cc3591e5",
     )
    

これでGoogleTestの依存関係を追加できているはずなので、GoogleTestをインクルードしたテストコードをビルドすることができる。

テストコードを用意して、ビルド実行

  1. テストコード追加

     #include <gtest/gtest.h>
    
     // Demonstrate some basic assertions.
     TEST(HelloTest, BasicAssertions) {
       // Expect two strings not to be equal.
       EXPECT_STRNE("hello", "world");
       // Expect equality.
       EXPECT_EQ(7 * 6, 42);
     }
    
  2. BUILD ファイルを追加

     cc_test(
       name = "hello_test",
       size = "small",
       srcs = ["hello_test.cc"],
       deps = ["@com_google_googletest//:gtest_main"],
     )
    
  3. ビルド実行

    //以降は、BUILDファイルのWorkspaceのルートからの相対パス

     bazel test --test_output=all //:hello_test
     bazel test --test_output=all //${BUILDファイルのパス}:${name}
    

    Build Tutorial - C++

Googletestの書き方

Googletest Primer

テストを書く前に

用語

googletestはISTQBなどで定義される、一般的な用語の定義とは違うので、注意が必要

Meaning googletest term ISTQB term
grouping related tests Test Case Test Suite
Exercise a particular program path with specific input values and verify the results Test Test Case

Assertions

googletest assertionsはマクロ

Assertions Description
ASSERT_* generate fatal failures when they fail, and abort the current function
EXPECT_* generate nonfatal failures, which don’t abort the current function.

Assertions Reference

Assertions Reference

シンプルなテストを書く

フォーマット

  • 第一引数:the name of the test suite
  • 第二引数:the test’s name within the test suite

※どちらもC++で有効なidentiferで、_ は使えない

TEST(TestSuiteName, TestName) {
  ... test body ...
}

自分はこういう風にした

インクルードが面倒でよくわからなかったので、パッケージごとにBUILDファイル用意。パッケージのディレクトリ直下に必要なファイルをすべて用意。

  1. lib配下にパッケージのディレクトリを作成

     cd lib
     mdkir Math ※パッケージ名の例
    
  2. 問題を解いたあとに、スニペットにしておきたいと思ったものを、関数にしておく

     touch digit.hpp
    
  3. 2.の単体テストコードを用意

     touch digitTest.cpp
    
  4. 作成したパッケージのディレクトリ内にBUILDファイルを用意

     touch BUILD
    
     cc_test(
       name = "lib_math_test", ※任意の名前。実行する際に使う
       size = "small",
       srcs = ["digit.hpp","digitTest.cpp"], ※テストコードと、テストコードがインクルードするファイルを記載
       deps = ["@com_google_googletest//:gtest_main"],
     )
    
  5. テスト実行

     bazel test --test_output=all //lib/Math:lib_math_test ※Mathパッケージ配下のテストコードを実行する
    
  6. 完成したらVSCode用にスニペットにしておく

    snippet generator

ディレクトリ構成

lib
└── Math ※パッケージ
    ├── BUILD ※パッケージごとに用意
    ├── digit.h ※実装ファイル(今回はテンプレート関数だったので、実装もヘッダファイルで宣言。なのでヘッダファイルのみ)
    └── digitTest.cpp ※テストコード

テンプレート関数のテストを書く時に参考にした記事

テンプレートのヘッダを用意した場合の注意点。テンプレートはヘッダに宣言だけでなく実装も定義する。

C++ テンプレート ヘッダと実装を別にした時に起こること - Qiita

その他

Google C++ スタイルガイド 日本語全訳

ジョブ型雇用

4年程前に大手SIerを退職した時は理由を上手く言葉にできなかったが、昨今のジョブ型雇用の流れで、やっと自分が何を危惧して退職したのかを明確に気づけたので文章に残しておく。

ジョブ型雇用とは 職務明確化、専門性高める

当時自分が一番感じていたのは、ジョブ型雇用の世界では生き残れないだろうという危機感だったのだと思う。

あの時はジョブ型雇用という言葉さえ知らなかったので、漠然としか想像していなかったけど、実際にこのコロナ禍で急速に前職の会社や業界がジョブ型雇用になっている現実を目の当たりにして、自分の中にあった不安感が具現化してきた。これだったんだなと思った。

もし、あのプロジェクトに残っていたら、メンバーシップ型の世界からは出れなかったと思う。あの環境ではスキルをスタックしていくことができない可能性が高かった。このことに一番危機感を感じていた。スキルを蓄積できないことへの不満は当時もしっかり自分の中で認識していた。

今は、実務もプラベートな時間でもスキルをスタックできる環境に近づいたので、働き方としては理想に近づけたと思う。まだジョブ型雇用で生き残るほどジョブ型人材ではないが、スキルを蓄積していく循環を用意できた点に関しては、現時点ではギリギリ合格点だなと感じている。

あの時の決断は現時点の評価としてはそんなに悪い決断ではなかったのかなと思うようになった。とはいえ、大手でのほほんと生活できていた環境と比べるとどちらがよいかは本当に個人の価値観によると思う。

2021/2022

年越し1時間前に書いた

2021

総括

  • Golangをしっかり勉強できた気がする
  • さらに実用的な知識、モダンな設計の知識が欲しいと思った
  • 英語..

Golang 😃

  • Learning Goを少しづつ読み進めた。この本のおかげでだいぶGoの基礎力は固まったと思う
  • Learning Goだけでいいかなと思ってたけど、年末にメルカリのGoの動画も見始めたらこっちもよかった。何より日本語なのはやっぱり楽。

Learning Go: An Idiomatic Approach to Real-World Go Programming (English Edition)

Java/SpringBoot 😰

  • JavaというかSpringBootだが、あまりカバーできなかった
  • 腰を据えて勉強をとなるとかなり時間がかかりそうなので、優先度的にも今年はGoを優先した

Blockchain関連 🙂

  • 業務上必要な知識はある気がするが、node立ててなにかアプリケーション作成くらいできればよかったかな

インフラ(AWS/Docker)🥺

  • AWSの資格は取得しておきたいかなと思うけど、優先度が低くて結局なにもやってない
  • 前職の知識でなんとかなってしまっているというのもある

アルゴリズムデータ構造 🙂

  • Golangの勉強も一段落したので、C++で久々に再開
  • 再開しただけでもえらい

問題解決力を鍛える!アルゴリズムとデータ構造 (KS情報科学専門書)

英語 😰

  • やっぱりまだまだ。今年はReading, Listeningの基礎に立ち返った

2022目標

  • スコープを少し広げてソフトウェアの設計周りの知識と実際サンプル実装してみる
  • Golangは並列処理もカバー。実践レベルまで完成させる。(チームでトップになる)

設計(ソフトウェア)

  • この記事で紹介されている本を読み始めるところから始める

ドメイン駆動設計からオブジェクト指向、そしてアジャイル開発まで。関連書籍練り歩きのススメ

Java/SpringBoot

  • ↑の設計と合わせてSpringBootの設計思想を見ていきたい

アルゴリズムデータ構造

  • 基本的なデータ構造までは復習

新しい言語

  • 以下の古い記事より。古いが本質だと思うので特に色褪せない。(挙げられてる言語や技術については古いかもれしれないが) https://www.yamdas.org/column/technique/21-daysj.html
  • 今年は★のついたものをカバーしたい。
  • 関数抽象
  • 構文抽象
  • ★coroutine -> Kotlinとかでいいかも https://en.wikipedia.org/wiki/Coroutine#Implementations
  • ★並列処理(Concurrency) -> Go, イベントループはJSでよいか

    少なくとも半ダースのプログラミング言語を学ぶこと。そのうちの一つはクラス抽象をサポートするもの(例えば JavaC++)、一つは関数抽象をサポートするもの(例えば Lisp や ML)、一つは構文抽象をサポートするもの(例えば Lisp)、一つは宣言的記述をサポートするもの(例えば PrologC++ テンプレート)、一つは coroutine をサポートするもの(Icon や Scheme)、そして一つは並列処理をサポートするもの(例えば Sisal)であること。

www.amazon.ca

www.amazon.ca

数学

  • 日本帰ったら統計検定1級受けてみたいけど、たぶん優先度低くて何もしなそう

レーニン

  • トムホ目指したい。あとジムまた行きたいな