Vimでテキストファイルを一括編集 実習編 −2

最後に
複数のファイルに対して処理をします。

まず現在のいるディレクトリを確認しましょう。
:pwd
変えたければ:cdです。
気をつけたいのはVimから出ないことです。
:qでVimを抜けて、シェルに戻ってしまうとマクロは消えてしまいます。

作業したいディレクトリに移動したらargsコマンドとワイルドカードを使って処理したいファイルを選びます。
:args *
今回はカレントディレクトリ下のすべてのファイルを選びました。

:args
と引数無しで実行すると開いているファイルのリストが得られます。
現在表示されているファイルは[]で囲まれています。
ファイル間の移動は
:first , :last , :prev , :next
といったコマンドで行います

開いている複数のファイルに対してマクロを使う場合は
:argdo normal @[レジスタ]
です。

一括処理をする前に:firstで先頭のファイルに移動しておきましょう。
1つのファイル全体に適用するマクロはzに保存しましたので
:argdo normal @z
で開いている全部のファイルに適用できそうです。
ですが、エラーが出ました。
最後の変更が保存されていません。とのこと。
いけません、忘れていました。
:argdo などのコマンドを使うときは
:set hidden  (:nextなどのコマンドに!をつけたことにするオプション)
の設定をして、ファイルを保存していなくても次のファイルに移れるようにする必要があります。

さてこれですべてOK。


ずいぶん長くなってしまったので今までやったことを簡潔にまとめます。
複数のファイルに対して一括で処理を行うには
1.1行向けのマクロを保存する(qx[操作]q)
2.ファイルの先頭から末尾まで適用するマクロを保存する(qz:%normal @xq)
3.複数のファイルを開きマクロを適用する (:set hiddenしてから :argdo normal @z)
注.レジスタxやzはアルファベット1文字で自由に指定できる。
注.マクロはVimを終了すると消えてしまう。

Vimでテキストファイルを一括編集 実習編 −1

複数のファイルに対して一括編集 実習編−1

前回は導入編ということで予備知識というかそういった説明をしました。今回は実践編です。

前回も書いたとおり、いきなりでは難しいので3つのステップを踏みます。
1.一行に対して処理をするマクロをつくる
2.ファイル内の各行に1.の処理を適用するマクロをつくる。
3.複数のファイルに対して2.の処理を連続して実行する。

今回やるのは2.までです

実際に使うのは次のようなテキストです。

再生: 君の知らない物語 (テレビアニメ『化物語』エンディングテーマ) 1. 君の知らない物語 (テレビアニメ『化物語』エンディングテーマ)
再生: カサブタ (テレビアニメ『金色のガッシュベル!!』オープニングテーマ) 2. カサブタ (テレビアニメ『金色のガッシュベル!!』オープニングテーマ)
再生: おジャ魔女カーニバル!! (テレビアニメ『おジャ魔女どれみ』オープニングテーマ) 3. おジャ魔女カーニバル!! (テレビアニメ『おジャ魔女どれみ』オープニングテーマ)
再生: 英雄 (特撮テレビドラマ『ウルトラマンネクサス』第1期オープニングテーマ) 4. 英雄 (特撮テレビドラマ『ウルトラマンネクサス』第1期オープニングテーマ)
再生: Snow halation (テレビアニメ『ラブライブ!』第2期挿入歌) 5. Snow halation (テレビアニメ『ラブライブ!』第2期挿入歌)
再生: DREAM SOLISTER (テレビアニメ『響け!ユーフォニアム』オープニングテーマ) 6. DREAM SOLISTER (テレビアニメ『響け!ユーフォニアム』オープニングテーマ)
再生: ペガサス幻想 (テレビアニメ『聖闘士星矢』オープニングテーマ) 7. ペガサス幻想 (テレビアニメ『聖闘士星矢』オープニングテーマ)
再生: 僕たちの行方 (テレビアニメ『機動戦士ガンダムSEED DESTINY』第3期オープニングテーマ) 8. 僕たちの行方 (テレビアニメ『機動戦士ガンダムSEED DESTINY』第3期オープニングテーマ)
再生: My Soul,Your Beats! (テレビアニメ『Angel Beats!』オープニングテーマ) 9. My Soul,Your Beats! (テレビアニメ『Angel Beats!』オープニングテーマ)
再生: 君が好きだと叫びたい (テレビアニメ『SLAM DUNK』オープニングテーマ) 10. 君が好きだと叫びたい (テレビアニメ『SLAM DUNK』オープニングテーマ)
再生: Good Bye,Good Luck (テレビアニメ『NARUTO -ナルト- 少年篇』オープニングテーマ) 11. Good Bye,Good Luck (テレビアニメ『NARUTO -ナルト- 少年篇』オープニングテーマ)
再生: オリオンをなぞる (テレビアニメ『TIGER & BUNNY』オープニングテーマ) 12. オリオンをなぞる (テレビアニメ『TIGER & BUNNY』オープニングテーマ)
再生: またあした (テレビアニメ『みつどもえ』『本気戦隊ガチレンジャー』主題歌) 13. またあした (テレビアニメ『みつどもえ』『本気戦隊ガチレンジャー』主題歌)

