徒然

日常を垂れ流します。

SDKMAN! メモ

久々更新です。 新しいテーブルとイスとキーボードによりモチベが高まっています。

SDKMAN! is 何

公式サイトより引用

The Software Development Kit Manager

だそうです。うん、そっか。

初心者的に解釈すると、Javaの言語自体のバージョンを管理できるツールです。 Rubyのrbenvを参考にしているのだとか。Pythonでいうとpyenv、Node.jsでいうとnvm、、、 どの言語にも大体ありますね。

SDKMAN!はScala、Kotlin等のJVMを使う言語や、Maven、Spling Boot等の主要なライブラリのSDKも提供しています。

何故SDKMAN!を使うのか

最近はDocker等によりその必要性が薄くなってきている感もありますが、コマンド一発でマイナーアップデートが取得出来たり、バージョン変えられたりしたらやはり便利です。

JavaだとIDEIntelliJ IDEA)がその辺の面倒も見てくれるので今まで使用していなかったのですが、コマンドでちょちょいとJavaScala使うことも多くなってきたのでインストールする気になりました。

今の使い方の想定としては、Java,Scala等の言語についてはSDKMAN!で管理して、Maven等は今まで通りなんも考えずにIDEに一任しようと思ってます。

SDKMAN!導入

公式サイトの通りにコピペすれば簡単です。

$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
$ sdk version

SDKMAN!ができること

JDKJava Development Kit)のインストール

$ sdk install java #stableかつfomalなJavaがインストールできます。

$ sdk install java 19-tem #Temurinというコミュニティ版JDK。バージョンも指定します。

インストールできるJavai一覧

$sdk list java

$ sdk list scala #scalaなんかもこんな感じで

想像以上に色んなJDKがありますね。GraalVMでネイティブ化するのもたのしそう。。

使用するJDKを設定

$ sdk use 19-tem

普段使うJDKを設定(グローバル)

$ sdk default 19-tem

普段使うJDKを設定(フォルダごと)

$ cd ~/work/sdkman_test # ここで使うバージョンを設定する。
$ sdk env init # これで初期設定。`.sdkmanrc`というファイルができる。
$ cat .sdkmanrc
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.4.1-tem

$ sdk env # ただディレクトリに入るだけじゃダメで、このコマンドでバージョンが入れ替わる。

もしcdだけでバージョンを切り替えたいなら、$ sdk configでエディタが開くので、sdkman_auto_env=trueとする。

管理下のパッケージをアップグレード

$ sdk upgrade

SDKMAN!自体をアップデート

$ sdk selfupdate

なんか困ったとき

$ sdk update # なんかメタデータを更新してくれるらしい
$ sdk flush # キャッシュを削除してくれるらしい
$ sdk flush archives # 古いパッケージを削除してくれるらしい
$ sdk flush tmp # 古いSDKMAN!のインストーラを削除してくれるらしい

その他

$ sdk broadcast # なんか新機能とかのニュースが見れるらしい

IDEとの連携

 ~/.sdkman
$ tree -L 3
.
├── bin
│   └── sdkman-init.sh
├── candidates
│   └── java
│       ├── 17.0.4.1-tem
│       ├── 19-tem
│       └── current -> 19-tem

~/.sdkman/candidatesJDKの本体が収まっていました。これ使えばよさそうです。

まとめ

SDKMAN!は機能がシンプルで使いやすい印象です。 カジュアルにJavaを楽しめるようになったかなぁ。

参考文献

sdkman.io

qiita.com

dev.classmethod.jp

Atom形式のフィードを読んで指定のファイルより新しいエントリを取得するVBScript

本当はチャットツールに差分をPOSTするところまで実装する予定だったが、力尽きたので今日はここまで。

Option Explicit

Dim FEED_URL
Dim PREV_RSS_UPDATE_DATE_FILE

'FEED_URL = "http://www.data.jma.go.jp/developer/xml/feed/regular.xml"
FEED_URL = "http://example.com"
PREV_FEED_UPDATE_DATE_FILE = "C:\Users\hayak\TMP\prevDate.txt"

' 本処理
Dim xml ' XMLDOMオブジェクト

GetFeed FEED_URL, xml ' フィードをダウンロード

' 更新確認
if StrComp(GetPrevUpdateDate(), GetUpdateDate(xml)) = -1 Then
    WScript.Echo GetNewEntry(xml, GetPrevUpdateDate())
End If

' フィードを取得してXMLDOMオブジェクトを格納する
Sub GetFeed(rssURL, xmlDom)
    Dim xmlhttp 
    Set xmlhttp = WScript.CreateObject("MSXML2.XMLHTTP.3.0") 
    Dim resText 

    xmlhttp.Open "GET", rssURL, False 
    xmlhttp.send 

    If xmlhttp.status <> 200 Then
        WScript.Echo "リクエスト失敗"
        Set xmlhttp = Nothing 
        WScript.Quit
    End If

    ParseXML xmlhttp.responseText, xmlDom
    Set xmlhttp = Nothing 
End Sub

' XML文字列をパースしてXMLDOMオブジェクトを格納する
Sub ParseXML(xmlString, ByRef xmlDom)
    Dim loadSuccess
    Set xmlDom = WScript.CreateObject("MSXML2.DOMDocument")
    
    loadSuccess = xmlDom.LoadXML(xmlString)

    If Not loadSuccess Then
      WScript.Echo xmlDom.parseError.errorCode
      WScript.Echo xmlDom.parseError.reason
      WScript.Echo xmlDom.parseError.line
      WScript.Echo xmlDom.parseError.linepos
      WScript.Echo xmlDom.parseError.filepos
      WScript.Echo xmlDom.parseError.srcText
      WScript.Echo xmlDom.parseError.url
      Set xmlDom = Nothing
      WScript.Quit
    End If
End Sub

' ファイルを読んで文字列を格納する
Sub ReadFile(filePath, textStr)
    Dim fs
    Dim tf

    Set fs = WScript.CreateObject("Scripting.FileSystemObject")
    Set tf = fs.OpenTextFile(filePath, 1)

    textStr = tf.ReadAll
    tf.Close
    Set fs = Nothing
    Set tf = Nothing
End Sub

' XMLから更新日を取得する
Function GetUpdateDate(xml)
    GetUpdateDate = xml.getElementsByTagName("updated").Item(0).Text
End Function

' ファイルから以前の更新日を取得する
Function GetPrevUpdateDate()
    ReadFile PREV_FEED_UPDATE_DATE_FILE, GetPrevUpdateDate
End Function

' 以前の更新日より新しいエントリを取得する
Function GetNewEntry(xml, prevDate)
    Dim entry
    Dim result
    result = "未読のentryがあります  \n"
    For Each entry in xml.getElementsByTagName("entry")
        AppendNewEntry prevDate, entry, result
    Next
    GetNewEntry = result
End Function

' 新しいエントリのみ追加する
Sub AppendNewEntry(prevDate, entry, result)
    Dim updateDate
    updateDate = entry.getElementsByTagName("updated").Item(0).Text
    If StrComp(GetPrevUpdateDate(), updateDate) = -1 Then
        Dim title
        Dim link
        title = entry.getElementsByTagName("title").Item(0).Text
        link = entry.getElementsByTagName("link").Item(0).getAttribute("href")
        result = result & title & "  \n" & link & "  \n" & "  \n" 
    End If
End Sub

PowerShellをPowerShellでアップデートする

久々です。

備忘録がてらの更新。※20201114あまりにも適当だったので手を加えました。

PowerShell7.0がリリースされて久しいですが、最近はこんな機能ができてます。

新バージョンの通知

Microsoft お得意の(?)おせっかいですね。 アップデート用のコマンドとか用意してくれたらいいのに…なんて思ったんですが、

ないんですよね、これが!!!

コマンドでPowershell7.0.xをインストールする

一応、PowerShell Gallery | Home にワンパンでアップデートできるモジュールは散見されますが、

何入れたか逐次忘れてしまう私は、そのモジュールのアップデートさえ怠ってしまいそうです、、、

よって、 WSL2を導入しましょう コピペで使えるアップデートコマンドを作ってメモしておこうという話です。

↓これです

PS > (New-Object Net.WebClient).DownloadFile("https://github.com/PowerShell/PowerShell/releases/download/v7.0.3/PowerShell-7.0.3-win-x64.msi", "$env:Temp\powershell.msi")
PS > msiexec.exe /package $env:Temp\powershell.msi /passive ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 ENABLE_PSREMOTING=1 REGISTER_MANIFEST=1

(New-Object Net.WebClient).DownloadFilecurl(Invoke-WebRequest) でもいいと思います。

