簡體   English   中英

C ++字符串解析思路

[英]C++ string parsing ideas

我有另一個程序的輸出,這個程序更像是人類可讀而不是機器可讀,但我還是打算解析它。 這沒什么太復雜的。

然而,我想知道在C ++中最好的方法是什么。 這更像是一種“一般慣例”類型的問題。

我查看了Boost.Spirit,甚至讓它工作了一下。 那東西太瘋狂了! 如果正在設計我正在閱讀的語言,它可能是適合這項工作的工具。 但實際上,考慮到它的極端編譯時間,當我做錯任何事情時,g ++會出現幾頁錯誤,這不是我需要的。 (我也不太需要運行時性能。)

考慮使用C ++運算符<<,但這似乎毫無價值。 如果我的文件中有“John有5個小部件”這樣的行,而其他人“Mary在459 Ramsy street工作”,我怎么能確保我的程序中有第一個類型的行,而不是第二個類型? 我必須閱讀整行,然后使用像string::findstring::substr這樣的東西。

這留下了sscanf 它會很好地處理上述情況

if( sscanf( str, "%s has %d widgets", chararr, & intvar ) == 2 )
      // then I know I matched "foo has bar" type of string, 
      // and I now have the parameters too

所以我只是想知道我是否遺漏了某些內容,或者C ++是否真的沒有太多的內置替代品。

sscanf確實聽起來非常適合您的要求:

  • 您可能會進行一些冗余解析,但是沒有性能要求禁止這樣做
  • 它將對不同輸入單詞的需求進行本地化,並允許將非字符串值直接解析為類型變量,從而使不同的輸入格式易於理解

一個潛在的問題是它容易出錯,如果你有很多不斷變化的解析短語,那么測試工作和風險就會令人擔憂。 保持sscanf的精神,但使用istream類型安全:

#include <iostream>
#include <sstream>

// Str captures a string literal and consumes the same from an istream...
// (for non-literals, better to have `std::string` member to guarantee lifetime)
class Str
{
  public:
    Str(const char* p) : p_(p) { }
    const char* c_str() const { return p_; }
  private:
    const char* p_;
};

bool operator!=(const Str& lhs, const Str& rhs)
{
    return strcmp(lhs.c_str(), rhs.c_str()) != 0;
}

std::istream& operator>>(std::istream& is, const Str& str)
{
    std::string s;
    if (is >> s)
        if (s.c_str() != str)
            is.setstate(std::ios_base::failbit);
    return is;
}

// sample usage...

int main()
{
    std::stringstream is("Mary has 4 cats");
    int num_dogs, num_cats;

    if (is >> Str("Mary") >> Str("has") >> num_dogs >> Str("dogs"))
    {
        std::cout << num_dogs << " dogs\n";
    }
    else if (is.clear(), is.seekg(0), // "reset" the stream...
             (is >> Str("Mary") >> Str("has") >> num_cats >> Str("cats")))
    {
        std::cout << num_cats << " cats\n";
    }
}

GNU工具flexbison是你可以使用的非常強大的工具,它們與Spirit一致,但(根據一些人的話)更容易使用,部分原因是因為工具有自己的編譯器,所以錯誤報告更好一些。 這個,或者Spirit,或者其他一些解析器生成器,是一種“正確”的方法,因為它為您提供了最大的靈活性。

如果您正在考慮使用strtok ,您可能需要查看stringstream ,它在空格上分割並允許您在字符串,基元等之間進行一些很好的格式轉換。它還可以插入到STL算法中,並且避免原始C風格字符串內存管理的所有混亂細節。

我用C ++編寫了大量的解析代碼。 它的效果非常好,但我自己編寫代碼並且不依賴於其他人編寫的更通用的代碼。 C ++沒有提供已編寫的大量代碼,但它是編寫此類代碼的一種很好的語言。

我不確定你的問題是什么,只是你想找到一個已經寫好的代碼來做你需要的代碼。 部分問題在於您沒有真正描述您的需求,或者就此問題提出了問題。

如果您可以更具體地提出問題,我很樂意嘗試提供更具體的答案。

我使用過Boost.Regex(我認為它也是tr1 :: regex)。 使用方便。

我想總有strtok()

看看strtok

根據您要解析的確切內容,您可能需要正則表達式庫。 請參閱msdn更早的問題

就個人而言,再次依賴於確切的格式,我會考慮使用perl進行初始轉換為更加機器可讀的格式(例如變量記錄CSV),然后更容易導入到C ++中。

如果堅持使用C ++,您需要:

  1. 確定一條記錄 - 希望只是一條線
  2. 確定記錄的類型 - 使用正則表達式
  3. 解析記錄 - scanf很好

關於以下行的基類:

class Handler
{
public:
    Handler(const std::string& regexExpr)
        : regex_(regexExpr)
    {}
    bool match(const std::string& s)
    {
        return std::tr1::regex_match(s,regex_);
    }
    virtual bool process(const std::string& s) = 0;
private:
    std::tr1::basic_regex<char> regex_;
};

為每個記錄類型定義派生類,在集合中粘貼每個記錄的實例並搜索匹配項。

class WidgetOwner : public Handler
{
public:
    WidgetOwner()
        : Handler(".* has .* widgets")
    {}
    virtual bool process(const std::string& s) 
    {
        char name[32];
        int widgets= 0;
        int fieldsRead = sscanf( s.c_str(),  "%32s has %d widgets", name, & widgets) ;

        if (fieldsRead == 2)
        {
            std::cout << "Found widgets in " << s << std::endl;
        }
        return fieldsRead == 2;
    }
};

struct Pred 
{
    Pred(const std::string& record)
        : record_(record)
    {}
    bool operator()(Handler* handler)
    {
        return handler->match(record_);
    }
    std::string record_;
};

std::set<Handler*> handlers_;
handlers_.insert(new WidgetOwner);
handlers_.insert(new WorkLocation);

Pred pred(line);
std::set<Handler*>::iterator handlerIt = 
     std::find_if(handlers_.begin(), handlers_.end(), pred);
if (handlerIt != handlers_.end())
    (*handlerIt)->process(line);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM