2025年05月28日(水) [長年日記]
■ [windows] WSLが起動しなくなったが復旧した
気づいたらWindowsが再起動していた。原因不明(原因を知る方法あるのかな?)。
その後WSLを起動しようとしたら次のエラーが出力され起動できなくなっていた。
ディスク 'C:\Program Files\WSL\system.vhd' を WSL2 にアタッチできませんでした: 指定されたファイルが見つかりません。
エラー コード: Wsl/Service/CreateInstance/CreateVm/MountDisk/HCS/ERROR_FILE_NOT_FOUND
不穏なメッセージだ…。C:\Program Files\WSL\system.vhd は存在していない。
検索すると、復旧はできるようだった。WSLの次のissueが参考になる。
このissueのこのコメントにあるように、GitHubからWSLのmsiファイルをダウンロードして、その中に含まれるsystem.vhdを C:\Program Files\WSL\ へコピーすればよい。
wsl --version コマンドで確認するとWSLのバージョンは2.5.7.0だったので、https://github.com/microsoft/WSL/releasesから wsl.2.5.7.0.x64.msi をダウンロードし、msiexecコマンドを使い次のようにしてmsiファイルをどこかへ展開し
start /wait msiexec /a wsl.2.5.7.0.x64.msi targetdir="C:\tmp\dokoka" /qn
展開先の PFiles64\WSL\system.vhd を C:\Program Files\WSL\ へコピーした。今の所これで問題なく復旧したように思われる。
msiexecの使い方は次のページを参考にした。
2025年03月28日(金) [長年日記]
■ [ruby] test-unit-runner-junitxmlのリポジトリをtest-unit公式配下に移した
6年前にリリースしたtest-unit-runner-junitxml というtest-unitのtest runnerのリポジトリを、https://github.com/test-unit 配下に置かせてもらうようになった。
次のissueがきっかけ。
2025年02月20日(木) [長年日記]
■ [c++] C++の正規表現メモ
<regex>の使い方をメモ。
正規表現オブジェクトとそれを使った文字列に対するマッチ処理について以下をメモする。
- std::basic_regex<CharT> (正規表現)
- std::regex_match() (全体マッチ判定)
- std::regex_search() (部分マッチ判定)
- std::regex_iterator<Iterator> (部分マッチ結果の列挙)
- std::regex_token_iterator<Iterator> (部分マッチ結果のサブマッチの列挙)
これら以外に文字列の置換機能もあるがそれについては割愛する。
std::basic_regex<CharT> (正規表現)
正規表現はstd::basic_regexクラステンプレートを使って表現する。
std::regex re{R"(abc (\d\d).(\d\d))"};
- std::regex は std::basic_regex<char> の別名であり、通常はこれを使うだろう。
- 「R"(...)"」部分は生文字リテラルであり、正規表現を書く際にはこの記法が役に立つ。
- 使える正規表現はデフォルトではECMAScript互換。コンストラクタで構文を切り替えることもできる。
- ひらがななどの非ASCII文字のサポートは期待できない模様(よくわかっていない)。
std::regex_match() (全体マッチ判定)
std::regex_match()を使うことで、文字列全体が正規表現にマッチするかどうかを判定できる。
void regex_match1() { static const std::regex re{R"(\w+ \d+/\d+/\d+)"}; const bool is_matched1 = std::regex_match("hello 2025/01/02", re); const bool is_matched2 = std::regex_match("hello world 2025/01/02", re); std::cout << "regex_match1" << std::endl; std::cout << is_matched1 << std::endl; //=> 1 std::cout << is_matched2 << std::endl; //=> 0 }
- 文字列の一部分ではなく全体がマッチする必要があることに注意。
regex_match()の引数にstd::match_resultsオブジェクトを指定すると、正規表現で指定したキャプチャグループ部分にマッチした内容を参照することができる。
void regex_match2() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; std::cmatch m; const bool is_matched = std::regex_match("hello 2025/01/02", m, re); std::cout << "regex_match2" << std::endl; std::cout << is_matched << std::endl; //=> 1 std::cout << m[0] << std::endl; //=> hello 2025/01/02 std::cout << m[1] << std::endl; //=> 2025 std::cout << m[2] << std::endl; //=> 01 std::cout << m[3] << std::endl; //=> 02 }
- std::cmatch は std::match_results<const char*> の別名。
- std::match_resultsクラステンプレートにはこのようなエイリアスがいくつか定義されている。が、std::string_viewを使う場合は std::match_results<std::string_view::const_iterator> などとしないといけないようだ。
- 上記の m[0] などで得られるのは単なる文字列ではなくstd::sub_matchオブジェクト。std::match_results は std::sub_match を要素に持つコンテナだと考えることができる。
std::regex_search() (部分マッチ判定)
std::regex_search()を使うことで、文字列が正規表現にマッチする部分を持つかどうかを判定できる。
void regex_search() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; std::cmatch m; const bool is_matched = std::regex_search("hello world 2025/01/02", m, re); std::cout << "regex_search" << std::endl; std::cout << is_matched << std::endl; //=> 1 std::cout << m[0] << std::endl; //=> world 2025/01/02 std::cout << m[1] << std::endl; //=> 2025 std::cout << m[2] << std::endl; //=> 01 std::cout << m[3] << std::endl; //=> 02 }
std::regex_iterator<Iterator> (部分マッチ結果の列挙)
std::regex_iteratorクラステンプレートを使うと、std::regex_search()を繰り返し呼び出し、それぞれの呼び出しに対するマッチ結果(std::match_results オブジェクト)を順番に得ることができる。
void regex_iterator() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9"; std::cout << "regex_iterator" << std::endl; for (std::sregex_iterator it{s.begin(), s.end(), re}, end; it != end; ++it) { std::cout << (*it)[0] << std::endl; std::cout << (*it)[1] << std::endl; std::cout << (*it)[2] << std::endl; std::cout << (*it)[3] << std::endl; std::cout << "---" << std::endl; } }
regex_iterator world 2025/01/02 2025 01 02 --- japan 1/2/3 1 2 3 ---
- sregex_iterator は regex_iterator<string::const_iterator> の別名。
std::regex_token_iterator<Iterator> (部分マッチ結果のサブマッチの列挙)
std::regex_token_iteratorクラステンプレートは regex_iterator に似ているが、マッチ結果ではなくサブマッチ(std::sub_match オブジェクト)の内容を直接得られるところが違う。
void regex_token_iterator() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9"; std::cout << "regex_token_iterator" << std::endl; for (std::sregex_token_iterator it{s.begin(), s.end(), re, {1, 3}}, end; it != end; ++it) { std::cout << *it << std::endl; } }
regex_token_iterator 2025 02 1 3
- sregex_token_iterator は regex_token_iterator<string::const_iterator> の別名。
- コンストラクタの4番目の引数で、何番目のサブマッチを列挙するかを指定する。ここに -1 を指定することでマッチしなかった部分を列挙することもできる。
2025年01月16日(木) [長年日記]
■ [windows][python] WindowsでのデフォルトのPython環境
Windows 11で使用するPythonはMicrosoft Storeからインストールするようにしている。
Microsoft StoreでのPythonはバージョン毎に別のアプリとして公開されているが、複数のバージョンをインストールしたときに使われるPythonをどう制御すればいいのか。
Windows上でのデフォルトのPython環境
コマンドプロンプト上などでpythonコマンドを実行したときに使われるバージョンは、次で表示される設定画面から制御できるようだ。
- [設定]→[アプリ]→[アプリの詳細設定]→[アプリ実行エイリアス]
この画面の一覧の中で、デフォルトで使用したいバージョンのPythonの設定をすべて「オン」にすることで、そのバージョンがデフォルトで使われるようになる(コマンド毎にオンにするバージョンを変えることもできるが、それは混乱の元にしかならないだろう)。
Visual Studio上でのデフォルトのPython環境
Visual Studio 2022でPythonプロジェクトを作成したとき、プロジェクトで明示的にバージョン指定をしないとPython環境として「グローバル デフォルト」と表示されるものが使われるようだ。そして、これは上述したWindows上でのデフォルト環境を変更してもそれに追従しなかった。
Visual Studio上のグローバルデフォルトは、次で表示されるPython環境ウィンドウで制御できるようだ。
- [ツール]→[Python]→[Python環境]
この画面の環境の一覧にて太字で表示されているものがグローバルデフォルトな模様。
これを変更するには、一覧で非グローバルデフォルト環境を選択し、「概要」欄の「これを新しいプロジェクトに対する既定の環境にする」をクリックすればよい。その後でPythonプロジェクトを開き直すと、Python環境として表示されるPythonのバージョンが変わった。
2024年11月26日(火) [長年日記]
■ [android][net] AndroidのBLEを使った通信で512バイトを超える送信データが破棄される
AndroidのBluetooth APIを使ったBLE通信で、送信データが破棄されれ送信されないことがあったので調べた。
原因はAndroid 13と14での動作の変更だった。
Android 13での変更
ちゃんとした資料は見つけられなかったが、Android 13から512バイトを超えるデータを送受信できなくなったというissueがちらほら見つかる。これなど。
次の変更が原因のようだ。
- Set maximum attribute value to defined by spec Core 5.3 (android.googlesource.com)
この変更は、次に示すBluetoothの仕様でattribute valueの最大長が512バイトに制限されていることへ準じるために行われている。
- 3.2.9. Long attribute values (Bluetooth Core Specification 6.0)
The maximum length of an attribute value shall be 512 octets.
どうも、Bluetoothでデータ送信を行う際のデータ長の上限としてATT(Attribute Protocol)のMTUサイズから3バイト引いた値を使うコードが多いようで(手元のコードもそうなっていた)、AndroidのATT MTUサイズは最大で517バイトになり得て、そうなったときに 517 - 3 = 515 バイトのデータを送信しようとしてしまい、その結果Android 12までであれば送信できていたのにAndroid 13になったら送信できなくなったという問題があるようだ。
Android 14での変更
Android 14では次の変更がされたとある。
- MTU is set to 517 for the first GATT client requesting an MTU (Android Developers)
これは、BluetoothGatt#requestMtu()によりMTUサイズの拡張を要求したときに引数の値によらず常に517バイトが使われるということのようだ。
手元のコードでは、上述したAndroid 13での変更に対する対策として BluetoothGatt#requestMtu() で515を使うようになっていたが、この対策はAndroid 14以降では意味がなくなってしまうことになる。
変更への対応方法
送信データの長さの上限を (MTUサイズ - 3) とするだけでは問題があるということになる。
送信データの長さの上限は (min(512, MTUサイズ - 3)) とする必要がある。
AndroidのATT MTUサイズの上限が517バイトなのは何故か
検索すると、AndroidのATT MTUサイズの上限は517バイトではなく515バイトであるべきだと書いている人を見かける。これに対しては、次のissueにGoogleの人による説明があった。
- [BLE] - Incoming packets longer then 515 dropped after BluetoothGatt.requestMtu(517) used, even though remote peer supports MTU 517 (Android Public Tracker)
I would like to provide you more details about why 517 was chosen instead of 515:
The number 517 was selected because the GATT_MAX_MTU_SIZE constant was set to 517 since the initial drop of Android Bluetooths stack in 2012.
A deeper reason is that the Bluetooth Specification allows the maximum size of an ATT attribute to be 512 bytes and the largest command ATT_PREPARE_WRITE_REQ has 5 bytes of header. Hence 512 + 5 = 517.