PS > Invoke-WebRequest "https://github.com/PowerShell/PowerShell/releases/download/v7.0.3/PowerShell-7.0.3-win-x64.msi" -OutFile "$env:Temp\powershell_a.msi"
PS > msiexec.exe /package $env:Temp\powershell_a.msi /passive ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 ENABLE_PSREMOTING=1 REGISTER_MANIFEST=1

msiexec.exe には公式では /quiet オプションが入っていましたが、進捗状況が分からなくなったので /passive としました。

再起動して終了です。

結構手間かかったので、フツーにインストールした方が早いかも。

以上!!!

GoとGCEで小さなWebアプリケーションを構築する(1) ~とりあえずGCE上で動かしてみるの巻~

久々に書きます。 今日はお仕事ネタではなく、Golangの話題についてです。

ひと月ほど前に友人からこんな本を頂きました。ほんとありがとね。

一通り遊んだので、そろそろアウトプットしていきたいと思います。

この記事の目的

以下のようなWebサイトをWWW(この世のすべて)に公開することです。 世界一簡単なサイト

もちろん、ここで終わりにするつもりはなく、 簡単なDB連携やES、JSONでのREST通信的なものも書いてみるつもりです。ネタは豊富です!

あと、誰かが見れるようにした方が楽しいと思ったので、あえてデプロイまでつっこみます。Golangなら、デプロイ自体はとても簡単ですよ。
※セキュリティには配慮しつつ書きますが、どうしても知識不足なところはあると思います。 このエントリだけでなく、他記事からも情報を仕入れつつ、慎重にデプロイしてください。

使うお道具

手順

GCEを整える

GCEは、マシンタイプ f1-micro であれば、無料で使えます。 会員登録等は省略します。

まず、こちらにアクセスし、必要事項を記入しましょう。

名前:適当、マシンタイプ:f1-micro
Debian、標準永続ディスク30GB、HTTPトラフィックを許可

右上にお金かかりそうなことが書いてありますが、この構成の1台のみならば、お金は発生しないはずです。 発生したらごめんね。

VMインスタンスがすぐに登録され、起動します。

SSHの設定

当方、Windowsなので、PowerShell(OpenSSH)にてSSH公開鍵作成&SSH接続をしてみます。

4096bitのrsa暗号鍵ペアを作ります。 -f オプションは適宜変更してください。

PS >ssh-keygen -t rsa -b 4096 -f C:\Users\(あなたのおなまえ)\.ssh\id_rsa_test

指定したパスに次のファイルができます。

拡張子なしのファイルが秘密鍵。動かしてはならない。.pubは公開鍵。

拡張子なしのファイルは秘密鍵です、動かしてはなりません。誰かに悟られてはいけません。 .pubを公開鍵といいます。これをGCE上に置きます。

GCEコンソールに戻って、作成したVMインスタンスをクリック、 上タブの「編集」をクリックして、.pubファイルの中身をコピペします。

では、PowerShellで接続してみましょう。

PS> ssh (あなたのおなまえ)@VMインスタンスの外部IP -i c:/users/(あなたのおなまえ)/.ssh/id_rsa_test -p 22

これで環境設定は終了です。と言いたいところですが、次のファイアウォールの設定は絶対にしておきましょう。

ファイアウォール

ここが詳しいです。必ず設定しましょう。 22ポートを開放していると、世界中の怖い人がたくさん訪問してきますよー^^ eightyfivelife.com

また、同様の設定でHTTP通信のポートも制限しておくといいかもしれません。

ポートを8080のみ許可する

これで、環境設定は終了です。

Goのインストール

他にたくさん文献がありますので、省略します。

この辺りを見たらできるんじゃないんでしょうか。

qiita.com

テキストエディタにてファイル編集

環境変数GOPATH以下が、Golangの主なワークスペースです。 Golangに付属するコマンドは、大体ここを見ながら動きます。

さあ、初めてのWebアプリを書きましょう(ここまで記事を書くのに3時間)。 いまは、Golangの文法等については気にしなくて結構です。

%GOPATH%\src\first_webapp\server.go

package main

import (
    "fmt"
    "net/http"
)

func handler(writer http.ResponseWriter, request *http.Request) {
    fmt.Fprintf(writer, "Hello World, %s", request.URL.Path[1:])

}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Goを コンパイルする

