Có những thư viện phân tích cú pháp tham số nào cho C ++? [đóng cửa]


76

Tôi muốn chuyển các tham số cho chương trình C ++ của mình theo cách sau:

./myprog --setting=value

Có thư viện nào giúp tôi thực hiện việc này một cách dễ dàng không?

Xem thêm Trình trợ giúp phân tích cú pháp đối số cho C và Unix


1
Gần đây đã viết bài này cho c ++ hiện đại: github.com/juzzlin/Argengine
juzzlin

Câu trả lời:


45

13
Đây có vẻ là tùy chọn rõ ràng nhất cho C ++, nhưng tài liệu của nó không đủ đầy đủ. Hãy thử tìm ở đó cách lưu trữ và truy xuất các tùy chọn từ tệp, một tính năng cần thiết. Tôi không thích mã sử dụng nó trông như thế nào, cụ thể là từ ngữ options.add_options()(option1)(option2)...mà tôi coi là lạm dụng ngữ pháp C ++.
gatopeich

8
Việc biên dịch mã với Boost.Program_options dường như không dễ dàng và bắt buộc phải có các tùy chọn liên kết, v.v., ngoài việc bao gồm tệp tiêu đề.
Richard

2
Bạn có thể nhận được khá nhiều như nhau với ít hơn nhiều. Nếu bạn muốn những thứ như thế --long-option, bạn hãy tự mình làm khá đơn giản.
Luis Machuca

Ở một khía cạnh nào đó, đối với các chương trình thực sự đơn giản hoặc bạn chỉ trực tiếp làm việc với mảng argv []. Một tình huống khác, để hoàn toàn linh hoạt trong các đối số của bạn, bạn có thể làm việc trực tiếp với mảng argv (bạn có thể thực hiện đầu vào prog -1 firstinput -2 giây -obj {constructor đối số ..}). Nếu không, hãy sử dụng boost, tclap hoặc nhiều thứ khác.
Kemin Zhou

boost::program_optionsđược khai thác quá mức, khó sử dụng và không có tài liệu. Một trong số ít các thư viện Boost sẽ được hưởng lợi rất nhiều từ việc thiết kế lại và viết lại hoàn toàn. Đừng sử dụng nó nếu bạn có thể tránh nó.
Laryx Decidua

26

GNU GetOpt .

Một ví dụ đơn giản sử dụng GetOpt:

// C/C++ Libraries:
#include <string>
#include <iostream>
#include <unistd.h>

// Namespaces:
using namespace std;

int main(int argc, char** argv) {
    int opt;
    bool flagA = false;
    bool flagB = false;

    // Shut GetOpt error messages down (return '?'): 
    opterr = 0;

    // Retrieve the options:
    while ( (opt = getopt(argc, argv, "ab")) != -1 ) {  // for each option...
        switch ( opt ) {
            case 'a':
                    flagA = true;
                break;
            case 'b':
                    flagB = true;
                break;
            case '?':  // unknown option...
                    cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
                break;
        }
    }

    // Debug:
    cout << "flagA = " << flagA << endl;
    cout << "flagB = " << flagB << endl;

    return 0;
}

Bạn cũng có thể sử dụng optarg nếu bạn có các tùy chọn chấp nhận đối số.


14
Ờ. Tôi hiểu việc sử dụng thư viện này trong mã C, nhưng IMO, đây là mức quá thấp để có thể chấp nhận được trong bất kỳ ứng dụng C ++ nào mà tôi từng viết. Tìm một thư viện tốt hơn nếu bạn không cần C. tinh khiết
Thomas Việc chỉnh sửa

1
Ngoài ra còn có một ví dụ GNU về getopt với giá trị như myexe -c myvalue Bạn có thể thử tìm kiếm "Ví dụ về phân tích cú pháp các đối số với getopt"
edW

đây là một ví dụ ví dụ được cung cấp bởi GNU bản thân ^^
finnmglas

19

Tôi thấy sử dụng ezOptionParser dễ dàng hơn . Nó cũng là một tệp tiêu đề duy nhất, không phụ thuộc vào bất kỳ thứ gì ngoài STL, hoạt động cho Windows và Linux (rất có thể là các nền tảng khác), không có đường cong học tập nhờ các ví dụ, có các tính năng mà các thư viện khác không có (như nhập / xuất tệp với nhận xét, tên tùy chọn tùy ý với dấu phân cách, định dạng sử dụng tự động, v.v.) và được cấp phép LGPL.


