C++勉強メモ(文字コード変換&改行コード)
文字コード変換と改行コード周りで勉強したことをメモ。Windowsだけ、あるいはLinuxだけ使うならあまり考えなくてもよいが、両方対応しようとしたらちょっと面倒だったのでメモしておく。なお、本稿途中のコードでは、最後に掲載するコードをライブラリとして使っている。
UTF8のテキストを文字化けなくコンソールに出力する
Windowsだと多くの環境では文字化けするのでSJISに直す(ふと思ったが、海外版のWindowsだったらデフォルトエンコーディングどうなってるんだろう、、)。
utf8_to_term_enc(line)
Powershell Core (v.7.1.3)なら
[console]::OutputEncoding = New-Object System.Text.UTF8Encoding
とすればUTF8でも文字化けしないが、SJISで出力しておいた方が無難だろう。
ちなみに、コマンドプロンプトやWindows Powershellのときはchcp 65001
と入力するとUTF8を文字化けなく表示できる。
getlineの出力をtrimする
改行コードがCRLFのテキストをgetlineで読み込むと、Linuxの場合最後に'\r'がつくのでこれをtrimする。
std::ifstream input_file("input-utf8.txt"); std::string line; while (getline_rtrim(input_file, line)) { cout << line << endl; }
上2つの合わせ技
UTF8のテキストを読込&ターミナルに出力する場合は、以下のようにしておけばWindows, Linuxで同じ結果が得られる。
std::ifstream input_file("input-utf8.txt"); std::string line; while (getline_rtrim(input_file, line)) { cout << utf8_to_term_enc(line) << endl; }
LinuxでBOM付きのUTF-8を出力する
計算結果をCSVファイルに出力する際、LinuxだとエンコーディングがUTF-8になってしまいExcelで開くと文字化けする。これを防止するために、CSVファイルの最初にBOMをいれる。
std::ofstream ofs("result.csv");
add_utf8_bom(ofs);
ライブラリでは、Linuxの場合ICUを使ってUTF8⇔SJIS変換を行うようにしているので、こっちを使ってもよいかも。
utf8_to_sjis("utf8 string"); sjis_to_utf8("sjis string");
210811_追記
ICUを使うと文字エンコーディングの判定ができたのでライブラリに入れてみた。
auto results = chardet("エンコーディングが判定できるくらい長い文章を入れる").value(); cout << "Detected encoding: " << results.enc << ", " << results.confidence << endl; // Detected encoding: UTF-8, 100
ライブラリ
QMK Firmwareが焼けるマクロパッド「YMD09」を買った
自分でファームウェア(QMK Firmware)を焼けるマクロパッド「YMD09」を購入したのでメモしておく。
Teamsのミュートショートカット(Ctrl+Shift+M)を押せるマクロパッドを探しており、最初はStream DeckかKoolertronのマクロパッドを買おうかと思っていた。だが、Stream Deckは値段が高く、かつ設定がPCに保存されるそうなので却下した。リモートワーク用のPCにも使いたいので、通常のキーボードとして認識してくれることが望ましい。
CORSAIR elgato STREAM DECK XL ライブコンテンツ作成コントローラー メーカー保証2年 USB3.0 LCDボタン:32個 10GAT9901
- 発売日: 2019/07/12
- メディア: Personal Computers
Koolertronのマクロパッドはその点申し分ないが、設定ソフトウェアがプロプライエタリなため購入に踏み切れなかった。
その後見つけたのがYMD09だ。YMD09は9キーのマクロパッドであり、キーボード向けのオープンソースなファームウェア「QMK Firmware」をインストールできる。しかも、QMKの設定を簡単に変更できるVIA(or Remap)にも対応しており、使い勝手もよい。 さらに、完成品にもかかわらず約3000円と値段も安く、購入に踏み切った次第だ。
手元に届いた状態でキーボード用ファームウェアが既に焼かれていたが、今となっては何が入っていたのかよくわからない。 分解した後、Reset用のスルーホールをショートさせてブートローダーを起動し、以下のページからダウンロードできるVIAのファームウェアをQMK ToolboxでYMD09にインストールした。
あとは、以下のページを読みつつ設定を進めた。
salicylic-acid3.hatenablog.com
salicylic-acid3.hatenablog.com
設定をしていく中で一つ問題が生じた。レイヤーのインジケータとしてRGB LEDを使いたかった(レイヤー1だったら青く光らせるなど)のだが、VIA、Remapでは設定不可だった。ググったところ、QMKの設定を変更すれば対応できるとのことだった。QMKの設定変更というと難しく感じるが、QMKの内部機能に手を入れる必要はない。設定ファイルを編集すればよいだけだ。具体的には、VIAが有効になっているファームウェアのkeymap.cを編集すればよい。詳細は下記ページに譲るが、レイヤー変更のたびに呼ばれる関数 layer_state_set_user
の中で、LEDカラー設定関数rgblight_set_layer_state
を呼ぶ。上記設定の場合、LED(正確にはRGBLIGHT_LAYER_SEGMENTS
で指定したLED)の色以外はVIAで(すなわちファームウェアの再インストールなしに)設定できるので、VIAによる柔軟性を維持できる。
参考のため、編集したファイルを以下のgistにおいておく。
qmk_firmware/keyboards/ymdk/ymd09/keymaps
以下に新しくディレクトリを作成し、以下のファイルを置いて書き込めば使用可能だ。デフォルトだとLEDがまぶしすぎたため、RGBLIGHT_LIMIT_VAL を128に設定している。
言うまでもないが、マクロパッドはAutoHotkeyと親和性が高い。 マクロパッドを買うような人は既にインストールしていることと思うが、念のため宣伝しておく。
参考
いいかんじのLatexテンプレートをつくった
LaTexは慣れれば非常に良い技術文書作成ツールだが、残念ながら、その段階に至るまでの道のりが険しすぎる。文献リストから参考文献を自動生成するためにbibtexまわりを調べたり、キャプションをいいかんじにするためにcaptionパッケージやsubcaptionパッケージに与える引数を調整したりしていると、「文章を書く」という本来の目的を忘れそうになる。まるで、環境構築に非常に時間のかかるプログラミング言語を触っているようだ。
多くの人はLaTexの細かな設定には興味がなく、「それなりに整って見える文書」が欲しいだけだと思う。そこで「いいかんじのLatexテンプレート:PrettyLatexReport」を作った。
インストール後、
\usepackage{PrettyLatexReport}
としてincludeするだけで、論文によくある「図1(a)」のような表示や、bibファイルからの参考文献リスト自動作成ができるようになる。PDFのしおりも挿入される。リポジトリには.latexmkrc
も同梱しているので、Perlが動けば*1latexmk
コマンドを打つだけでコンパイルできる。インストールの仕方等、詳細は上記ページのREADMEを参照のこと。
ちなみに、テンプレートと言ってもベースはbxjsarticleであり(template.tex参照)、本体であるPrettyLatexReport.styの中では、必要なパッケージをincludeしつつ、figureまわり、「PDFのしおり」まわり、bibtexまわり等の煩雑な設定をしているだけだ。ただ、そのような設定をすべてstyに押し付けたおかげで、メインファイル(texファイル)では文章を書くことに集中できる。
学位論文申請用のテンプレートも作ったのだが、こちらはもう使う予定がないのでアーカイブしてしまった。Reportの方は会社業務で使っているので、大学を離れた今でもメンテしている。
M5Paper公式ライブラリのOSSライセンス違反について
M5Paperの公式ライブラリであるM5EPDのコードを読んでいたところ、ソフトウェアライセンス的にまずい点を見つけたので記録しておく。なお、issueは以下の通り約1か月前に投稿済みだが、返答なし。
まずい点は、MITライセンスで公開しているにもかかわらず
を含んでいることである。いずれもコピーレフト型ライセンスなので、MITライセンスは使えない。
※ライセンス周りはあまり詳しくないので、間違っていたら指摘してください。
GPL-3.0配布のソフトウェア
SHT3x.hとSHT3x.cppが該当すると思われる。元リポジトリはおそらくhttps://github.com/Risele/SHT3x。GPL-3.0を含むということは、M5EPDだけでなくM5EPDに依存するソフトはすべてGPL-3.0で配布する必要がでてくるため、影響範囲が大きい。
CC BY-SA 3.0配布のソフトウェア
Button.hとButton.cpp。CC-BY3.0SAの資料を改変したり、加工した場合には、同じくCC-BY3.0SAで配布する必要がある。CC-BY4.0SAはGPL-3.0に対して一方向の互換性があるが、CC-BY3.0SAには互換性はないので、上記のGPL-3.0と両立するのか不明。ちなみに、Arduino Button Libraryの最新版はGPL-3.0で配布されている。
なお、CC-BY3.0SAをincludeしたコード(すなわち、GPLでいうところの「派生物」)はCC-BY3.0SAで配布する必要があるのか、私はよくわかっていない、、(CCはコードへの適用を想定していないのでグレーゾーンか?)
LGPL-2.1配布のソフトウェア
CommUtil.hとM5Timer.h。他コードからincludeはされていない。なお、M5Stackライブラリは上記ファイルをincludeしている。
M5Stack製品はハードの出来はものすごくよいと思うので、ソフトウェア周りも頑張ってほしいと思う次第である。
なお、私が公開しているM5Paper-Dashboardでは、自身でフォークしたM5EPDを使用している。
上記ファイルをすべて削除しているため、ライセンス的にはクリーン。ボタンにはezButtonライブラリを使用している。この影響で、ボタンに関するAPIが変わっている。また、画面描画にはLovyanGFXを使用することを想定し、もともとM5EPDに入っていた描画系コードも削除している。
中心力場における単一質点の軌道の可視化
最近、V.I.アーノルド「古典力学の数学的方法」を読んでいるのだが、かっこいい図があったので自分で描いてみた。
- 作者:V.I.アーノルド
- 発売日: 2003/05/28
- メディア: 単行本
エディタのAtomみたいな画像である。 以下、どう上の画像を描いたかかの説明。
いま、平面上の中心力場(質点にはたらく力の向きが「原点から質点へ向かう単位ベクトルかその逆ベクトル」であり、力の大きさも原点からの距離のみに依存する場。例えば重力場。)における、質量1の質点の運動を考える。支配方程式は、
である。この場合、原点からの距離は、次のポテンシャルエネルギー(有効ポテンシャルエネルギー)をもつ一次元の問題に従う。
すなわち、
である。 簡単にいうと、は、有効ポテンシャルエネルギーがなす「坂」を滑らかに転がったときの、地面に平行な方向への変位と同様に変化する。
また、今回のケースにおいては、極座標における角度とは次の関係がある。
ここで、は全エネルギー
である。 したがって、ポテンシャルの形状、および、を決めれば数値的に軌道を求めることができる。なお、の形状によっては、軌道を解析に求められる。例えば、重力場()では円錐曲線がの式より導かれる。
ここまで説明してようやく画像の話に戻れる。 当該画像は、
と設定したときの軌道である。 以下のPythonスクリプトにより、をRunge-Kutta45で積分して描画した。 のときにの分母が0になってしまいうまく計算できないので多少の誤差はある(と設定し、楕円曲線からズレていくのを見ると明らかだ)が、 解析解は(多分)ないので仕方ないだろう。
from functools import partial import matplotlib.pyplot as plt import numpy as np from scipy.integrate import solve_ivp def U(r): return 0.5 * r ** 3 def V(r): return U(r) + 0.5 / r ** 2 def dphi(r, E): return 1 / r ** 2 * (1 / np.sqrt(2 * (E - V(r)))) r_min = 0.50842208660526406332007805917088124001667877021803 r_max = 1.5286429152095940020185327716896748970412268021517 E = 2 fun = partial(dphi, E=E) r_eval = np.arange(r_min, r_max, 0.001) sol = solve_ivp(lambda r_, phi: fun(r_), [r_min, r_max], [0], t_eval=r_eval) phi = sol.y.flatten() r = sol.t ax = plt.subplot(111, projection="polar") for i in range(26): ax.plot(phi, r, color="k") r = r[::-1] phi = 2 * phi[-1] - phi[::-1] circle_phi = np.arange(0, 2 * np.pi, 0.01) ax.plot(circle_phi, r_min * np.ones(circle_phi.shape), color="k") ax.plot(circle_phi, r_max * np.ones(circle_phi.shape), color="k") ax.axis("off") plt.savefig("orbit.png", bbox_inches="tight", dpi=200) plt.show()
ちなみに、最初の画像の軌道は「ドーナツ」の部分を稠密に満たす。以下は十分に時間がたった時(range(26*6)
)の、初期時刻からの軌道である。
M5PaperでNTPサーバの時刻をRTCに正しく設定する
M5Paper開発の際、NTPで取得してRTCに設定した時刻がたまにズレて困っていたが、configTzTime()
を呼んだ後にsntp_get_sync_status()
で同期状態を確認するようにしたら直った。sntp_get_sync_status()
を紹介している日本語記事は見当たらなかったので、短いが記事にしておく。
最初は以下のようなコードを書いていたが、分・秒の値が狂うことがあり気になっていた。
configTzTime(tz, server1, server2, server3); struct tm datetime; if (!getLocalTime(&datetime)) return 1; //error //以下でRTCに時刻を設定
ググッてみたところ、esp-idfの公式リポジトリに答えがあった。sntp_get_sync_status()
の戻り値が、SNTP_SYNC_STATUS_RESET
でない(SNTP_SYNC_STATUS_COMPLETED
かSNTP_SYNC_STATUS_IN_PROGRESS
である)ことを確認すればよさげだ。
つまり、以下のコードとすればよい。
configTzTime(tz, server1, server2, server3); struct tm datetime; int retry = 0; constexpr int retry_count = 50; while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { delay(100); } if (retry == retry_count) { return 1; //error } if (!getLocalTime(&datetime)) return 1; //error //以下でRTCに時刻を設定
ちなみに、上記のesp-idfレポジトリのコードはPublic Domainなのでコピペしても安心である。
dアニメストアの今期アニメ配信情報をGoogleカレンダーに登録する
しょぼいカレンダーにはアニメの放送日をGoogleカレンダーに表示できる機能があり、以前までは便利に使っていた。以前まで、と書いたのは、私は最近、録画派からdアニメ派に鞍替えしており、dアニメ視聴だと上述の機能が使えない*1ためである。 そこで、Golangで同様の機能を簡易に実装してみたというのが本記事の内容である。
成果物は以下のリポジトリにおいた。
これは、dアニメストアの今期アニメ一覧ページの情報をスクレイピングし、指定したアニメの配信情報を表すiCalデータを作るプログラムである。iCalデータは手動でGoogleカレンダーに読み込ませることを想定している。アニメ一覧ページのURLおよび、アニメタイトルの指定はconfigs.json
で行う。configs.json
の場所は下記のようにコマンドライン引数で与える。指定しない場合には、カレントディレクトリのものが読み込まれる。
danime-ical.exe PATH_TO_CONFIGS_JSON
生成されるiCalファイルの例は以下の通り。クールの初めの月(冬アニメだったら1月)の最初の配信曜日から、毎週の配信予定が記述されている。アニメの話数は13話を想定。
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Arran Ubels//Golang ICS library METHOD:REQUEST BEGIN:VEVENT UID:のんのんびより のんすとっぷ DTSTART;TZID=Asia/Tokyo:20210101T010000 DTEND;TZID=Asia/Tokyo:20210101T013000 SUMMARY:のんのんびより のんすとっぷ RRULE:FREQ=WEEKLY;COUNT=13 END:VEVENT END:VCALENDAR
クールの始まり終わりあたりは不正確な可能性もあるが、ひとまずはこれで十分だろう