4 Stimmen

Fügen Sie Vektor von Zeichenfolgen zu std::ostream hinzu (wie boost::join)

Ich habe einen Vektor von Strings und möchte ihn in einen Stream (eigentlich einen Dateistream) ausgeben. Dabei möchte ich ein Trennzeichen zwischen den Vektor-Elementen haben. Es gibt eine Möglichkeit, den standardmäßigen ostream_iterator zu verwenden.

std::vector  strs;
std::ostream_iterator out_file_iterator (out_file, delim);
std::copy(strs.begin(), strs.end(), out_file_iterator);

Ich mochte diese Methode nicht, weil nach jedem Element ein delim Text steht, aber ich brauche kein delim nach dem letzten Element. Ich würde gerne etwas wie boost::join verwenden. Allerdings gibt boost::join einen String zurück und mein Vektor ist zu groß, um ihn als String auszugeben.

Was ist der eleganteste Weg, um mein Ziel zu erreichen?

5voto

Jon Purdy Punkte 51628

Für eine allgemeine Lösung (nicht getestet):

template
class ostream_join_iterator {
public:

    // Konstruiere wie ein ostream_iterator.
    ostream_join_iterator(std::ostream& stream,
        const std::string& delimiter = "")
        : stream(stream), delimiter(delimiter), first(true) {}

    // Verhalte dich wie ein Ausgabeiterator.
    ostream_join_iterator& operator++()    { return *this; }
    ostream_join_iterator& operator++(int) { return *this; }
    ostream_join_iterator& operator*()     { return *this; }

    // Gib ein Trennzeichen vor allen außer dem ersten Element aus.
    template
    ostream_join_iterator& operator=(const T& value) {
        if (!first) {
            stream << delimiter;
        } else {
            first = false;
        }
        stream << value;
        return *this;
    }

private:

    std::ostream& stream;
    const std::string delimiter;
    bool first;

};

Du kannst es wie einen regulären std::ostream_iterator verwenden:

std::copy(strings.begin(), strings.end(),
    ostream_join_iterator(file, delimiter));

2voto

R. Martinho Fernandes Punkte 217895

Ein Weg, der funktioniert, ist, den letzten separat zu behandeln. Aber denk nicht, dass es sehr elegant ist. Natürlich könntest du die Hässlichkeit in deine eigene join-Funktion verpacken.

assert(strs.size() > 0);
std::ostream_iterator out_file_iterator ( out_file, delim );
std::copy ( strs.begin(), strs.end()-1, out_file_iterator );
out_file << strs.back();

2voto

Xeo Punkte 126280

Das Eleganteste wäre, Ihre eigene Schleife zu schreiben. Oder eine separate Funktion.

template
void print_range(Stream& s, InIt first, InIt last, char const* delim = "\n"){
  if(first == last)
    return;
  s << *first++;
  for(; first != last; ++first){
    s << delim << *first;
  }
}

0 Stimmen

Wie wäre es mit einem schnellen Test für leere Bereiche?

0 Stimmen

@Benjamin: Guter Punkt, obwohl ich denke, dass ich das eher als eine Voraussetzung auferlegt auf den Benutzer von print_range haben möchte.

1 Stimmen

Hmm, ich dachte nicht an "assert". Ich dachte vielmehr daran, einen leeren Bereich als vollkommen gültig zu behandeln und einfach nichts auszugeben. Immerhin sind leere Mengen in der realen Welt recht häufig.

-1voto

Loom Punkte 9608

Dies ist eine Idee mit einem Funktor

using namespace std;

struct add_delim_t {
  add_delim_t(const char *_delim) : delim_(_delim), is_first_(true) {}
  string operator () (const string &_val) {
    if (is_first_) { is_first_ = false; return _val; } else return delim_ + _val;
  }
private:
  const string delim_;
  bool is_first_;
};

transform(s.begin(), s.end(), ostream_iterator(cout), add_delim_t(" , "));

Das Problem bei dieser Lösung ist, dass sie einen zustandsbehafteten Prädiktor verwendet. Theoretisch bedeutet das UB.

-1voto

Loom Punkte 9608

Es gibt eine Idee mit Boost Function Input Iterator

using namespace std;

struct generator {
  typedef string result_type;
  generator(result_type _delim) : delim_(_delim), is_first_(true) {}
  result_type operator () () { 
    if (!is_first_)
      return delim_; 
    is_first_ = false; 
    return ""; 
  }
private:
  result_type delim_;
  bool is_first_;
};

template
struct reverse_plus : public binary_function {
  T operator()(const T& _lhs, const T& _rhs) const { return (_rhs + _lhs); }
};

// Ausgabe auf Datei-Stream
transform
( strs.begin()
, strs.end()
, boost::make_function_input_iterator(generator(" , "), boost::infinite())
, ostream_iterator out_file_iterator(out_file)
, reverse_plus()
);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X