1
Bắt đầu với phiên bản 0.1.3, giấy phép hiện là MIT. Tôi đang thử điều này trên một dự án mới thay vì TCLAP và cho đến nay nó có vẻ rất hứa hẹn. Tùy chọn cấu hình tệp khá đẹp.
Sean

6
Tôi vừa dùng thử exOptionParser, nhưng nó có rất nhiều vấn đề. Trước hết, tôi nhận được 58 cảnh báo về chuyển đổi int sang int không dấu. Nó cũng cố gắng tăng các trình vòng lặp danh sách (không thể được sử dụng như vậy) và sự cố. Giao diện của nó là quá khủng khiếp. Nó sử dụng các tham chiếu ở khắp nơi thay vì chỉ trả về dữ liệu bạn muốn. Nó trông giống như một thư viện C mặc dù nó được xây dựng trên C ++ STL.
Andrew Larsson

Ghi chú; phát hiện các đối số không xác định không hoạt động. Ngoài ra, tiêu đề cho lỗi biên dịch nếu không được đặt trước các tiêu đề khác. Tôi sẽ tìm kiếm phân tích cú pháp khác ..
Totte Karlsson

19

TCLAPlà một thiết kế nhẹ thực sự đẹp và dễ sử dụng: http://tclap.sourceforge.net/


4
Tôi đã sử dụng getopt, gflags của google, chương trình_options từ Boost và tclap thật tuyệt vời . Tôi không thể nói đủ điều tốt về tclap, đặc biệt là xem xét các lựa chọn thay thế có sẵn. Mức độ khó hiểu của tôi là định dạng trợ giúp của nó "khác" so với những gì mắt tôi thường làm.
Sean

16

Và có một thư viện Google .

Thực sự, phân tích cú pháp dòng lệnh đã được "giải quyết". Chỉ cần chọn một.


(Cảm ơn bạn @QPaysTaxes đã nhận thấy liên kết bị hỏng; Tôi không biết tại sao bản chỉnh sửa của bạn bị từ chối, nhưng bạn chắc chắn đã chính xác).
Max Lybbert

5
Tôi không thể nghĩ ra câu trả lời ít hữu ích hơn cho một câu hỏi. 'Nó đã được giải quyết. Chọn một.' Xin lỗi, nhưng "duh." Với khoảng 15 phút suy nghĩ về vấn đề này, tôi đã đưa ra khoảng 30 kịch bản khác nhau về cách một người có thể tiếp cận nó. Tôi nghi ngờ câu trả lời 'đúng' gần giống với việc giải thích cách một tập hợp các mối quan tâm cụ thể sẽ dẫn đến một tập hợp triển khai mã cụ thể. Nhưng, này, cảm ơn vì đã gọi.
MarkWayne

10

Tôi nghĩ rằng GNU GetOpt không phải là quá ngay để sử dụng.

Qt và Boost có thể là một giải pháp, nhưng bạn cần tải xuống và biên dịch rất nhiều mã.

Vì vậy, tôi đã tự mình triển khai một trình phân tích cú pháp tạo ra một tham số std :: map <std :: string, std :: string>.

Ví dụ: gọi:

 ./myProgram -v -p 1234

bản đồ sẽ là:

 ["-v"][""]
 ["-p"]["1234"]

Cách sử dụng là:

int main(int argc, char *argv[]) {
    MainOptions mo(argc, argv);
    MainOptions::Option* opt = mo.getParamFromKey("-p");
    const string type = opt ? (*opt).second : "";
    cout << type << endl; /* Prints 1234 */
    /* Your check code */
}

MainOptions.h

#ifndef MAINOPTIONS_H_
#define MAINOPTIONS_H_

#include <map>
#include <string>

class MainOptions {
public:
    typedef std::pair<std::string, std::string> Option;
    MainOptions(int argc, char *argv[]);
    virtual ~MainOptions();
    std::string getAppName() const;
    bool hasKey(const std::string&) const;
    Option* getParamFromKey(const std::string&) const;
    void printOptions() const;
private:
    typedef std::map<std::string, std::string> Options;
    void parse();
    const char* const *begin() const;
    const char* const *end() const;
    const char* const *last() const;
    Options options_;
    int argc_;
    char** argv_;
    std::string appName_;
};

