2016-06-22 59 views
3

私はのgetline functonに2つの以上の区切り文字を使用することができ、それは私の問題だ方法を知りたい:C++の関数getlineで2つ以上のデリミタを使用できますか?

プログラムは、テキストファイルを読み込む...各行は、のようになりgoningさ:

New Your, Paris, 100 
    CityA, CityB, 200 

私はgetl​​ine(file、line)を使用していますが、私はCityA、CityB、そしてその番号を取得したいときに、全体の行を取得しました。 '、'区切り文字を使用すると、次の行がいつわかるのかわからないので、解決策を見つけようとしています..

カンマと区切り記号を区切り記号として使用するにはどうすればよいですか? ちなみに、私は文字、文字列型のない操作だ、そうはstrtokはできません:/

いくつかの傷を:

string line; 
ifstream file("text.txt"); 
if(file.is_open()) 
    while(!file.eof()){ 
    getline(file, line); 
     // here I need to get each string before comma and \n 
    } 
+3

を参照してください[なぜEOFループでは間違っている](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop -condition-considered-wrong) – WhiZTiM

+1

'strtok'はできませんが、' find_first_of'があります。 – zneak

+0

あなたのフォーマットが基本的に裸のCSVです - あなたは[this](http://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv -files-in-c?lq = 1)。 –

答えて

5

あなたはそれを分離した値を、std::getlineを使用して行を読んで、その後std::stringstreamにラインを通過して、コンマを読むことができます

string line; 
ifstream file("text.txt"); 
if(file.is_open()){ 
    while(getline(file, line)){ // get a whole line 
     std::stringstream ss(line); 
     while(getline(ss, line, ',')){ 
      // You now have separate entites here 
     } 
    } 
4

いいえ、std::getline()のみオーバーライドする単一の文字を受け入れデフォルト区切り文字。 std::getline()には複数の代替区切り文字のオプションがありません。

入力のこの種を解析するための正しい方法は、次にstd::istringstreamを構築、std::stringに行全体を読むためにデフォルトstd::getline()を使用し、次にコンマ別々の値に、さらにそれを解析することです。

ただし、真にコンマ区切りの値を解析する場合は、a proper CSV parserを使用する必要があります。

1

私はあなたが問題を(たとえそれを行うことができたとしても)攻撃する方法ではないと思います。代わりに:

  1. あなたが欲しいのピースを取得するためにカンマでその行を分割後の各ライン
  2. で読むために持っているものを使用します。

strtokが#2のジョブを実行する場合は、文字列を常にchar配列に変換できます。

1

は多くの場合、階層的に文字入力を解析し、より直感的かつ効率的です、文字列をメジャーブロックに分割した後、各ブロックを処理し、小さな部分に分割するなど、ツリーのような方法で処理します。

これに代わる方法は、strtokのようにトークン化することです - 入力の始めから、入力の終わりまで一度に1つのトークンを処理します。単純な入力を構文解析するときには、これは簡単に実装できるので、これが望ましい場合があります。このスタイルは、ネストされた構造で入力を解析するときにも使用できますが、単一の関数や限られたコード領域内で維持するには複雑すぎるコンテキスト情報を維持する必要があります。

C++標準ライブラリに頼っている人は、たいていstd::stringstreamと、std::getlineと一緒に文字列入力をトークン化してしまいます。しかし、これはデリミタを1つだけ与えます。彼らはstrtokの使用を考慮しません。なぜなら、それはCランタイムライブラリからの非リエントラントなジャンクです。したがって、それらはストリームを使用して終了し、デリミタが1つのみで、階層的な解析スタイルを使用する義務があります。

しかし、zneakはstd::string::find_first_ofになりました。これは文字セットを取り、セットから文字を含む文字列の先頭に最も近い位置を返します。また、他のメンバー関数、find_last_of,find_first_not_ofなどがあります。これは文字列を解析する目的のために存在するようです。しかし、std::stringは、便利なトークン化機能を提供するには不足しています。

もう1つのオプションは、任意の操作を行うことができる<regex>ライブラリですが、新しいものであり、その構文に慣れる必要があります。

しかし、少しの労力で、std::stringの既存の機能を利用して、ストリームに頼らずにトークン化タスクを実行できます。ここに簡単な例があります。 get_to()はトークン化関数であり、tokenizeはその使用方法を示しています。

この例のコードは、解析されている文字列の先頭から常に文字を消去し、部分文字列もコピーして返すため、strtokより遅くなります。これはコードを分かりやすくしますが、トークン化の効率化がより効率的であるとは限りません。これよりずっと複雑ではありません。現在の位置を追跡し、std::stringメンバー関数のstart引数として使用し、ソース文字列を変更しないでください。さらに優れた技術が存在することは間違いありません。

例のコードを理解するには、下部にあるmain()がどこにあり、どのように機能が使用されているかをご覧ください。このコードの先頭には、基本的なユーティリティ関数とダムのコメントがあります。

#include <iostream> 
#include <string> 
#include <utility> 

namespace string_parsing { 
// in-place trim whitespace off ends of a std::string 
inline void trim(std::string &str) { 
    auto space_is_it = [] (char c) { 
     // A few asks: 
     // * Suppress criticism WRT localization concerns 
     // * Avoid jumping to conclusions! And seeing monsters everywhere! 
     // Things like...ah! Believing "thoughts" that assumptions were made 
     // regarding character encoding. 
     // * If an obvious, portable alternative exists within the C++ Standard Library, 
     // you will see it in 2.0, so no new defect tickets, please. 
     // * Go ahead and ignore the rumor that using lambdas just to get 
     // local function definitions is "cheap" or "dumb" or "ignorant." 
     // That's the latest round of FUD from...*mumble*. 
     return c > '\0' && c <= ' '; 
    }; 

    for(auto rit = str.rbegin(); rit != str.rend(); ++rit) { 
     if(!space_is_it(*rit)) { 
      if(rit != str.rbegin()) { 
       str.erase(&*rit - &*str.begin() + 1); 
      } 
      for(auto fit=str.begin(); fit != str.end(); ++fit) { 
       if(!space_is_it(*fit)) { 
        if(fit != str.begin()) { 
         str.erase(str.begin(), fit); 
        } 
        return; 
    } } } } 
    str.clear(); 
} 

// get_to(string, <delimiter set> [, delimiter]) 
// The input+output argument "string" is searched for the first occurance of one 
// from a set of delimiters. All characters to the left of, and the delimiter itself 
// are deleted in-place, and the substring which was to the left of the delimiter is 
// returned, with whitespace trimmed. 
// <delimiter set> is forwarded to std::string::find_first_of, so its type may match 
// whatever this function's overloads accept, but this is usually expressed 
// as a string literal: ", \n" matches commas, spaces and linefeeds. 
// The optional output argument "found_delimiter" receives the delimiter character just found. 
template <typename D> 
inline std::string get_to(std::string& str, D&& delimiters, char& found_delimiter) { 
    const auto pos = str.find_first_of(std::forward<D>(delimiters)); 
    if(pos == std::string::npos) { 
     // When none of the delimiters are present, 
     // clear the string and return its last value. 
     // This effectively makes the end of a string an 
     // implied delimiter. 
     // This behavior is convenient for parsers which 
     // consume chunks of a string, looping until 
     // the string is empty. 
     // Without this feature, it would be possible to 
     // continue looping forever, when an iteration 
     // leaves the string unchanged, usually caused by 
     // a syntax error in the source string. 
     // So the implied end-of-string delimiter takes 
     // away the caller's burden of anticipating and 
     // handling the range of possible errors. 
     found_delimiter = '\0'; 
     std::string result; 
     std::swap(result, str); 
     trim(result); 
     return result; 
    } 
    found_delimiter = str[pos]; 
    auto left = str.substr(0, pos); 
    trim(left); 
    str.erase(0, pos + 1); 
    return left; 
} 

template <typename D> 
inline std::string get_to(std::string& str, D&& delimiters) { 
    char discarded_delimiter; 
    return get_to(str, std::forward<D>(delimiters), discarded_delimiter); 
} 

inline std::string pad_right(const std::string&  str, 
          std::string::size_type min_length, 
          char     pad_char=' ') 
{ 
    if(str.length() >= min_length) return str; 
    return str + std::string(min_length - str.length(), pad_char); 
} 

inline void tokenize(std::string source) { 
    std::cout << source << "\n\n"; 
    bool quote_opened = false; 
    while(!source.empty()) { 
     // If we just encountered an open-quote, only include the quote character 
     // in the delimiter set, so that a quoted token may contain any of the 
     // other delimiters. 
     const char* delimiter_set = quote_opened ? "'" : ",'{}"; 
     char delimiter; 
     auto token = get_to(source, delimiter_set, delimiter); 
     quote_opened = delimiter == '\'' && !quote_opened; 
     std::cout << " " << pad_right('[' + token + ']', 16) 
      << " " << delimiter << '\n'; 
    } 
    std::cout << '\n'; 
} 
} 

int main() { 
    string_parsing::tokenize("{1.5, null, 88, 'hi, {there}!'}"); 
} 

この出力:

{1.5, null, 88, 'hi, {there}!'} 

    []     { 
    [1.5]    , 
    [null]    , 
    [88]    , 
    []     ' 
    [hi, {there}!]  ' 
    []     } 
関連する問題