- 投稿日:2020-11-16T19:03:09+09:00
FitGen.exeを日本語Windowsで実行したとき発生するJavaのエンコーディングに関するエラーを回避する
TL;DR
環境変数の
JAVA_TOOL_OPTIONS
に-Dfile.encoding=UTF-8
を設定する。詳細
GARMIN FIT SDKでカスタムメッセージ等を定義した場合、FitGen.exeを実行しFIT SDKを生成する必要がある。
https://developer.garmin.com/fit/cookbook/fitgen/しかし、最近Garmin Developersで配布されているFIT SDKのEventMesg.javaのドキュメントコメントに謎の文字化けが混入しておりそのままFitGen.exeを実行すると
> .\FitGen.exe -o test -java FIT Code Generator - Protocol 2.00 Profile 21.40Release SDK Version: production/akw/21.40.00-0-g813c158 Parsing existing config.csv... Writing new config.csv... Generating Java code... Building Java tools...0% com\garmin\fit\EventMesg.java:679: エラー: この文字は、エンコーディングMS932にマップできません * Comment: Theテつ?firstテつ?byteテつ?isテつ?theテつ?radar_threat_level_max, theテつ?secondテつ?byteテつ?isテつ?theテつ?radar_threat_count, and theテつ?lastテつ?16テつ?bitsテつ?areテつ?reservedテつ?forテつ?futureテつ?useテつ?andテつ?shouldテつ?beテつ?setテつ?toテつ?FFFF. ^ 以下略というエラーが発生し、ビルドに失敗する。
この文字は、エンコーディングMS932にマップできません
というエラーはjavacにUTF-8等適切なエンコーディングを渡してやることで回避することが可能。以下のようなオプションを渡すことで回避できる。javac -encoding UTF-8 EventMesg.javaしかしFitGen.exeの場合、javac呼び出し部分はFitGen.exe内部で行われているので直接javacにオプションを渡すことができない。
そこでFitGen.exeを実行するWindowsのユーザー環境変数にJAVA_TOOL_OPTIONSを追加し、javacに渡したいオプションを指定することでエラーを回避することができる。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/envvars002.html
https://qiita.com/n_slender/items/6c566bb345e844ba8127
- 投稿日:2020-11-16T18:28:54+09:00
FitGen.exeを日本語Windowsで実行したとき発生するJavaのエンコーディングに関するエラーを回避する
TL;DR
環境変数の
JAVA_TOOL_OPTIONS
に-Dfile.encoding=UTF-8
を設定する。詳細
GARMIN FIT SDKでカスタムメッセージ等を定義した場合、FitGen.exeを実行しFIT SDKを生成する必要がある。
https://developer.garmin.com/fit/cookbook/fitgen/しかし、最近Garmin Developersで配布されているFIT SDKのEventMesg.javaのドキュメントコメントに謎の文字化けが混入しておりそのままFitGen.exeを実行すると
> .\FitGen.exe -o test -java FIT Code Generator - Protocol 2.00 Profile 21.40Release SDK Version: production/akw/21.40.00-0-g813c158 Parsing existing config.csv... Writing new config.csv... Generating Java code... Building Java tools...0% com\garmin\fit\EventMesg.java:679: エラー: この文字は、エンコーディングMS932にマップできません * Comment: Theテつ?firstテつ?byteテつ?isテつ?theテつ?radar_threat_level_max, theテつ?secondテつ?byteテつ?isテつ?theテつ?radar_threat_count, and theテつ?lastテつ?16テつ?bitsテつ?areテつ?reservedテつ?forテつ?futureテつ?useテつ?andテつ?shouldテつ?beテつ?setテつ?toテつ?FFFF. ^ 以下略というエラーが発生し、ビルドに失敗する。
この文字は、エンコーディングMS932にマップできません
というエラーはjavacにUTF-8等適切なエンコーディングを渡してやることで回避することが可能。以下のようなオプションを渡すことで回避できる。javac -encoding UTF-8 EventMesg.javaしかしFitGen.exeの場合、javac呼び出し部分はFitGen.exe内部で行われているので直接javacにオプションを渡すことができない。
そこでFitGen.exeを実行するWindowsのユーザー環境変数にJAVA_TOOL_OPTIONSを追加し、javacに渡したいオプションを指定することでエラーを回避することができる。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/envvars002.html
https://qiita.com/n_slender/items/6c566bb345e844ba8127
- 投稿日:2020-11-16T17:39:00+09:00
Selenium
Selenium
UIベースでの単体テストの方法
メインソース
package G_T.OfficeSystem.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; public class SeleniumTest { public static void main(String[] args) { SeleniumTest doTest = new SeleniumTest(); doTest.threadTest(); } void threadTest() { ExecutorService pool = Executors.newFixedThreadPool(8); RunImpl runImpl = new RunImpl(); for (int i = 0; i < 3; i++) { pool.submit(runImpl); } pool.shutdown(); } class RunImpl implements Runnable { @Override public void run() { WebDriver driver = new ChromeDriver(); driver.get("http://localhost:8080/OfficeSystem_Hibernate/Login"); WebElement userId = driver.findElement(By.id("userId")); WebElement email = driver.findElement(By.id("email")); WebElement password = driver.findElement(By.id("password")); userId.sendKeys("hoang"); email.sendKeys("hoang1@gmail.com"); password.sendKeys("1"); driver.findElement(By.cssSelector(".BUTTON")).click(); driver.get("http://localhost:8080/OfficeSystem_Hibernate/Chat"); // driver.close(); } } }pom
<dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.9.1</version> </dependency>ほかに必要なもの
driveとしてchromedriver.exeが必要
driver.get("http://localhost:8080/OfficeSystem_Hibernate/Login");
- 投稿日:2020-11-16T17:19:17+09:00
Spring Frameworkが分からない奴はこれを買え!!!!!!
はじめに
皆さんJavaの教本と言えばなんでしょうか?色々あると思いますが一番おすすめできるのは「スッキリわかるJava入門」と多くの人が答えると思います。
じゃあ一通りやった後にJavaのフレームワークを触ってみましょうとなった際や、就職してJavaのシステム開発に携わることになった場合多くの場所でSpringが使われていると思います。
ですがSpringの習得難易度は初学者にとってはそこそこ高くOJTをしながらやっている場合がほとんどかと思います。そんな中Spring入門書籍としては最強の本を発見したので紹介したいです。Spring Boot 2 入門: 基礎から実演まで
Amazonへのリンクはこちらになります。
この本のいい点を簡単に紹介していきたいと思います。値段が1000円以下なので技術書としてはかなりお手軽な部類
Hello Worldから簡単なCRUDできるアプリの開発
(CRUDできるアプリはフロント周りもちょっとおしゃれでモチベが上がる)
Thymeleafというテンプレートエンジンでの開発
Kindle本且つテキストなのでコピペもできる(ちょっと工夫はいる)
ログインページの作成
Spring Testの追加(2020/11月追加)やっていることとしては難しくないことが分かると思います。このレベルのテキストが欲しかった人が多かったのではないでしょうか。
今まで入門書としては「Spring徹底入門」という本があったりしましたが、この本はタイトル詐欺で初心者がこれを買うのはお勧めしません。この本が理解できるなら普通にフリーランスで食っていけるレベルかと…。
それと違い「Spring Boot 2 入門: 基礎から実演まで」は多分業務未経験~業務一年未満を想定して書かれていると思うのでとにかくわかりやすいです。
ただ、Javaだけでなく最低限のSQLとhtmlの知識があったほうがいいと思います。勉強のフロー
ではどのような順番で初学者は勉強していくとよいのでしょうか?
自分のおすすめする順番を書いておきます。「スッキリわかるJava入門」
「スッキリわかるJava入門 実践編」
これらで基礎固めをします。実践編は半分程度こなせば大丈夫です。
↓
「スッキリわかるサーブレット&JSP入門」
これでWebアプリの入門をする。しっかり学習してどう動いているか理解する。
↓
「Spring Boot 2 入門: 基礎から実演まで」
フレームワークを使用した簡単なWebアプリの開発ができるようになる
フレームワークのどこが便利か認識できるようになる以上となります。SQLなどはこのレベルでは適宜調べるか写経する感じでいいと思います。一度にすべてやっても覚えきれないので。
おわりに
技術書にありがちな「初級者向けの本と上級者向けの本が出てるけど中級者向けの本がない…」というものを埋めてくれる本が出てくれて感動しました。(これは初級向けに近いですが…)自分は「Spring徹底入門」で挫折した身なのでこの本のおかげでもう一回Springを勉強するモチベーションがわいてきました。
これがUdemyの動画だったり紙で出てくれたら最高なのになぁ。11/17追記
徹底入門のインタビューがあったのでのせておきます。面白かったので共有。
https://codezine.jp/article/detail/9552
- 投稿日:2020-11-16T16:54:22+09:00
【Java】FileReaderクラスとBufferedReaderクラスの使い方
プログラミング勉強日記
2020年11月16日
FileReaderクラスとBufferedReaderクラスの使い方を改めてまとめておく。FileReaderクラスとは
テキストファイルを読み込むためのAPIで、テキストファイルの内容を元にした処理などを行うために使う。ファイルの文字は、データを文字単位で読み書きできる文字のストリームで読み込まれる。FileReaderクラスは文字を読み込むメソッドとしては、1文字ずつ読み込むreadメソッドしか用意がないので、データをバイト単位で読み書きできるバイトのストリームで読みたい場合はFileInputStreamクラスを使用する。
readメソッドで1文字ずつ読み込むimport java.io.File; import java.io.FileReader; import java.io.IOException; public class Main { public static void main(String[] args) { try { // Fileクラスのコンストラクタの引数にファイルのパスを指定 File file = new File("text.txt"); // ファイルが存在しない場合の例外処理 // existsメソッドはファイルが存在する場合にはtrue、存在しない場合にはfalseを返す if (!file.exists()) { System.out.print("ファイルが存在しません"); return; } // FileReaderクラスとreadメソッドを使って1文字ずつ読み込み表示する FileReader fileReader = new FileReader(file); int data; // ファイルの終わりになるまで処理を繰り返す while ((data = fileReader.read()) != -1) { // 読み込んだ文字をchar型にキャストして文字を表示させる System.out.print((char) data); } // 最後にファイルを閉じてリソースを開放する fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } }BufferReaderクラスとは
FileReaderクラス同様にテキストファイルを読み込むクラスで、FileReaderクラスとは用意されているメソッドが違う。BufferReaderクラスでは1行ずつ読み込むreadlineメソッドが用意されている。
FileReaderクラスでは1文字ずつ読み込んでいるが、文字数が多い場合に効率が悪くなることがあるので、一定量メモリを決めて、たまったらOSに読み込み命令を出すようにする。これをバッファリングという。readlineメソッドで1行ずつ読み込むimport java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class Main { public static void main(String[] args) { try { // Fileクラスのコンストラクタの引数にファイルのパスを指定 File file = new File("c:SampleTest.txt"); // ファイルが存在しない場合の例外処理 // existsメソッドはファイルが存在する場合にはtrue、存在しない場合にはfalseを返す if (!file.exists()) { System.out.print("ファイルが存在しません"); return; } // BufferedReaderクラスのreadLineメソッドを使って1行ずつ読み込み表示する FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader); String data; // ファイルの終わりになるまで処理を繰り返す while ((data = bufferedReader.readLine()) != null) { System.out.println(data); } // 最後にファイルを閉じてリソースを開放する bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } }参考文献
クラスBufferedReader
クラスFileReader
【Java】FileReader、BufferedReaderでテキストファイルを読み込む
- 投稿日:2020-11-16T15:19:24+09:00
Memento Pattern
特定のクラスの状態を複製するクラスを用意してMementoとする。userは複製を作成したときにMementoをインスタンス化してコレクションに記録し、必要に応じてインスタンスを取り出す
Mementoは複製するメンバを保持する以下のクラス構成で確認します
クラス 説明 Origin.class 複製対象のクラス Memento.class Originを複製する場合に、MementoにOriginのデータを複製し、Mementoを保存する MementoList.class コレクション(ListやMapなど)を保持し、Mementoインスタンスを格納する user(Main.class) 動作を確認する *user 他の開発者がこのパターンを利用する、という意味合いを含みます
Origin.classclass Origin{ int No; void setNo(int No){ this.No=No; } Memento createMemento(){ return new Memento(No); } }Memento.classclass Memento{ int No; // Originと同じfieldを持たせる Memento(int No){ this.No=No; } int getNo(){ return No; } }MementoList.classclass MementoList{ Map map = new HashMap(); void put(int key,Memento mmt){ map.put(key,mmt); } Memento getMemento(int key){ return (Memento) map.get(key); } }user(Main.class)public static void main(String[] args){ Origin or = new Origin(); MementoList list = new MementoList(); or.setNo(1); list.put(1,or.createMemento()); or.setNo(2); list.put(2,or.createMemento()); Memento m1 = list.getMemento(1); System.out.println(m1.getNo()); }
- 投稿日:2020-11-16T13:02:39+09:00
【スタージュンも認めた】グルメスパイザー欲しいけど1103兆3543億円もするのでandroidアプリ作った
お断り
主はandroidアプリ開発、java言語が今回が初めてなのでレイアウトがボロボロだったり所々突っ込みどころがあると思います。
ネタが嫌いな方はブラウザバック推奨します
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇
⌇グルメスパイザーとは
バンダイから発売されてしまった赤い腕型のおもちゃ。正式名称は「トリコ 菓子粉砕機グルメスパイザー」。中にスナック菓子を入れてレバーをシャカシャカ動かすと、内部で菓子が粉砕され、ふりかけとなって出てくる仕組み。ぶっちゃけタカラトミーのおかしなフリカケのパクr。
CMも作られており、トリコが嬉しそうにグルメスパイザーをシャカシャカしている。以下から引用
対象読者
・1103兆3543億円もってない人
・手軽に菓子粉砕したい人
・美食家
・スタージュン
・どこでもポン!クラッシュ!クラッシュ!パッパッパ!したい人動画:説明あり
https://www.nicovideo.jp/watch/sm37827489
https://www.youtube.com/watch?v=CkE88pO2VAIダウンロード、インストール
野良アプリになるのでgumbyさんの記事参考にしてもらえればインストールできると思います
開発期間とか
企画、要件定義とか => 3秒
環境構築、プログラミング =>6時間くらい
合計6時間3秒です機能
PON!! CRASH!! UMASOO!! GOURMETSPYZER!!
ボタンを押下するとトリコが喋ります
スパイスをかける音
ボタンを押下するとフラッシュライトが光り例のシーンが簡単に再現できます
ボタンを再度押下するとフラッシュライトが消えます動作確認
huwei p30 lite
ソース
MainActivity.javaimport android.media.AudioAttributes; import android.media.SoundPool; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraManager; import android.os.Handler; import android.content.Context; public class MainActivity extends AppCompatActivity { private SoundPool soundPool; private int soundPon, soundCresh,soundUmasooo,soundGourmetSpyzer,UfoSoundEffect; private Button button1, button2,button3,button4,button5; private CameraManager McameraManager; private String McameraID; private boolean SW; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); McameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); AudioAttributes audioAttributes = new AudioAttributes.Builder() // USAGE_MEDIA // USAGE_GAME .setUsage(AudioAttributes.USAGE_GAME) // CONTENT_TYPE_MUSIC // CONTENT_TYPE_SPEECH, etc. .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build(); soundPool = new SoundPool.Builder() .setAudioAttributes(audioAttributes) // ストリーム数に応じて .setMaxStreams(5) .build(); // wav をロード soundPon = soundPool.load(this, R.raw.pon, 1); soundCresh = soundPool.load(this, R.raw.crash, 1); soundUmasooo = soundPool.load(this, R.raw.umasooo, 1); soundGourmetSpyzer = soundPool.load(this, R.raw.gourmetspyzer, 1); UfoSoundEffect = soundPool.load(this, R.raw.ufosoundeffect, 1); soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { Log.d("debug", "sampleId=" + sampleId); Log.d("debug", "status=" + status); } }); button1 = findViewById(R.id.button1); button2 = findViewById(R.id.button2); button3 = findViewById(R.id.button3); button4 = findViewById(R.id.button4); button5 = findViewById(R.id.button5); button1.setOnClickListener(v -> { // pon.wav の再生 // play(ロードしたID, 左音量, 右音量, 優先度, ループ,再生速度) soundPool.play(soundPon, 1.0f, 1.0f, 0, 0, 1); }); button2.setOnClickListener(v -> { // crash.wav の再生 soundPool.play(soundCresh, 1.0f, 1.0f, 1, 0, 1); }); button3.setOnClickListener(v -> { // Umasooo.wav の再生 soundPool.play(soundUmasooo, 1.0f, 1.0f, 1, 0, 1); }); button4.setOnClickListener(v -> { // gourmetspyzer.wav の再生 soundPool.play(soundGourmetSpyzer, 1.0f, 1.0f, 1, 0, 1); }); button5.setOnClickListener(v -> { soundPool.play(UfoSoundEffect, 1.0f, 1.0f, 1, 0, 1); //カメラを取得できなかった時は何もしない if (McameraID == null) { return; } try { if (SW == false) { //SWがfalseならばトーチモードをtrueにしてLDEオン if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { McameraManager.setTorchMode(McameraID, true); } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { McameraManager.setTorchMode(McameraID, false); } //SWがtrueならばトーチモードをfalseにしてLDEオフ } } catch (CameraAccessException e) { //エラー処理 e.printStackTrace(); } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { McameraManager.registerTorchCallback(new CameraManager.TorchCallback() { //トーチモードが変更された時の処理 @Override public void onTorchModeChanged(String cameraId, boolean enabled) { super.onTorchModeChanged(cameraId, enabled); //カメラIDをセット McameraID = cameraId; //SWに現在の状態をセット SW = enabled; } }, new Handler()); } } }activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:layout_marginRight="20dp" android:layout_marginBottom="20dp" android:text="pon!!" app:backgroundTint="#F44336" app:layout_constraintBottom_toBottomOf="@+id/imageView" app:layout_constraintEnd_toEndOf="@+id/button5" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginBottom="20dp" android:text="crash!!" app:backgroundTint="#F44336" app:layout_constraintBottom_toBottomOf="@+id/imageView" app:layout_constraintStart_toEndOf="@+id/button3" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginLeft="20dp" android:layout_marginBottom="20dp" android:text="umasoo!!" app:backgroundTint="#F44336" app:layout_constraintBottom_toBottomOf="@+id/imageView" app:layout_constraintStart_toStartOf="parent" app:strokeColor="#F44336" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginEnd="9dp" android:layout_marginRight="9dp" android:layout_marginBottom="32dp" android:text="GourmetSpyzer!!" app:backgroundTint="#F44336" app:layout_constraintBottom_toTopOf="@+id/button2" app:layout_constraintEnd_toStartOf="@+id/button5" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" /> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/home" /> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" android:text="スパイスをかける音" app:backgroundTint="#F44336" app:layout_constraintBaseline_toBaselineOf="@+id/button4" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/button4" /> </androidx.constraintlayout.widget.ConstraintLayout>後書き
週末を返せ
- 投稿日:2020-11-16T12:58:27+09:00
Optionalを使ったnull/空文字判定2.0
結局if文使うなら、いっそOptionalは一切使わないほうがいいよなって思ったりした。null/空文字判定をOptionalで進化させたい。
なお、以下断りがなければJava1.8以上で動作する想定です。
きっかけ
最近、文字列の「存在チェック」を行うコードでこんなのを発見して、かなり違和感があった
String uri = SystemPropertiesUtil.getPropertyOptional(Constant.URI).orElse(""); if (StringUtils.isEmpty(dkInfoGetApiUrl)) { // nullならなんかやる }DBからAPIのURIを取得してきて、APIコールに使う場面です。
getPropertyOptional
はOptionalを返すメソッド。
また、if文に登場するStringUtils#isEmpty
はSpring Frameworkが提供しているユーティリティメソッド。これなら普通にnull/空文字判定をすればよくて、Optionalを使わないほうがむしろシンプルでいい。
この例は少し極端だが、一般的によくある「nullではない∧空文字ではない」をOptionalを利用して使い勝手や安全性を向上させられないか、検討する。
Optionalの性質
Optionalは、Javadocによると
null以外の値が含まれている場合も含まれていない場合もあるコンテナ・オブジェクトです。
とある (Optional (Java Platform SE 8 ))。
Optional#ofNullabele
でラップした時点でnull出ない場合はそのまま値を取得できるし、そうでない場合は別途処理をつけたせばよい。つまり、あえて空文字を返す理由もあまりない。ただし、Stringのインスタンスがnullでないことと空文字でないことの双方を確認する場合、
Optional#ofNullable
で初期化すると空文字でないことはチェックから抜けてしまことに留意する。解決案
一般的に、空文字判定するときは
- nullチェック
- 空文字チェック
をまとめてbooleanを返すstaticメソッドで判定するケースが多いと思う。たとえば
StringUtil.javapublic static boolean isNullOrEmpty(String string) { if (string == null || string.equals("")) return true; return false; }それを、以下のように代替できないか。
Optionalで期待した文字列が入っていることを確認する場合、よくあるStringUtils
に以下のようなメソッドを追加してみる。StringUtil.javapublic static Optional<String> ofPresentable(String string) { if (string == null || string.equals("")) return Optional.empty(); return Optional.of(string); }2020-11-17:追記
コメントで別案をいただき、よかったので補足。StringUtil.javapublic static Optional<String> ofPresentable(String string) { return Optional.ofNullable(string) .map(String::trim) .filter(s -> !s.isEmpty()); // Java 11以上ならisBlankが使える // return Optional.ofNullable(string).filter(Predicate.not(String::isBlank)); }戻り値をOptionalでラップすることで値が存在しない可能性を明示できる。そのため、今までなら
SomeObject object = new SomeObject(); if(id == null || id.equals("")) { object.setId("default"); } else { object.setId(id); }としていたところを
final String idPresentable = StringUtil.ofPresentable(id).orElse("default"); SomeObject object = new SomeObject(idPresentable);とできる。
正直、やっていることに大きな差はないのだが制御構文がなくなることで状態の変更に気を配る負荷を小さくでき、追いかけやすく変更しやすくなる。
また、nullになる可能性を明示できるため、ロジック上nullになることはありえない箇所は任意の実行時例外を投げることで予期しないエラーを防ぐこともできる。つまり、
final String idPresentable = StringUtil.ofPresentable(id).orElseThrow(new RuntimeException("予期しない入力です")); SomeObject object = new SomeObject(idPresentable);100%代替できるわけではないし、向いてないユースケースもあるだろう。しかし、いくつかのケースにおいてはむしろこちらのパターンのほうが有効な気がしており、今後是非試してみたいと思っている。
おまけ
今回、強烈な課題を感じたのが文字列の処理だったが、ListやMapについても同じようなメソッドを用意することで要素存在判定、ある場合の処理を簡潔かつ安全にできそうです。
たとえば
ListUtil.javapublic static <T> Optional<List<T>> ofPresentable(List<T> list) { if (list == null || list.size() == 0) return Optional.empty(); return Optional.of(list); }リッチにするならリストの要素にnullが含まれているかも判定してもよいかもしれません。
- 投稿日:2020-11-16T12:18:40+09:00
FitGen.exeを日本語Windowsで実行した時発生するJavaのエンコーディングに関するエラーを回避する
TL;DR
環境変数の
JAVA_TOOL_OPTIONS
に-Dfile.encoding=UTF-8
を設定する。詳細
GARMIN FIT SDKでカスタムメッセージ等を定義した場合、FitGen.exeを実行しFIT SDKを生成する必要がある。
https://developer.garmin.com/fit/cookbook/fitgen/しかし、最近Garmin Developersで配布されているFIT SDKのEventMesg.javaのドキュメントコメントに謎の文字化けが混入しておりそのままFitGen.exeを実行すると
> .\FitGen.exe -o test -java FIT Code Generator - Protocol 2.00 Profile 21.40Release SDK Version: production/akw/21.40.00-0-g813c158 Parsing existing config.csv... Writing new config.csv... Generating Java code... Building Java tools...0% com\garmin\fit\EventMesg.java:679: エラー: この文字は、エンコーディングMS932にマップできません * Comment: Theテつ?firstテつ?byteテつ?isテつ?theテつ?radar_threat_level_max, theテつ?secondテつ?byteテつ?isテつ?theテつ?radar_threat_count, and theテつ?lastテつ?16テつ?bitsテつ?areテつ?reservedテつ?forテつ?futureテつ?useテつ?andテつ?shouldテつ?beテつ?setテつ?toテつ?FFFF. ^ 以下略というエラーが発生し、ビルドに失敗する。
この文字は、エンコーディングMS932にマップできません
というエラーはjavacにUTF-8等適切なエンコーディングを渡してやることで回避することが可能。以下のようなオプションを渡すことで回避できる。javac -encoding UTF-8 EventMesg.javaしかしFitGen.exeの場合、javac呼び出し部分はFitGen.exe内部で行われているので直接javacにオプションを渡すことができない。
そこでFitGen.exeを実行するWindowsのユーザー環境変数にJAVA_TOOL_OPTIONSを追加し、javacに渡したいオプションを指定することでエラーを回避することができる。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/envvars002.html
https://qiita.com/n_slender/items/6c566bb345e844ba8127
- 投稿日:2020-11-16T11:34:39+09:00
備忘録No.6「文字列として受け取った1桁の和差の式を計算する」[Java]
はじめに
初めてgetNumericValueメソッドを使用したので、記録。
やりたい事
・長さ自由の文字列で計算式を受け取る(使われる数字は全て1桁、演算子は'+'と'-'のみ)
・受け取った文字列を数値にして計算
・計算結果を出力する(例)
入力:"1+2+3"
出力:6コード
Scanner sc = new Scanner(System.in); String line = sc.nextLine(); sc.close(); char [] c = line.toCharArray(); int sum = 0; boolean add = true; for(int i = 0; i < line.length(); i++) { if(i % 2 == 0) {//数値と演算子が交互に来るため、偶数は数値の計算,奇数は演算子の入れ替えを行う if(add) { int n = c[i]; sum += n; } else { int n = c[i]; sum -= n; } } else { if(c[i] == '+') { add = true; } else { add = false; } } } System.out.println(sum);入力:"1+2+3"
出力:150コード1の問題点
・char型の配列に入れられた数値をint型にした際に、ASCII文字コードで代入されてしまっている。→ 配列に入っている数値の見た目そのままで取り出したい!
改善したコード
if(add) { int n = Character.getNumericValue(c[i]); sum += n; } else { int n = Character.getNumericValue(c[i]); sum -= n; }入力:"1+2+3"
出力:6まとめ
char型の数値をそのまま使いたい時には、Character型のgetNumericValue()を使う。
追記
省略前
if(c[i] == '+') { add = true; } else { add = false; }上記の部分では、比較演算子はboolean型を返すので、addに直接代入することで、真偽判定ができる。
省略後
add = c[i] == '+';
- 投稿日:2020-11-16T11:30:16+09:00
【Java】部分文字列を抜き出す(AOJ13 - シャッフル)
文字列から部分文字列を抜き出す
- Stringクラスのsubstringメソッド
- 第一引数に抜き出し開始の位置を設定
public class Main { public static void main(String[] args) { String str = "ねこはなんでこんなにMOFUいんでしょうか"; //10~14文字目まで抜き出す String mofu= str.substring(10,15); //山崎を表示 System.out.println(mofu); //MOFUい } }シャッフル(ITP1-9)
1つのアルファベットが描かれた n 枚のカードの山をシャッフルします。
1回のシャッフルでは、下から h 枚のカードをまとめて取り出し、それを残ったカードの山の上に積み上げます。
カードの山は以下のように1つの文字列で与えられます。
abcdeefab
最初の文字が一番下にあるカード、最後の文字が一番上にあるカードを示しています。
例えば、これを h が 4 でシャッフルすると、最初の4文字 abcd が、残りの文字 eefab の末尾へ連結されるので以下のようになります:
eefababcd
このシャッフルを何回か繰り返します。
カードの山の最初の並び(文字列)と h の列を読み込み、最後の並び(文字列)を出力するプログラムを作成して下さい。
Input
複数のデータセットが入力として与えられます。各データセットは以下の形式で与えられます:
最初の並びを表す文字列
シャッフル回数 m
h1
h2
.
.
hm
最初の並びを表す文字列が "-" のとき入力の終わりとします。
Output
各データセットに対して、最後の並び(文字列)を1行に出力して下さい。import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (true) { String a = scanner.next(); if (a.equals("-")) { break; } int b = scanner.nextInt(); //b行分の合計値を計算 int sum = 0; for (int i = 0; i < b; i++) { sum += scanner.nextInt(); } System.out.println(sum); //7 System.out.println(a); //vwxyz 5 //2倍 にする String c = a + a; //vwxyzvwxyz //System.out.println(c); /*文字列aの文字を一文字ずつ、(sum/文字列)のあまり分だけ左に動かしたい substringメソッド 開始位置:(sum/文字列a長さ)のあまり 7/5=1...2 2 終了位置:(sum/文字列a長さ)のあまり+文字列a長さ 2+5=9 */ System.out.println(c.substring(sum % a.length(), sum % a.length() + a.length())); } } }
- 投稿日:2020-11-16T08:54:05+09:00
【Java】部分文字列/要素を検索(AOJ12 - リング)
AIZU ONLINE JUDGE の教材を使って勉強していきます
行いたい操作や文法をまとめ、
実際にその文法を使ってAOJを解答した例を載せていますindexOfで部分文字列を検索
- indexOfメソッド
- 文字列に含まれる部分文字列を検索
- ヒット場合は、文字列のインデックス番号を返す(先頭0)
- lastIndexOfメソッドは文字列の後ろから検索
- 最初に見つかった位置のインデックスを返す
public class Main { public static void main(String[] args){ String str1 = "Penpineappleapplepen"; String str2 = "pineapple"; int result = str1.indexOf(str2); if (result != -1) { System.out.println(str2 + "発見!"); System.out.println(str2 + "が見つかった位置:" + result); } else { System.out.println(str2 + "は見つかりませんでした。。。"); } } }pineapple発見! pineappleが見つかった位置:3indexOfで複数要素を検索
- 第2引数を指定して複数の要素を検索する
- indexOfメソッドの第2引数で検索開始位置指定
- 文字がヒットしたらその次のインデックスを第2引数に指定し、続きから残りの文字列を検索
import java.util.ArrayList; import java.util.Arrays; public class Main { public static void main(String[] args){ String str1 = "Penpineappleapplepen"; int result; for (int i = 0; i < str1.length(); i++) { result = str1.indexOf("p", i); if (result != -1) { i = result; System.out.println("pを " + result +" 番目に発見した!"); } } } }pを 3 番目に発見した! pを 8 番目に発見した! pを 9 番目に発見した! pを 13 番目に発見した! pを 14 番目に発見した! pを 17 番目に発見した!リング(ITP1-8)
図のようなリング状の文字列 s の任意の位置から、時計回りに連続した文字をいくつか選んで、文字列 p が作れるかを判定するプログラムを作成してください。
Input
1行目に文字列 s が与えられます。 2行目に文字列 p が与えられます。
Output
p が作れる場合は Yes と、作れない場合は No と1行に出力してください。
Constraints
* 1≤pの長さ≤sの長さ≤100
* 文字列は英小文字からなる
Sample Input 1
vanceknowledgetoad
advance
Sample Output 1
Yesimport java.util.Scanner; public class Main { public static void main(String[] args){ Scanner scan=new Scanner(System.in); String s=scan.next(); String p=scan.next(); s+=s; if(s.indexOf(p)>=0)System.out.println("Yes"); else System.out.println("No"); scan.close(); } }
- 投稿日:2020-11-16T08:53:44+09:00
【Java】文字判定・文字列整形(AOJ11 - 文字のカウント)
アルファベットの小文字かどうか判定
- ASCIIコード表で’a’から'z'は連続している
- 'b'の値は'a'の値よりも1大きい
- c がアルファベットの小文字なら真になるコードを書くには
c >= 'a' && c <= ‘z’
//c がアルファベットの小文字なら真になるコード public class Main { public static void main(String[] args){ char c = 'c'; System.out.printf("確認したい文字cは? '%s'\n", c); /*cがアルファベットの小文字なら真*/ if (c >= 'a' && c <= 'z') System.out.println("c は小文字"); /*cが英小文字なら真*/ if (c >= 'a' && c <= 'z') System.out.println("c は小文字"); else System.out.println("c は小文字ではない"); /*cが数字なら真*/ if (c >= '0' && c <= '9') System.out.println("c は数字"); else System.out.println("c は数字ではない"); /*cが'+'か'-'なら真*/ if (c == '+' || c == '-') System.out.println("c は符号"); else System.out.println("c は符号ではない"); } }確認したい文字cは? 'c' c は小文字 c は小文字 c は数字ではない c は符号ではないScannerクラスのhasNext関数で繰り返し判定
- 入力を読み込んでまだ繰り返し続行を判定する
- 前から順番に値を取得していき、まだ値を取得できる時にはtrueを、これ以上値を取得できない場合にはfalse
Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { String str = scanner.next(); System.out.println(str); }文字列整形
- formatメソッド:書式指定子を使って値を任意の形式に変換
- printfメソッド:formatの代わりにそのまま表示。改行文字は付与しない
- 書式指定子:書式文字列の中に値を埋めこむための記法
- 書式指定子以外はそもまま出力
- 書式指定子の例
System.out.println(String.format("%sの体重はりんご%d個分です。","アルクマ",70));
- 1\$,2\$:1,2番目の引数を表す
- %.2s:文字列の最小幅指定
- %8s:文字列の幅指定。不足した分は左が空白。負数で右空白
- %#x: 接頭辞に0/0xをつけて16進数表示
- %.2e:小数点以下の2桁表示、eで指数表記
- %tF:日付 %tY-%tm-%td と同じ
- %td:時刻
import java.util.*; import java.time.LocalDateTime; import java.util.Locale; public class Main { public static void main(String[] args) { System.out.println(String.format("%sの体重はりんご%d個分です。","アルクマ",70)); System.out.println(String.format("%1$sの体重はりんご%3$d個分です。%1$sは元気です。","アルクマ","キティ",70)); //アルクマの体重はりんご70個分です。アルクマは元気です。 //文字列の最小幅指定 System.out.println(String.format("%.2sです。", "アルクマ")); //アルです。 //文字列の幅指定 System.out.println(String.format("%8sです。", "アルクマ")); // アルクマです。 //最大桁数指定 System.out.println(String.format("10進数 %08d", 12345)); //10進数 00012345 //16進数 System.out.println(String.format("16進数 %#x", 10)); //16進数 0xa //小数点以下の桁数指定 System.out.println(String.format("小数点数 %.2f", 123.456)); //小数点数 123.46 //指数 System.out.println(String.format("指数/小文字 %.2e", 123.456)); //指数/小文字 1.23e+02 //日時 var d = LocalDateTime.now(); System.out.println(String.format("%tr", d)); //03:42:23 AM System.out.println(String.format("%1$tY年 %1$tm月 %1$td日", d)); //2020年 10月 31日 //printfメソッドで表示 System.out.printf("%.2sです。\n", "アルクマ"); } }文字のカウント(ITP1-8)
与えられた英文に含まれる、各アルファベットの数を数えるプログラムを作成して下さい。 なお、小文字と大文字は区別しません。
Input
複数の行にまたがる1つの英文が与えられます。
Output
与えられた英文に含まれる各アルファベットの数を以下に示す形式で出力して下さい:
a : aの個数
b : bの個数
c : cの個数
.
.
z : zの個数
Constraints
* 英文が含む文字の数 < 1200import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner scan=new Scanner(System.in); int alf[]=new int[26]; while(scan.hasNext()){ String str=scan.next().toLowerCase(); for(int i=0;i<str.length();i++){ char c=str.charAt(i); if('a'<=c&&c<='z'){ alf[str.charAt(i)-'a']++; } } } for(int i=0;i<26;i++)System.out.println((char)('a'+i)+" : "+alf[i]); scan.close(); } }