エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

TabをSpaceに展開(復刻版)


必要に迫られて書き直すなど。
せっかくなのでイテレータで受け付けるようにしてみた。
加えて。前回のはシングルラインしかきちんと処理できなかったのを、マルチラインも処理できるようにした。条件をふたつ加えただけですけど。

#ifndef EMATTSAN_TABTOSPACE_H
#define EMATTSAN_TABTOSPACE_H

namespace emattsan
{

template<typename InputIterator, typename OutputIterator>
OutputIterator tabToSpace(int tabSize, InputIterator begin, InputIterator end, OutputIterator out)
{
    int spaceSize = tabSize;
    for(InputIterator i = begin; i != end; ++i)
    {
        if(*i == '\t')
        {
            while(spaceSize-- != 0)
            {
                *out = ' ';
                ++out;
            }
            spaceSize = tabSize;
        }
        else
        {
            *out = *i;
            ++out;
            --spaceSize;
            if((spaceSize == 0) || (*i == '\n') || (*i == '\r'))
            {
                spaceSize = tabSize;
            }
        }
    }

    return out;
}

} // namespace emattsan

#endif//EMATTSAN_TABTOSPACE_H

実践その1:std::stringからstd::stringへ。

#include "TabToSpace.h"

#include <iostream>
#include <string>

int main(int, char* [])
{
    std::string source("\ta\tbc\tdef\na\tbc\td\n");

    // 全部TABだったときの最大長を確保すれば大丈夫だろう、という大雑把な確保の仕方
    std::string result(source.size() * 4, '\0');

    // 戻り値に最後の文字の次の位置が返る
    std::string::iterator eos = emattsan::tabToSpace(4, source.begin(), source.end(), result.begin());

    // 余った領域の文字列を削除
    result.erase(eos, result.end());

    std::cout << result << std::endl;

    return 0;
}


実行結果は省略。

実践その2:std::stringからstd::stringへ、再。

文字列が伸びるコストを無視していいなら、最初に領域を確保しないで伸びるに任せてもOK。そのばあいstd::back_insert_iteratorを利用。std::back_inserter関数テンプレートを使えば自動生成してくれます。

#include "TabToSpace.h"

#include <iostream>
#include <string>
#include <iterator>

int main(int, char* [])
{
    std::string source("\ta\tbc\tdef\na\tbc\td\n");
    std::string result;

    emattsan::tabToSpace(4, source.begin(), source.end(), std::back_inserter(result));

    std::cout << result << std::endl;

    return 0;
}

実践その3:C文字列からC文字列へ。

ポインタもイテレータ。ゆえにポインタをわたしてもOK。

#include "TabToSpace.h"

#include <iostream>
#include <cstring>

int main(int, char* [])
{
    char source[] = "\ta\tbc\tdef\na\tbc\td\n";

    // 全部TABだったときの最大長を確保すれば大丈夫だろう、という大雑把な確保の仕方
    char result[sizeof(source) * 4];

    // 戻り値に最後の文字の次の位置が返る
    char* eos = emattsan::tabToSpace(4, source, source + std::strlen(source), result);

    // ゼロ終端を書き込む
    *eos = '\0';

    std::cout << result << std::endl;

    return 0;
}

実践その4:ストリームからストリームへ。

std::istream_iteratorstd::istream_iteratorを使えばストリームもイテレータで扱えるのでストリームもOK。この例では標準入力から標準出力へ。

#include "TabToSpace.h"

#include <iostream>
#include <iterator>

int main(int, char* [])
{
    // 空白を読みとばす (skip white space) フラグをオフにする
    std::cin.unsetf(std::ios::skipws);

    std::istream_iterator<char> begin(std::cin); // 読み始めの位置をあらわすイテレータ
    std::istream_iterator<char> end;             // 読み終わりの位置をあらわすイテレータ
    std::ostream_iterator<char> out(std::cout);  // 書き始めの位置をあらわすイテレータ

    emattsan::tabToSpace(4, begin, end, out);

    return 0;
}