これはEnson3というアルバムの商品説明部分(amazonによる)からその曲名リストだけをコピペしたものです。
元はこんな感じでした。
enson6.png
1行毎に曲名が並んでいますがテキストとしてコピーしたものはトラック番号を挟んでタイトルと番組名が2回出てきます。これをトラック番号以下だけを残したいと思います。

通常のVimの操作はこんな感じです。

d/[0-9]\+\.

 
dは切り取りオペレータ
/は検索で
正規表現を使ってトラック番号にマッチするようにします。
[0-9]は0〜9の数字にマッチし、\+は直前の文字の1回以上の繰り返し([0-9]*[0-9]とおなじ)
\.で次に.ドットが来るパターンにマッチするようにしました。

これでトラック番号以下が残りました。
これで良さそうなので(uや:edit!で)変更前の状態に戻してから
一連の操作をマクロにします。
マクロは
q[レジスタ][操作]q
で記録し
@[レジスタ]
で実行します。
レジスタとは記憶領域でa-zの好きな1文字を使えばよいです。

ではxレジスタに操作を記録しましょう。
qxd/[0-9]\+\.[Enter]q
jで次の行に移って@xをするとうまいことできています。
10@xと打てば操作を10回繰り返すことができるのですが
それだと次の行に移るコマンドがないので失敗します。
これを避けるためにはマクロの最後にjを加えます。
qy@xjq
と打てばyレジスタにxの内容とjがつながって入ります。
(qXjqとすればxに上書きすることも可能です。が、今回はxを別の使い方をしますので上書きしません。)

10@yとすれば10行分処理できています。
+g(コントロールキーを押しながらgを打つ)でファイルの行数がわかりますので
14@yを打つとファイル末尾までうまくいきそうです。
が、内容が消えてしまいました。
これはファイル末尾まで行って先頭からもう一度検索してしまったためです。
勝手に先頭に戻らないようにするには
:set nowrapscan
というコマンドを実行しておきましょう。
これなら100@yと打っても末尾まで行ったら検索が終わりますので思っていた通りの動作をします。

ここまでできれば
ファイルの先頭から処理することはできます。
ですが不便なのは繰り返す回数を指定すること。
今回は多めに指定すればいいので難しくないのですが違うケースでは厄介になるかもしれません。

1行向けに書いたマクロをファイルの各行に適用することができるコマンドがあります。
:%normal @[レジスタ]
このコマンドを使えば柔軟な繰り返しが可能になります。(興味がある方は実践VimやほかのHPをご覧ください)

1行向けのマクロ(次の行に移動しないバージョン)はxレジスタに保存してあるので
:%normal @x
これでスマートに繰り返しを行うことが出来ました。
次の処理のためにこれもマクロにしましょう。
qz:%normal @xq
でzレジスタに保存します。

Vimでテキストファイルを一括編集 導入編

(※Vimの操作はわかるから早く知りたい方は次の記事をどうぞ。今回は導入です。)

わたしは普段テキストエディターとしてVimを使いません。日本語入力を切り替えながら使用するのが煩雑だからです。それでもVimは好きで使います。どんなときにVimを使うかというとすでにあるリストを自分が使いやすいように整形するときです。

きれいに区切られたCSVファイルの場合はUnixコマンドのcutとかsedを組み合わせるとうまくいくでしょう。しかし、人からもらった、あるいはネットで拾ってきたテキストにはそれほどきれいではないものもあると思います。

今回はそういったファイルをVimを使って自分好みに整形するというネタです。
目標としては特定のディレクトリにあるファイルに対して一括で処理するところまでやります。

いきなり複数のファイルに対して処理をするのはこんがらがるのでいくつかのステップに分けます。

1.一行に対して処理をするマクロをつくる

2.ファイル内の各行に1.の処理を適用するマクロをつくる

3.複数のファイルに対して2.の処理を連続して実行する。

マクロというのは簡単に言えば自分の行った操作をVimに覚えてもらうことです。エクセルのマクロの記録とおなじです。マクロはVimScript と言われるものとは違いVimの基本的なコマンドを積み重ねる感じです。条件分岐をするとか複雑な処理はできませんが自分に必要な処理をパズル的に組み合 わせるだけですのでプログラミングの知識がなくてもできると思います。(予め処理を決めておくという広い意味ではプログラミングかも?)

