標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

【解説】 CSVI v1.7.1 をリリースしました。

前に説明したのは v1.5.0 だったので、v1.6.0, v1.7.0 の変更点も合わせて

CSView → CSVI と名前を変えました

  • View という単語が名前に入ってると読み取り専用ビューアという印象があるが、今は編集可能になっている
  • 同カテゴリに CSView という名前のツールが結構ある

ということから改名した方がよいな思いつつ、今頃になってしまいました。

名前を変えると、変えてからしばらくすると微妙になってくることがたまにあるんですが、今回は大丈夫だったみたいです。今のところ、特に「えぇー」という声もなさげです (本当かな)

ただ、英大文字・英小文字の表記には迷いがあって、まだ、その時の気分で CSVI, Csvi, csvi と表記に揺らぎがあります(あかん)

バックグランド読み込み対応

「数百メガのファイルも読み込める」とお褒めの言葉をいただいたので、実際に開いてみたら、表示までしばらく待たされることが判明しました。

こりゃいかんということで、binview の internal パッケージ (internal/nonblock) を丸コピーしてきました。

最初の100行程度を読み込んだ時点で画面を開き、残りはキー入力待ちの間にデータを読み込ませるようにしました。これによって、十数秒間の待ち時間はほぼ一瞬になりました。

このあたりはノウハウがあったから実装は簡単だったんですが、ちょっと使い方が分かりにくいライブラリなので、もうちょっと見易くできないかなと試行錯誤もしたあげく、結局そのまま利用という形になりました。

UTF-16対応の強化

  • UTF-16 かどうかを判断する先読みバイト数を10バイト数に
  • 強制的にUTF-16と判断させるオプション -16le, -16be を追加

現在の判断ロジックは

  • オプション -16be が指定されていたら UTF-16BE
  • オプション -16le が指定されていたら UTF-16LE
  • \xFE\xFF で始まっていたら UTF-16BE
  • \xFF\xFE で始まっていたら UTF-16LE
  • 最初の10バイト以内の奇数バイト位置に \0 があれば UTF-16BE
  • 最初の10バイト以内の偶数バイト位置に \0 があれば UTF-16LE

という、「なんとしてもUTF-16 を見逃さないぞ」という体制になっています

なぜ、ここまで UTF-16対応に力を入れるかというと、 前職の時に作ったアプリケーションのデータファイルをUTF-16LEのTSVにしてしまったことに、すごい悔いがあった からかもしれません。 ( 閲覧も編集も面倒くさい)

自動処理対応

csvi -auto "<|$|a|ほげ|w|bar.csv|q|y" foo.csv

で、foo.csv の最初の行の末尾に「ほげ」というフィールドを追加して、bar.csv というファイルとしてセーブするという操作を指示できます。

これは実用を考えたものよりは、自動テストのために作りました1。前は Expect-Lua を使ってテストコードを書いていたんですが、

  • 端末画面がない自動テスト環境(CI)では使えない可能性がある
  • タイミングで失敗することもありえる
  • テストのプラットフォームも Windows だけになってしまう

という問題がありました。この -auto オプションを使えば、タイミングがずれることはありませんし、外部プロセスを呼び出せる任意の言語でテスト可能になります。2

なお、今回、端末入力の一般化/オートパイロット化の仕組みのコツがつかめたので、他のアプリにも適用してゆくかもしれません。

【不具合修正】幅の判定が難しい文字が現れても、表示位置がズレないよう対処した

今までは文字の幅を測って、次の列まで足りない桁分の空白を出力していたんですが、どうやっても幅が推定うまく推定できない文字というものもあるようです(runewidth ライブラリを使って、east-asian ambiguous width を配慮しても、なんかおかしい時がある)

( CSView v1.5 で、Windows10のコマンドプロント上から某CSVファイルを表示させた結果。ガタガタだ。Windows Terminal だと、ここまで酷くはない)

これに対し、もう空白を桁位置を調整する方式はあきらめて、ANSI エスケープシーケンスの ESC[%dG (水平方向の%d 桁目まで移動)で次の列位置まで移動させるようにしました。

(CSVI v1.7.1 で同じファイルを表示。きれいになった!)

この方式をやる場合、ESC[K(カーソル位置以降を削除)と組み合わせる都合上、画面書き変え量が増えるという問題があります。普通の環境だと問題はないんですが、仮想マシンの中のターミナルだと、かなりモッサリします。ですが、それでも全く使えないレベルでもないので、これは許容範囲としました。

【不具合修正】> の後の o で、最終行の前の改行コードが欠けてしまう不具合を修正

これは各行の改行コードは極力現状維持させるという仕様のせいでした。 最終行の末尾は EOF なので改行コードがないんですよね。そのまま次の行を追加すると、保存時に改行コードなしのまま出力するので、次の行と連結されてしまいます。

こういう例外ケースの場合は、デフォルト改行コード(そのファイルで最初に見付かった改行コード)を挿入するようにしました。

【不具合修正】 長い行から短い行に移動した後のカーソル位置が無効になることがあり、編集するとクラッシュする不具合(v1.6.0-)を修正

v1.6.0 で、それまでスライスで全行を管理していたんですが、ギガクラスに対応できるよう、binview 同様に "container/list" を使うよう、書き換えました。その際にエンバグしてしまいました。

たとえば、 5列ある行の5列目にカーソルがある時に3列しかない行に移動すると、v1.5.0 までだと3列目にカーソルが移動するんですが、v1.6.0-v1.7.0 ではカレントセルがない状態になってしまっていました。

この動作字体は気付いてはいたんですが、別に落ちたりする様子がなかったため、そういう仕様だったかと勘違いして放置していました。が、いざセルを編集してみたら即クラッシュ。とほほ ( 直接の原因は、ポインタを更新漏れ )


これで、CSVI は端末用としては、かなり完成に近い形になったと思います。

CSView からは、CSVI とは別に、Lisp を内蔵させてスプレッドシート化できないかとトライした Lispred という fork を作ったこともありました。こちらは行や列を挿入した時に Excel のように参照するセルの参照先を自動的に調整するのが難しく、これを完璧に実用化しても手間の割が合わないなということで放置状態になっています。

ということで、原点回帰で「 本番データの手直しに使える高速・軽量の端末向けCSVエディター 」というポリシーをこちらですすめてきたわけですが、どうやら正解だったようです。 ( STAR の数も18に増えました。まさか binview を抜くとは )


  1. 実用を考えるなら、GopherLua なり gmnlisp なりを内蔵して、本格的なスクリプティングをサポートすべきだが、そのような需要は無いと判断している。そこまでするなら、Go言語で、本ツールの内蔵ライブラリの github.com/hymkor/csvi/uncsv を import して、別ツールを作った方がよい
  2. とはいえ、作り直したテストコードは結局 PowerShell なんですけどね