まず、Goのワークスペースフォルダに移動して、 上記で作成したプロジェクト「first_webapp」を初期化します。

PS> cd $env:GOPATH
PS> go install first_webapp

Goをビルドしてできたバイナリは、別OS上では動作しません。 しかし、環境変数を設定することによって、Linux用のバイナリを作成することが可能です。

PS>  $env:GOOS = "linux"
PS>  $env:GOARCH = "amd64"

ビルドの時は、いつの間にか出来ているbinフォルダに入って行います。

PS>  cd bin
PS>  go build first_webapp

さ、これでバイナリが出来上がっています。

上がLinux用。下はinstallで作成されたWIndows用の実行ファイル

あとはこのバイナリをSCP送信して、実行します。

PS>  scp -i (秘密鍵) (goのバイナリ)  (外部IP):~/
bash: first_webapp

WEBページにつないでみましょう。 開発用にポートを∶8080にしたことに注意してください。

これで、晴れて世界デビューです。おめでとうございます。

詳しい解説は、また別日に!!

PHP FPDI + FPDF でPDFファイルを分割する

タイトルの通りです。お仕事で使ったので、備忘録。

青空文庫から人間失格ー太宰治のPDFを取得し、テストデータとした。

PDFから、最初の3ページ分を抜粋し、新しいPDFとして保存する。

今回使うのは、PHPのPDFの編集用ライブラリとして太古の昔から存在する、FPDIとFPDFである。

PHPで使えるPDFのライブラリはたくさんこと存在する。PDFlib、TCPDF、haru...

今回の選定ポイントは、古いPHP動くこと、その一点に尽きる。