ただ最低限自分のやりたいことが実現できる程度にVimのノーマルモードでの操作は慣れておいて欲しいです。ノーマルモードの操作は難しく言えばオペレータとモーションとその組み合わせです。

・オペレータ
aやiで挿入モードに移る
dで文字列を切り取る、pで貼り付けるなど

・モーション
bで1つ前の単語にもどる、wで次の単語に進む
0,$で行頭、行末に移る
/で検索して移動など

あとは簡単なコマンドとして:sで置換あたりを知っていれば十分だと思います。

たぶんVimを使う方ならこのくらいはできるはずですし、知らなければ覚えればいいし、忘れたり探したりするときは「Vim 置換」とかでググればいいですよね。一覧が欲しければVimを起動して:help operatorと打てばよいです。(英語ですが)

以下のページも参考になります。
よく使う Vim のコマンドまとめ
Vimコマンド : 便利一覧
こういう機能があると知っておくだけで十分でしょう。使いたくなったら調べればいいし、繰り返し使えばそのうち覚えます。

次は実践編ー1です。

テキストファイルを1行ずつクリップボードにコピー

ウェブブラウザで動作します。テキストファイルを読み込むと1行ごとにコピーボタンが作成されクリックするとクリップボードにコピーできます。半自動化?

たとえばファイルのリネームとか一括でしてくれるソフトはありますし、同じようなことをはるかに便利にコピーできるソフトもあると思いますが、ブラウザで実行できたら便利だと思い作りました。

・対応しているファイル形式はUTF-8です。読み込んだファイルが文字化けしてしまう場合はフォーマットがUTF-8でない可能性があります。たとえばWindowsのメモ帳だと「ファイルを保存」するときにANSIとかShift-JISになっている部分ををUTF-8に変更してください。。

・別のファイルを選択するときはリセットのためページの再読み込みをしてください。

以下のソースをコピーしてhtmlファイルとして保存してください。

 

<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title> テキストファイルを1行ずつコピーする </title>
    </head>
    
    <body>
        <form name="test">
            <input type="file" id="file" >
            <input type="button" id="reset" value="注意書き" onclick=showNotes()>
            <input type="button" id="reset" value="リセット" onclick=location.reload()>
            <div id = "output"> </div>            <!--テキストとコピーボタンを表示するためのエリア-->
        </form>
        
        <!---------------- プログラム部分はここから ---------------->
        <script>
            //ファイルの中身を1行毎に収めた配列(グローバル)            
            var arrayOfContents;

            //ファイルが選択されたら一連の関数を呼び出す
            document.getElementById("file").addEventListener('change',readFile,false);

            //ファイルを読み込んで処理を始める
            function readFile(evt){

                var f = evt.target.files;
                var contents;

                var reader = new FileReader();
                reader.readAsText(f[0]);
                //ファイルが読み込まれたら
                reader.addEventListener("load",function(){
                    contents = reader.result;
                    //ファイルの中身を改行文字で区切る
                    arrayOfContents = contents.split(/\r\n|\r|\n/);
                    makeFields();
                    addEvents();
                },false);
            }

            //テキストフィールドとコピー用のボタンを作る関数
            function makeFields() {
                var output = document.getElementById('output');
                // リスト要素の作成
                var p = document.createElement('p');
                p.id = 'form';

                var i;
                var len = arrayOfContents.length;
                for ( i=0; i < len; i++) {
                    //テキストフィールドの作成
                    var text = document.createElement("input");
                    text.type = 'text';
                    text.id = 'text_' + i;
                    text.value = arrayOfContents[i];
                    p.appendChild(text);
                    // ボタンの作成
                    var input = document.createElement('input');
                    input.type = 'button';
                    input.id = 'btn_'  + i;
                    input.className = "btn";
                    input.value = (i+1) + "行目をコピー";
                    p.appendChild(input);
                    var br = document.createElement("br");
                    p.appendChild(br);
                }
                output.appendChild(p);
            }

            //隣のテキストをクリップボードにコピーする関数
            function copyText(n){
                document.getElementById("text_" + n ).select();
                document.execCommand("copy");
            }

            //イベントの設定
            function addEvents(){
                var buttons = document.getElementsByClassName("btn");
                var len = arrayOfContents.length;
                for (var j = 0;  j < len; j++) (function(n) {
                  buttons[n].addEventListener('click', function() {
                    copyText(n);
                  }, false);
                })(j);
            }
            //注意書きを表示
            function showNotes(){
                var notes = '使用上の注意・ファイルはUTF-8形式に変更してから使ってください';
                alert(notes);
            }

        </script>
        <!---------------- プログラム部分はここまで ---------------->
    </body>
<html>

```