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

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

C/C++スタイルのコメントの除去

以前、ストリームから入力を得て、コメントを除去した結果をストリームに出力するを書いたことがあるんですが、いざ使ってみようと思うと使い勝手が悪い。ストリームが悪いわけではないけれど、読み込んだ文字列に対してコメント除去を施そうとするときとか一旦ストリームにしてやらないとならないのが面倒。
というわけで、イテレータを使って書き直してみた。

template<typename input_iterator_t, typename output_iterator_t>
output_iterator_t removeComment(input_iterator_t in, input_iterator_t end, output_iterator_t out)
{
    char c;
    char prev = 0;

    while(in != end)
    {
        c = *in++;
        if(c == '/')
        {
            prev = c;
            c = *in++;
            if(c == '*')
            {
                do
                {
                    while((in != end) && ((c = *in++) != '*'))
                    {
                        if(c == '\n')
                        {
                            *out++ = c;
                        }
                    }
                } while((in != end) && ((c = *in++) != '/'));

                c = ' ';
            }
            else if(c == '/')
            {
                while((in != end) && ((c = *in++) != '\n'))
                    ; // NOP
            }
            else
            {
                *out++ = prev;
            }
        }
        else if((c == '"') && (prev != '\\') && (prev != '\''))
        {
            do
            {
                *out++ = c;
                if(c == '\\')
                {
                    *out++ = *in++;
                }
            } while((in != end) && ((c = *in++) != '"'));
        }

        *out++ = c;
        prev = c;
    }

    return out;
}


使い方その1。
従来のようにストリームで使う。標準入力から入力を得て標準出力に出力しています。
skipwsunsetfしてやらないと、空白文字がスキップされてしまいます。

#include <iostream>
#include <iterator>

void sample1()
{
    std::cin.unsetf(std::ios::skipws);

    std::istream_iterator<char> begin(std::cin);
    std::istream_iterator<char> end;
    removeComment(begin, end, std::ostream_iterator<char>(std::cout, ""));
}


使い方その2。
文字列から文字列へ。removeCommentはコメントを除去した後の末尾の次を指すイテレータを返すので、その戻り値を使って余分な文字列をeraseメソッドで削除しています。

#include<string>

std::string sample2(const std::string& s)
{
    std::string result(s.size(), ' ');
    result.erase(removeComment(s.begin(), s.end(), result.begin()), result.end());
    return result;
}


使い方その3。
文字列から文字列へ。別解。

#include<string>
#include <iterator>

std::string sample3(const std::string& s)
{
    std::string result;
    removeComment(s.begin(), s.end(), std::back_inserter(result));
    return result;
}