[英]C++ string parsing ideas
我有另一個程序的輸出,這個程序更像是人類可讀而不是機器可讀,但我還是打算解析它。 這沒什么太復雜的。
然而,我想知道在C ++中最好的方法是什么。 這更像是一種“一般慣例”類型的問題。
我查看了Boost.Spirit,甚至讓它工作了一下。 那東西太瘋狂了! 如果我正在設計我正在閱讀的語言,它可能是適合這項工作的工具。 但實際上,考慮到它的極端編譯時間,當我做錯任何事情時,g ++會出現幾頁錯誤,這不是我需要的。 (我也不太需要運行時性能。)
考慮使用C ++運算符<<,但這似乎毫無價值。 如果我的文件中有“John有5個小部件”這樣的行,而其他人“Mary在459 Ramsy street工作”,我怎么能確保我的程序中有第一個類型的行,而不是第二個類型? 我必須閱讀整行,然后使用像string::find
和string::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工具flex
和bison
是你可以使用的非常強大的工具,它們與Spirit一致,但(根據一些人的話)更容易使用,部分原因是因為工具有自己的編譯器,所以錯誤報告更好一些。 這個,或者Spirit,或者其他一些解析器生成器,是一種“正確”的方法,因為它為您提供了最大的靈活性。
如果您正在考慮使用strtok
,您可能需要查看stringstream
,它在空格上分割並允許您在字符串,基元等之間進行一些很好的格式轉換。它還可以插入到STL算法中,並且避免原始C風格字符串內存管理的所有混亂細節。
我用C ++編寫了大量的解析代碼。 它的效果非常好,但我自己編寫代碼並且不依賴於其他人編寫的更通用的代碼。 C ++沒有提供已編寫的大量代碼,但它是編寫此類代碼的一種很好的語言。
我不確定你的問題是什么,只是你想找到一個已經寫好的代碼來做你需要的代碼。 部分問題在於您沒有真正描述您的需求,或者就此問題提出了問題。
如果您可以更具體地提出問題,我很樂意嘗試提供更具體的答案。
我使用過Boost.Regex(我認為它也是tr1 :: regex)。 使用方便。
我想總有strtok()
看看strtok 。
根據您要解析的確切內容,您可能需要正則表達式庫。 請參閱msdn或更早的問題 。
就個人而言,再次依賴於確切的格式,我會考慮使用perl進行初始轉換為更加機器可讀的格式(例如變量記錄CSV),然后更容易導入到C ++中。
如果堅持使用C ++,您需要:
關於以下行的基類:
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.