MainOptions.cpp

#include "MainOptions.h"

#include <iostream>

using namespace std;

MainOptions::MainOptions(int argc, char* argv[]) :
        argc_(argc),
        argv_(argv) {
    appName_ = argv_[0];
    this->parse();
}

MainOptions::~MainOptions() {
}

std::string MainOptions::getAppName() const {
    return appName_;
}

void MainOptions::parse() {
    typedef pair<string, string> Option;
    Option* option = new pair<string, string>();
    for (const char* const * i = this->begin() + 1; i != this->end(); i++) {
        const string p = *i;
        if (option->first == "" && p[0] == '-') {
            option->first = p;
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "" && p[0] == '-') {
            option->second = "null"; /* or leave empty? */
            options_.insert(Option(option->first, option->second));
            option->first = p;
            option->second = "";
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "") {
            option->second = p;
            options_.insert(Option(option->first, option->second));
            option->first = "";
            option->second = "";
            continue;
        }
    }
}

void MainOptions::printOptions() const {
    std::map<std::string, std::string>::const_iterator m = options_.begin();
    int i = 0;
    if (options_.empty()) {
        cout << "No parameters\n";
    }
    for (; m != options_.end(); m++, ++i) {
        cout << "Parameter [" << i << "] [" << (*m).first << " " << (*m).second
                << "]\n";
    }
}

const char* const *MainOptions::begin() const {
    return argv_;
}

const char* const *MainOptions::end() const {
    return argv_ + argc_;
}

const char* const *MainOptions::last() const {
    return argv_ + argc_ - 1;
}

bool MainOptions::hasKey(const std::string& key) const {
    return options_.find(key) != options_.end();
}

MainOptions::Option* MainOptions::getParamFromKey(
        const std::string& key) const {
    const Options::const_iterator i = options_.find(key);
    MainOptions::Option* o = 0;
    if (i != options_.end()) {
        o = new MainOptions::Option((*i).first, (*i).second);
    }
    return o;
}

2
Ý bạn là gì khi "... không quá ngay lập tức để sử dụng" ? Bạn có thể xây dựng?
Peter Mortensen


7

Nếu có thể, tôi cũng khuyên bạn nên xem qua thư viện phân tích cú pháp tùy chọn mà tôi đã viết: dropt .

  • Đó là một thư viện C (với một trình bao bọc C ++ nếu muốn).
  • Nó nhẹ.
  • Nó có thể mở rộng (các loại đối số tùy chỉnh có thể dễ dàng được thêm vào và có giá trị ngang nhau với các loại đối số tích hợp sẵn).
  • Nó phải rất dễ di chuyển (nó được viết bằng C chuẩn) mà không có phụ thuộc nào (ngoài thư viện C chuẩn).
  • Nó có một giấy phép không bị ràng buộc (zlib / libpng).

Một tính năng mà nó cung cấp mà nhiều người khác không có là khả năng ghi đè các tùy chọn trước đó. Ví dụ: nếu bạn có bí danh shell:

alias bar="foo --flag1 --flag2 --flag3"

và bạn muốn sử dụng barnhưng --flag1bị vô hiệu hóa, nó cho phép bạn thực hiện:

bar --flag1=0

1
Điều này trông khá gọn gàng. Rất vui vì tôi đã cuộn xuống; không có gì rất tốt cho C đơn giản, hãy lưu lại!
Asherah

Nghe có vẻ tuyệt vời, nhưng nó có vẻ hơi quá lớn. Ngoài ra, tôi chỉ đang tìm cách phân tích cú pháp từ các chỉ định định dạng scanf khỏi một đối số, tôi đã viết trình phân tích cú pháp của riêng mình (mặc dù cơ bản hơn).
MarcusJ

1
@MarcusJ Có vẻ hơi kỳ lạ khi bạn nói rằng cái này quá lớn (nó nhỏ hơn nhiều so với hầu hết các trình phân tích cú pháp tùy chọn dòng lệnh khác) nhưng bạn lại muốn nó phân tích cú pháp thông số định dạng printf / scanf (không phải là dòng lệnh trình phân tích cú pháp tùy chọn thường làm) ...
jamesdlin

