7年前に買ったHPのプリンターの調子が悪く、ページの一部分がかすれたり滲んで印刷されるようになってしまった。なので、去年末に新しいプリンターを買った。
買ったのは MF272dw という複合機タイプのモノクロレーザープリンターで15860円。
HPのプリンターは悪くなかったが、カラー印刷は不要でレーザープリンターを試してみたくなりこれにした。
今までのものより底面積は小さいが、高さが高く大きい。コピーやスキャンの結果はHPの方が奇麗だったように感じた。
]]>Linuxでは /proc/[PID]/fd でプロセスが開いているファイルディスクリプタの情報を参照できる。
例えば、次のコマンド
$ cat - | tr [:lower:] [:upper:]
の実行中に /proc にアクセスすると、cat と tr が共通のパイプを読み書きしていることが分かる。
$ ps a | grep 'cat\|tr' | grep -v grep 8776 pts/16 S+ 0:00 cat - 8777 pts/16 S+ 0:00 tr [:lower:] [:upper:] $ ls -l /proc/8776/fd /proc/8777/fd /proc/8776/fd: 合計 0 lrwx------ 1 kenichi kenichi 64 12月 11 15:59 0 -> /dev/pts/16 l-wx------ 1 kenichi kenichi 64 12月 11 15:59 1 -> 'pipe:[1252330]' lrwx------ 1 kenichi kenichi 64 12月 11 15:59 2 -> /dev/pts/16 /proc/8777/fd: 合計 0 lr-x------ 1 kenichi kenichi 64 12月 11 15:59 0 -> 'pipe:[1252330]' lrwx------ 1 kenichi kenichi 64 12月 11 15:59 1 -> /dev/pts/16 lrwx------ 1 kenichi kenichi 64 12月 11 15:59 2 -> /dev/pts/16
また、パイプを表すディスクリプタに外部から書き込みを行うこともできる。
$ echo hello > /proc/8776/fd/1 $ echo world > /proc/8777/fd/0
これにより、最初に示した cat と tr を実行している端末に
HELLO WORLD
と表示される。
]]>Windowsフォトビューアーを使えるようにするにはレジストリの変更が必要なようだ。変更内容はわりと簡単。
以上の設定をすると、画像ファイルの右クリックメニューで「プログラムから開く」を選択したときに、選択肢としてWindowsフォトビューアーが表示されるようになる。
]]>新しいPCにFirefoxをインストールした。以前の環境ではブックマークツールバーに「よく見るページ」というフォルダ(スマートフォルダ)があったのだけれど、これの作り方が分からず調べた。
どうも、最近のFirefoxにはスマートフォルダは用意されておらず、作るためのUIも無いようだ。が、メニューの[ブックマーク]→[ブックマークを管理]で開く画面から、URLとして
place:sort=8&maxResults=10
を指定したブックマークを作成したら、よく見るページに相当するフォルダを作れた。
この特殊なURL指定の仕様は見つけられなかったが、
にて少し説明されていた。
]]>メーラーをSylpheedからClaws Mailへ移行してみた。モダンな感じは一切ない。
設定変更のために ~\AppData\Roaming\Claws-mail\clawsrc を編集した所をメモ。このファイルの設定項目はマニュアルに説明がある。
左部のフォルダリストと上部のメッセージリストにて1行おきに背景色が変わるようになっていたが、これが見難いと感じた。
use_stripes_everywhere の値を0に変更することで、この表示方法をオフにできた。
use_stripes_everywhere
Enable alternately coloured lines in GtkTreeView components when set to '1' (default). Set it to '0' to disable them.
メッセージを削除したとき、デフォルトだと削除したメッセージの一つ前のメッセージが選択状態になるがこれが使いにくいと思った。
next_on_delete の値を1に変更することで、削除したメッセージの一つ後のメッセージが選択状態になるようにできた。
]]>next_on_delete
'0' or '1'. Controls the message selection after deleting, moving, or trashing a message. Default is '0', select previous message.
4年前に買ったシェーバーのバッテリーがすぐに切れるようになってしまったので買い替え。替刃に交換してからあまり経っていないので残念。
旧型の安いやつがないか探したが見つからず、現行のES-LV7Jをヨドバシカメラで30000円で買った。
以前のものと違いはないと思う。洗浄機の互換性を保つようにしてほしい。
]]>今使っているPCが5年前のものなので、新しいものを注文した。前回と同じくパソコン工房から、STYLE-S06M-134-UHX-Lというスリム型筐体のものを購入。
これで89,400円。今回はMS Officeを付けなかったのでだいぶ安くなった。Officeは必要な期間だけ365を契約すればいいだろうと思っている。
]]>system_clockから取得する現在時刻を文字列として出力すると秒未満の値も出力されるが、秒未満の値を出力しないようにするにはどうするのか。
std::cout << std::chrono::system_clock::now() << std::endl;
2023-08-23 15:41:17.7805038
<iomanip>にあるput_time()を使うと一応できる。でも gmtime とか呼びたくない。
#include <chrono> #include <iomanip> #include <iostream> #include <time.h> int main() { namespace ch = std::chrono; const auto now = ch::system_clock::now(); const auto now_t = ch::system_clock::to_time_t(now); tm tm; gmtime_s(&tm, &now_t); std::cout << std::put_time(&tm, "%F %T") << std::endl; }
2023-08-23 15:43:08
format()でフォーマット指定することで制御できそうなものだが、秒だけを出力する指定子は無いみたい。put_time()とは「%T」(というか「%S」)の挙動が違う。
time_pointの刻みを粗くして、秒単位の時刻に変換すれば秒未満の値は出力されなくなる。釈然としないが、これが想定されている制御方法のようだ。
#include <chrono> #include <iostream> int main() { namespace ch = std::chrono; const auto now = ch::system_clock::now(); const auto now2 = ch::floor<ch::seconds>(now); std::cout << now << std::endl; std::cout << std::format("{:%F %T}", now) << std::endl; std::cout << now2 << std::endl; std::cout << std::format("{:%F %T}", now2) << std::endl; }
2023-08-23 15:43:36.5353184 2023-08-23 15:43:36.5353184 2023-08-23 15:43:36 2023-08-23 15:43:36]]>
std::chronoについてメモ。C++20で色々機能が追加されており、それを前提とする。
durationは時間を表すクラステンプレート。テンプレートパラメータとして値の型と、値の1が何秒を表すかの比率を持つ。
テンプレートをインスタンス化したクラスとして seconds、milliseconds、years などの色々な型が用意されている。なお、yearsは31556952秒、monthsは2629746秒という「平均的な長さ」が単位になっていることに注意。
sなどのリテラル演算子も用意されており、これらを使ってオブジェクトを作ることもできる。
durationクラス間の変換は、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はduration_cast()やfloor()などを使い分けて行う。
#include <chrono> #include <iostream> int main() { using namespace std::literals::chrono_literals; namespace ch = std::chrono; const ch::duration<int> d1{ -100 }; const ch::milliseconds d2 = d1; const auto d3 = ch::duration_cast<ch::minutes>(d1); const auto d4 = ch::floor<ch::minutes>(d1); const auto d5 = d4 + 1s; const ch::months d6{ 1 }; const ch::years d7{ 1 }; std::cout << "d1 = " << d1 << std::endl; std::cout << "d2 = " << d2 << std::endl; std::cout << "d3 = " << d3 << std::endl; std::cout << "d4 = " << d4 << std::endl; std::cout << "d5 = " << d5 << std::endl; std::cout << "d6 = " << d6 << std::endl; std::cout << "d7 = " << d7 << std::endl; }
d1 = -100s d2 = -100000ms d3 = -1min d4 = -2min d5 = -119s d6 = 1[2629746]s d7 = 1[31556952]s
time_pointは時刻を表すクラステンプレート。テンプレートパラメータとして「時計」型と、時刻の刻み幅とするdurationを持つ。
時計型としてsystem_clockやlocal_tなどが用意されており、それらを使ってtime_pointテンプレートをインスタンス化したクラスとしてsys_secondsやlocal_daysなどが用意されている。
時刻は起点となる時刻(エポック)からの経過時間として表現されている。エポックは時計型から決まる。
特定の時刻を生成するには、/ 演算子を使ってyear_month_dayオブジェクトを作り、それをsys_daysに変換するという方法が使える。
time_pointクラス間の変換もdurationと同様で、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はtime_point_cast()やfloor()などを使い分けて行う。
#include <iostream> int main() { using namespace std::literals::chrono_literals; namespace ch = std::chrono; const ch::time_point<ch::system_clock> t1; // system_clockのエポック const ch::sys_seconds t2{ ch::months{ 1 } }; // エポックから約一か月後 const auto t3 = ch::sys_days{ 2023y/8/15 } + 12h + 34min + 56s; const ch::time_point<ch::system_clock, ch::milliseconds> t4 = t3; const auto t5 = ch::floor<ch::days>(t3); std::cout << "t1 = " << t1 << std::endl; std::cout << "t2 = " << t2 << std::endl; std::cout << "t3 = " << t3 << std::endl; std::cout << "t4 = " << t4 << std::endl; std::cout << "t5 = " << t5 << std::endl; }
t1 = 1970-01-01 00:00:00.0000000 t2 = 1970-01-31 10:29:06 t3 = 2023-08-15 12:34:56 t4 = 2023-08-15 12:34:56.000 t5 = 2023-08-15
時計は現在時刻を取得するときに使うクラス。いくつか用意されているが、通常はsystem_clockを使い、経過時間を計る際にsteady_clockを使うくらいか。
system_clock::now()が返す時刻はUTC相当のものであることに注意。システムのタイムゾーンでの時刻を取得するには、current_zone()でtime_zoneオブジェクトを取得し、そのメンバ関数to_local()を呼び出す方法が使える。
また、時刻をタイムゾーン付きで管理したい場合はzoned_timeクラステンプレートを使う。zoned_timeは時刻を表すがtime_point型ではなく、time_pointとtime_zoneのペアである。
#include <chrono> #include <iostream> int main() { namespace ch = std::chrono; const auto now1 = ch::system_clock::now(); const auto now2 = ch::current_zone()->to_local(now1); const ch::zoned_time now3{ ch::current_zone(), now1 }; std::cout << "now1 = " << now1 << std::endl; std::cout << "now2 = " << now2 << std::endl; std::cout << "now3 = " << now3 << std::endl; }
now1 = 2023-08-23 10:16:29.2074391 now2 = 2023-08-23 19:16:29.2074391 now3 = 2023-08-23 19:16:29.2074391 JST]]>
pytestでParametrizing fixturesを使った次のテストを実行すると、パラメータの識別子として表示させたい日本語部分がエスケープされてしまった。
import pytest data = [ ("テスト", "データ1"), ("テスト", "データ2"), ("テスト", "データ3"), ] def data_name(d): return f"{d[0]}-{d[1]}" @pytest.fixture(params=data, ids=data_name) def parameter(request): return request.param def test_sample(parameter): assert True
$ pytest -v test_1.py =============================== test session starts =============================== platform linux -- Python 3.11.1, pytest-7.3.1, pluggy-1.0.0 -- /home/kenichi/.anyenv/envs/pyenv/versions/3.11.1/bin/python3.11 cachedir: .pytest_cache rootdir: /home/kenichi/work/python/pytest collected 3 items test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf1] PASSED [ 33%] test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf2] PASSED [ 66%] test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf3] PASSED [100%] ================================ 3 passed in 0.01s ================================
調べると、
に次の説明があった。
pytest by default escapes any non-ascii characters used in unicode strings for the parametrization because it has several downsides. If however you would like to use unicode strings in parametrization and see them in the terminal as is (non-escaped), use this option in your pytest.ini:
[pytest] disable_test_id_escaping_and_forfeit_all_rights_to_community_support = TrueKeep in mind however that this might cause unwanted side effects and even bugs depending on the OS used and plugins currently installed, so use it at your own risk.
リスクはあるようだが、pytest.iniを用意してそこに上記の設定を書くことで、次のように日本語が表示されるようになった。
$ pytest -v test_1.py =============================== test session starts =============================== platform linux -- Python 3.11.1, pytest-7.3.1, pluggy-1.0.0 -- /home/kenichi/.anyenv/envs/pyenv/versions/3.11.1/bin/python3.11 cachedir: .pytest_cache rootdir: /home/kenichi/work/python/pytest configfile: pytest.ini collected 3 items test_1.py::test_sample[テスト-データ1] PASSED [ 33%] test_1.py::test_sample[テスト-データ2] PASSED [ 66%] test_1.py::test_sample[テスト-データ3] PASSED [100%] ================================ 3 passed in 0.01s ================================]]>
Visual Studio 2019で次のコードをビルドすると __FILE__ マクロの値が絶対パスで出力される。
#include <iostream> int main() { std::cout << __FILE__ << "\n"; }
C:\Users\kenichi\source\repos\ConsoleApplication1\d1trimfile\main.cpp
コンパイルオプションに
/d1trimfile:"$(SolutionDir)\"
を追加すると、上記の結果が相対パスに変わることを知った。
d1trimfile\main.cpp
このコンパイルオプションについてのリファレンスは見つけることができなかった。言及しているページをメモ。
]]>次のコードをVC++でコンパイルして実行すると、エラーメッセージが日本語で出力される。
#include <filesystem> #include <iostream> int main() { try { std::filesystem::copy_file("あ.txt", "ああ.txt"); } catch (const std::filesystem::filesystem_error& e) { std::cout << e.what() << std::endl; } }
copy_file: 指定されたファイルが見つかりません。: "あ.txt", "ああ.txt"
これを英語で出力されるようにするにはどうしたらいいのか?(OSの言語設定は日本語のままで)
std::setlocale(LC_ALL, "C");
を追加したらいいかなと思ったけど、こうしてもメッセージは日本語のままだった。
に
おまけ:FormatMessage関数のdwLanguageId引数=0で実装されているため、SetThreadUILanguage関数でスレッドのLANGIDを事前に変更しておく案もある。シングルスレッドプログラムならこれでもいいか...
とあるのを発見し、これを試すと英語メッセージに変わった。こうするしかない?
#include <filesystem> #include <iostream> #include <windows.h> int main() { SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); try { std::filesystem::copy_file("あ.txt", "ああ.txt"); } catch (const std::filesystem::filesystem_error& e) { std::cout << e.what() << std::endl; } }
copy_file: The system cannot find the file specified.: "あ.txt", "ああ.txt"]]>
pybind11を使ってC++で書かれたPython用のDLLのコードを、Visual Studio 2019のデバッガで開きたかった。
Visual Studioの [デバッグ] → [プロセスにアタッチ] で開くダイアログからPythonのプロセスにアタッチすればいいだろうと考えたが、DLLのコードにブレークポイントを設定してもそこで止まってくれない。
原因は、アタッチした時にPython用のデバッガが開いてしまっているからのようだ。「プロセスにアタッチ」ダイアログには「アタッチ先」という欄があって「自動」が選ばれていたが、「選択」ボタンで開く「コードの種類の選択」ダイアログで「ネイティブ」を選択したらDLLのコードのブレークポイントで止まるようになった。
Python用のデバッガが開いてしまうのはVisual Studioに「Python開発」のワークロードをインストールしているからな気がする。これが無い環境なら何も設定しなくてもC++用のデバッガが開くのかもしれない。
]]>VC++でもマニフェストファイルを用意することでマルチバイト文字列にShife-JISではなくUTF-8を使うことが自然にできるようになる。
また、localeにもUTF-8を指定できるようになっており、これにより動作が変わるようだ。
これらの設定により動作がどのように変わるのかを次のコードで試してみた。
#include <clocale> #include <cstdint> #include <cstdlib> #include <filesystem> #include <fstream> #include <iomanip> #include <iostream> #include <sstream> std::string dump(const char* s) { if (s == nullptr) return ""; std::ostringstream oss; oss << std::hex << std::setfill('0'); while (*s != '\0') { oss << std::setw(2) << static_cast<int>(static_cast<std::uint8_t>(*s)) << ' '; ++s; } return oss.str(); } int main(int argc, char* argv[]) { //std::setlocale(LC_ALL, ".UTF8"); if (argc <= 1) { std::cerr << "too few arguments" << std::endl; std::exit(2); } const char* message = argv[1]; std::cout << "raw: " << message << std::endl; std::cout << "bytes: " << dump(message) << std::endl; std::filesystem::path path{message}; std::filesystem::create_directory(path); std::filesystem::current_path(path); std::ofstream ofs{path}; ofs << message; }
以下では、実行する際に「あ」をパラメータとして指定することにする。「あ」のバイト列は、UTF-8だと「e3 81 82」、Shift-JISだと「82 a0」である。
特に何も設定せずにビルドして試すと、次のように出力される。
raw: あ bytes: 82 a0
入力した文字列はShift-JISとして読み込まれている。ファイルにもShift-JISで出力された。
ただし、コマンドプロンプトのコードページをUTF-8に変更する(chcp 65001)と、rawの行には何も表示されなくなる。上記の結果はコードページがShift-JISの場合(chcp 932)である。
VC++のプロジェクトの「リソースファイル」フィルタ配下に次の内容のファイルを拡張子「.manifest」で追加してからビルドすると、振る舞いが変わる。なお、assemblyIdentityのnameが「...」になっていることに注意。このままでも動作確認には支障がないようだ。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyIdentity type="win32" name="..." version="6.0.0.0"/> <application> <windowsSettings> <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage> </windowsSettings> </application> </assembly>
こうした場合は、コードページがShift-JISのコンソールでは次のように出力される。
raw: 縺 bytes: e3 81 82
バイト列はUTF-8に変わっているが、rawの行は文字化けする。コードページをUTF-8に変更すると文字化けせず表示される。
ファイルに出力される文字のエンコーディングもUTF-8に変わる。
マニフェストファイルがある状態で、始めに示したコードでコメントアウトしていた
std::setlocale(LC_ALL, ".UTF8");
の行を生かすように変更すると、コンソールのコードページがShift-JISとUTF-8のどちらでも
raw: あ bytes: e3 81 82
と表示されるようになった。ファイル出力も問題ない。
]]>UTF-8を使って書かれたソースコードをVC++でコンパイルすることは、正しい設定をしていれば問題ない。ただ、正しく設定されていないと分かりにくい問題が起こることがあるのでメモ。
UTF-8で保存した次のコードはVC++でコンパイルできる。
#include <iostream> int main() { int a = 0; // ああ a = 100; // あ a = 200; std::cout << "a=" << a << std::endl; }
しかし、条件が揃っていると、実行したときに
a=100
と出力される。
不思議だし、エラーになるわけではないのでこのような問題が起きていることに気づかないケースもあるだろう。
上記のようになるのは、次の条件が揃っているときだ。
自分が遭遇したのは、問題なかったコードに対してビルドできないという報告が上がってきたこと。原因は、それまではgitの設定が core.autocrlf=true な環境で使っていたのを core.autocrlf=false な環境でビルドしたため、改行コードがLFに変わったことだった。
上述の条件のどれかを変えればいいのだけれど、通常は、コンパイルオプションで文字エンコーディングを指定するのがいいだろう。
具体的には、/utf-8を指定するようにすればよい。
]]>