Vimで小数を四捨五入して置換

渡されたSVGファイルを見たらpath要素のアンカーポイントの数字が小数点以下6桁くらいから30桁まで混在していて、無駄な感じがあった。SVGOでできるのでそれでやっても良かったが、まずは単純に小数を指定桁(3から5桁)で四捨五入したいだけなので他に何かされてしまう可能性があるツールはちょっと避けたい。ということでVimの置換でどうにかした。

コマンドとしては長いがやっていることは普通なので、後述の説明が理解できればソラで打てるんじゃないかと思う。

:%s@\d\+\.\d\+@\=round(str2float(submatch(0))*1000)/1000@g

例えば、

 0.12345
12.34567
 0.99999
56.78999

は、

 0.123
12.346
 1.0
56.79

と置換される。それぞれ切り捨て、切り上げ、切り上げて余分な0を削除、切り上げて余分な0を削除と置換されている。できれば1.01になって欲しいが、round()の仕様上少し面倒くさそう。


使うのは

になる。

:substitute

全置換なので:s[ubstitute]%を付けてgフラグ付きで行う。通常は/で検索パターンと置換パターンを区切るが、置換側で/を使いたいので@を使って区切るとエスケープで頭が混乱することはない。

sub-replace-special (:s\=)

単純な置換ではなく、検索した文字列を加工したいので、置換パターンの方を\=で始めて、Vimの内蔵関数を使えるようにする。

submatch()

通常の置換では置換パターンで\0を使うことで、検索パターン全体のマッチ拾うことが出来る。しかし\=ではそれは使えない(と思う)ので、代わりにsubmatch()で直前の検索パターンの全体のマッチ(及びサブマッチ)を拾うことができるのでこれを使う。ここではマッチ全体を拾えば良いのでsubmatch(0)で良い。

str2float()

検索文字列はStringなのでFloatへキャストする必要がある。暗黙の型変換でもうまくいくような気がするが念のためキャストした方が良い。

round()

小数の丸めには3種類あるが、四捨五入したい場合はround()を使う。しかしこれは強制的に整数へ(.0付きで)丸められるため、事前に維持したい桁数だけ少数点をずらしておく必要がある。ここでは3桁維持するとして、まず1000を掛けておき、round()した後に1000で割って戻す。


ちゃんと関数を書かないと面倒かと思ったが、それほどでもなかった。