PHP動くようにする(XAMPP、VSCode

今回の記事も長くなりそうである。なぜなら、環境から作る必要があるから。

でも環境構築なら優れた記事はもうたくさんあるので丸投げして任せよう。

ここがいいんじゃないかな

Qiitaーisual Studio Code でPHPを開始するまでの手順

f:id:whzwiz:20200522021333p:plain うむうむ。

ん~、開発用途に一つサーバー借りようかな。VSCodeをリモートで動かしたい。

さて次

FPDI と FPDF のダウンロード

Composerを使ったりとかするんだろうけど、今回はそんな大層なことはしないので愚直にクラスファイルをダウンロードして、htdocsに突っ込むことにする。

ダウンロード

フォルダ構成は以下

f:id:whzwiz:20200522021425p:plain

ソースコード

最小構成ならば、実に簡単にPDFのあれこれができてしまう。

<?php

use setasign\Fpdi\Fpdi;

require_once('fpdf/fpdf.php');         // はじめにFPDF
require_once('fpdi/src/autoload.php'); // 次にFPDIの順で読み込むよ。

// PDFファイルを読み込む
$pdf = new Fpdi();
$pageCount = $pdf->setSourceFile('NingenShikkaku.pdf');

for ($i = 1; $i <= $pageCount; $i++) {        // カウントは1から!(ページ数に相当)
    if ($i <= 3) {
        $templateId = $pdf->importPage($i);   // 該当ページをテンプレートとしてインポート
        $pdf->AddPage();                      // 出力用のページを一つ追加
        $pdf->useTemplate($templateId);       // 出力用のページに、テンプレートを適用する
    }
}

$pdf->Output('result_3pages.pdf', 'F');   // ファイルとしてフォルダ内に書き出し

echo("<h1>Finished.</h1>");               // おわり

動かす

用意したのは青空文庫人間失格をPDF化したもの。47ページ。

f:id:whzwiz:20200522021712p:plain

ブラウザ。まあ見るまでもないわなw

f:id:whzwiz:20200522021529p:plain

生成ファイルは、"result_3pages.pdf"

f:id:whzwiz:20200522021620p:plain

生成されたファイルは3ページになっているが。。。おっと。

f:id:whzwiz:20200522021640p:plain

もとが横長のページだったが、それが反映されず、縦向きの文書として出力された結果である。

$pdf->useTemplate($templateId);

$pdf->useTemplate($templateId, ['adjustPageSize' => true]);

とすればよい。

はいどうぞ

f:id:whzwiz:20200522021739p:plain

感想

師が「PHPはなんか、よくわからんが、何でもできるんだ。気軽にな。」と言っていた。

ようやく気持ちが分かってきた気がする。なんだかわからんが確かにできるんだなぁ。気軽になぁ。

Apache Commons Net listFiles() の注意点

JavaFTPサーバーのデータを扱う場合、Apache Commons Net APIを使うのが一般的(多分)。

FTPというのはHTTPと同じように通信を行うときの約束事(Protocol)のひとつで、

File Transfer Protocolの略である。HTTPのコマンド(GET,POST)と同じように、FTPにもコマンドがある(コマンド一覧)。

例えば、CWDコマンドでディレクトリを移動して、LISTコマンドでそのフォルダにあるファイルの一覧(もしくは、ファイル自体)を取得できる。

ただここで一つ懸念があって、LISTコマンドで取得できるファイル情報は、サーバー側のOSに依存した結果だったりする。つまりFTPサーバーが運用の途中で切り替わったりすると大本から回収が必要になる。

これじゃ悲しい結末を迎えるのが確定的に明らかなので、そのOS間の差を吸収できるようにしたのがApache Commons Net APIFTP関連のクラスたちである。

恐らく多くのシステムでこのAPIは使われていて、信用度はかなりのものだと思う。

だが、これで取得したデータでNullぽしてしまったのである。えーーーー。

 

org.apache.commons.net.ftp.FTPClient

上記のクラスで、相手のホストへの接続、ログイン、ファイル転送すべてが行える。いろいろクラスはあるが、基本的にこれさえ覚えればもう大丈夫。簡単にFTPサーバーへとアクセスできる。

この中のメソッドに、listFiles()というものがあり、上記のLISTコマンドで取得したファイル一覧を取得してFTPFile型の配列を返してくれる。相手がCentOSであれUbuntuであれ、WindowsServerであれ、後も先も同じメソッドが使えるということである。

便利なメソッドlistFiles()だが、一つ注意点がある。もしファイル情報が取得できなかったら、こいつ、配列にnullを入れてくる

つまり、こんな感じの処理をすると...

FTPFile[] files = client.listFiles();    // ファイル一覧取得
for (FTPFile file : files) {             // もし、filesのどれかがnullだったりしたら
    System.out.println(file.getName());  // .getName()でぬるぽ
}

最近、ぬるぽしか診てなくてミスターぬるぽと呼ばれはじめた。

やあ、nullさん。いつも仕事をくれてありがとう。もう出てこなくていいよ。

 

Javadocもそう言っている

閑話休題Apache Commonsの公式ドキュメントを見てみる。

Returns:

The list of file information contained in the current directory in the format determined by the autodetection mechanism.

NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for null before referencing it.

公式ドキュメント:FTPClient#listFiles()より)

もしファイルのパーシング(OS間の差をなくしたりして、FTPFile型の値に直す処理)に失敗したら、nullを入れる*1から、値を参照する前にちゃんとnullチェックしてね。ということだ。公式が言っているんだから、ちゃんとチェックしないといけないね(2回目)。

にもかかわらず、いま診てるシステムではしていなかったのだなあ。ネットに転がっているサンプルコードでも、nullチェックしているものは少ない。(そもそもこのAPIに関する記事が少ないけどね)

改善案

さっきから愚痴が多いが、直すならこう。

FTPFile[] files = client.listFiles();    // ファイル一覧取得
for (FTPFile file : files) {             // もし、filesのどれかがnullだったりしたら
    if(file == null)
        continue;                        // 最初に戻る
    System.out.println(file.getName());  // .getName()でぬるぽらない!
}

周りの人を困らせたいならこう

Arrays.asList(client.listFiles())       // ファイル一覧取得
    .stream()
    .filter(Objects::nonNull)     // nullをひっかけて
    .map(FTPFile::getName)        // Stream<String>にして
    .forEach(System.out::println);// わぁい!

*1:ちなみに、具体的にどんなファイルがnullになるかはよくわからない。文字コードが変わってしまったり、ファイル名に禁止文字が入ったりしたときかな?(これもしくはこれ

Java String → Oracle varchar2 入りきらない文字列をByte単位で切り捨てるための策

今回の経緯

お仕事ではJava7とOracle11を使っている。今回の改修で問題になったのが、インポートする予定のCSVファイルのある文字列データが、何文字入るのか不定であること。

もし、入力文字列が100Byteで、受け止めるカラムでvarchar2(64 Byte)しか用意してないとき、INSERTしたらORA-12899 でエラー吐いてしまう。

対応としては以下ものがパッと考えられる。

  • まずそんなデータを入力するな。データ仕様くらい決めておけ。
    • ほんそれ
  • CROBで受け止めればいいじゃん。&varchar2(4000)にしよう。
    • データ量と速度の観点から、実用的でない。ウン十万行あるテーブルなんでできるだけ、データ容量もメモリバッファも減らしたくない。
  • 文字列オーバーしたら切り捨てよう。
    • え、データなくなってもいいんだ。。。そっか。。。

ということで、今回の方策としては、「文字列オーバーしたら切り捨てよう」なった。そっか。。。

そもそも入ってくるだけで使わないデータだという。でもDBには保存してほしいんだと。そっか。。。

既存のCSVtoDBの処理では、PLSQL使わずに直INSERTしているため、Javaでのバリデートである。

文字列の切り捨ての方策

String.substr()で行こうと思ったが、少し考えればそれじゃダメなことが分かる。varchar2のサイズ指定は、デフォルトでByte単位だ。文字数じゃない。それに、DB側はMS932なのに対し、Javaの内部処理は決まってUTF-16である。これじゃいかん。

とりあえず、2通りの方法で切り捨て可能だと思う。

  • String.substr()を使うなら、テーブル定義の変更で吸収する必要が出てくる。navarchar2か、varchar(20 CHAR)で宣言するか、もしくはNLS_LENGTH_SEMANTICSをCHARにするか。これまでのDB設計や、特殊文字入ることも考えると良くないなと思う。変更箇所が分かれるのは混乱を招くし、そもそもDBは専門外なんで、余計なことはしたくない(甘え)。

  • JavaでちゃんとByte単位で切り捨ててからINSERTしてやる。これが一番健康的であろう。なら、String.substr()は使えない。

java.nioで切り捨てる

参考にしたのは、Qiita - Java 文字列をバイト数で切り捨てる

上記の例だと、特殊文字がうまいことエスケープできていなかったようなので、手直しした。でもこれでいいかは不安である。

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;

public class Converter {

    /**
    * @param s 文字列
    * @param charset 文字コード
    * @param maxBytes 最大バイト数
    * @return 最大バイト数に入りきる分だけの文字列
    */
    public String cutString(String s, Charset charset, int maxBytes) {
        ByteBuffer bb = ByteBuffer.allocate(maxBytes);
        CharBuffer cc = CharBuffer.wrap(s);
        CharsetEncoder encoder = charset.newEncoder()
                .onMalformedInput(CodingErrorAction.REPLACE)
                .onUnmappableCharacter(CodingErrorAction.REPLACE)
                .reset();
        encoder.encode(cc, bb, true);
        encoder.flush(bb);
        bb.flip();
        return charset.decode(bb).toString();
    }
}

いきなりjava.nio のクラスがぞろぞろ出てきて嫌だけど、1行1行理解すれば読めると思う。

  • まずByteBuffer bbmaxBytes分の箱を用意しておく。
  • つぎにCharBuffer cb に入力値を入れる。
  • 読み取れないバイトコードをデフォルトのリプレース文字(?)に変更するエンコーダーを作成する。
    • onMalformedInputは入力値に不正な(Malformed)文字がないか判定する
    • onUnmapppableCharacterエンコード後のバイトが変換先の文字コードに存在するか確認する(例:SHIFT_JISに変換するなら①とか)。
    • reset()は礼儀。
  • エンコードを通す。すると、bbに入る分だけ文字が入る。2バイト文字等で最後まで入りきらない場合は、あきらめてくれる。
  • flush()でメモリ内に残ったデータを排出する。
  • bbを反転させて、データが入っているところだけを有効化する。
  • デコードする(ここは簡易メソッドでいいはず)。

バイトに直した後に、わざと小さい箱を用意して入れるだけ入れて、文字列に戻すという操作をする。

もっといい方法はありそうだけど、とりあえずこれでいいか相談だ。

雑感

文字コードを忌避していた節があったから、たった一つのメソッドだけど勉強になった。

参考にしたのはこの辺

www5d.biglobe.ne.jp

すごい煽ってくるけどすごく参考になりました。ありがとうございます。

qiita.com

Shift-JISとMS932の違いについて解説している

 

あと、記事書くのに時間かかり過ぎだ。もっと気楽な気持ちで書きたいな。