Vâng, tôi biết tôi có một số yêu cầu cụ thể cho việc này, tôi sẽ tiếp tục và viết lại trình phân tích cú pháp tùy chọn của mình, nhưng tôi đã xem mã của bạn và ý tưởng chuyển vào một cấu trúc để chứa các tùy chọn khác nhau thực sự thú vị (cho đến nay của tôi chỉ là mã hóa cứng). Bản thân nó không quá lớn, chỉ là tôi có một dự án .c / .h duy nhất và mã của bạn sẽ gấp đôi số lượng mã mà tôi đã có, vì vậy nó quá lớn đối với dự án cụ thể của tôi.
MarcusJ

5

Qt 5.2 đi kèm với API phân tích cú pháp dòng lệnh .

Ví dụ nhỏ:

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QDebug>

int main(int argc, char **argv)
{
  QCoreApplication app(argc, argv);
  app.setApplicationName("ToolX");
  app.setApplicationVersion("1.2");

  QCommandLineParser parser;
  parser.setApplicationDescription("Tool for doing X.");
  parser.addHelpOption();
  parser.addVersionOption();
  parser.addPositionalArgument("infile",
      QCoreApplication::translate("main", "Input file."));

  QCommandLineOption verbose_opt("+",
      QCoreApplication::translate("main", "be verbose"));
  parser.addOption(verbose_opt);

  QCommandLineOption out_opt(QStringList() << "o" << "output",
      QCoreApplication::translate("main", "Output file."),
      QCoreApplication::translate("main", "filename"), // value name
      QCoreApplication::translate("main", "out")   // default value
      );
  parser.addOption(out_opt);

  // exits on error
  parser.process(app);

  const QStringList args = parser.positionalArguments();

  qDebug() << "Input files: " << args
    << ", verbose: " << parser.isSet(verbose_opt)
    << ", output: " << parser.value(out_opt)
    << '\n';
  return 0;
}

Ví dụ đầu ra

Màn hình trợ giúp được tạo tự động:

$ ./qtopt -h
Cách sử dụng: ./qtopt [options] infile
Công cụ để làm X.

Các tùy chọn:
  -h, --help Hiển thị trợ giúp này.
  -v, --version Hiển thị thông tin phiên bản.
  - + dài dòng
  -o, --output Tập tin đầu ra.

Tranh luận:
  infile Tập tin đầu vào.

Đầu ra phiên bản được tạo tự động:

$ ./qtopt -v
ToolX 1.2

Một số cuộc gọi thực tế:

$ ./qtopt b1 - + -o tmp blah.foo
Tệp đầu vào: ("b1", "blah.foo"), verbose: true, output: "tmp"
$ ./qtopt          
Tệp đầu vào: (), verbose: false, đầu ra: "out"

Một lỗi phân tích cú pháp:

$ ./qtopt --hlp
Tùy chọn không xác định 'hlp'.
$ echo $?
1

Phần kết luận

Nếu chương trình của bạn đã sử dụng các thư viện Qt (> = 5.2), thì API phân tích cú pháp dòng lệnh của nó đủ thuận tiện để hoàn thành công việc.

Hãy lưu ý rằng các tùy chọn Qt tích hợp được sử dụng QApplicationtrước khi trình phân tích cú pháp tùy chọn chạy.


3

argstreamkhá giống với boost.program_option: nó cho phép liên kết các biến với các tùy chọn, v.v. Tuy nhiên, nó không xử lý các tùy chọn được lưu trữ trong tệp cấu hình.


3

Hãy thử thư viện CLPP. Đây là thư viện đơn giản và linh hoạt để phân tích cú pháp các tham số dòng lệnh. Chỉ tiêu đề và đa nền tảng. Chỉ sử dụng thư viện ISO C ++ và Boost C ++. IMHO nó dễ dàng hơn Boost.Program_options.

Thư viện: http://sourceforge.net/projects/clp-parser/

Ngày 26 tháng 10 năm 2010 - bản phát hành mới 2.0rc. Nhiều lỗi đã được sửa, cấu trúc lại toàn bộ mã nguồn, tài liệu, ví dụ và nhận xét đã được sửa.


1

Bạn có thể thử tiêu đề tùy chọn nhỏ của tôi (166 vị trí rất dễ hack) options.hpp . Nó là một triển khai tiêu đề duy nhất và sẽ thực hiện những gì bạn yêu cầu. Nó cũng tự động in cho bạn trang trợ giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.