Форум программистов, компьютерный форум CyberForum.ru

Парсинг значений из командной строки - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 30, средняя оценка - 4.93
MishQa
 Аватар для MishQa
1 / 1 / 0
Регистрация: 12.11.2011
Сообщений: 11
12.11.2011, 16:31     Парсинг значений из командной строки #1
Всем доброго времени суток,
столкнулся с необходимостью получения значений из командной строки, не просто чисел, а разделенных '/'.
Тоесть, например, при запуске программы prgrm.exe 1/3 2/5 3/5 1/2 я должен разобрать эти "дроби".
Мне нужно чтобы я мог получать пару чисел (1,3) (2,5) .... для последующей передачи их в функцию , для того, чтобы создавать экземпляры класса.
Я довольно таки много времени провел в поисках решения... залез даже в библиотеки BOOST::spirit,
так же почитал про getopt , но гетопт для моего случая вроде бы не подходит, а буст спирит вроде подходит, но там я столкнулся с другой проблемой=) там в созданный парсер необходимо передавать указатели на начало и конец строки, которую будем парсить.
Не оч понимаю как извлечь из ARGV указатель на конец нужной строки.
Тоесть , поправте меня если я не прав, имею на входе массив указателей char*argv[] каждый из которых указывает на область памяти , в которой лежит первый символ строки. А как получить указатель на послежний символ.
Прошу подсказать мне, чем следует воспользоваться при данной задаче? В поиске я пошарил, но похожего не нашел, если все же такое уже было, не ругайтесь дайте ссылочку, буду очень благодарен.
Еще раз повторю, что я могу пользоваться любыми библиотеками и т.д.
лишь бы получить из такого входного представления, множество пар чисел=)
Заранее благодарю
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
12.11.2011, 16:31     Парсинг значений из командной строки
Посмотрите здесь:

C++ Аргументы командной строки
C++ Вывод командной строки
C++ Имитация командной строки
C++ использование командной строки
аргементы командной строки C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
12.11.2011, 16:59     Парсинг значений из командной строки #2
MishQa, ну тут спирит - это как из пушки по воробьям. Вот на STL, хотя можно ещё проще.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <vector>
#include <stdexcept>
#include <sstream>
 
struct ratio
{
    double a, b;
};
 
std::istream & operator>>( std::istream &is, ratio &rat )
{
    ratio temp;
 
    if( !(is >> temp.a) || is.get() != '/' || !(is >> temp.b) )
        throw std::runtime_error( "bat ratio format" );
 
    rat = temp;
 
    return is;
}
 
int main( int argc, char ** argv )
{
    std::vector<ratio> cmd_line_args;
 
    for( int i = 1; i < argc; i++ )
    {
        ratio temp;
 
        std::istringstream stream_in( argv[i], std::istringstream::in );
 
        try
        {
            stream_in >> temp;
        }
        catch( std::runtime_error &e )
        {
            std::cerr << "argument #" << i << ": " << e.what() << '\n';
            continue;
        }
 
        cmd_line_args.push_back( temp );
    }
 
 
    // -- прочитали --
    for( std::vector<ratio>::const_iterator it = cmd_line_args.begin();
         it != cmd_line_args.end(); it++ )
         {
             std::cout << it->a << '/' << it->b << "; ";
         }
 
    std::cout << '\n';
 
 
    return 0;
}
Если немного усложнить, можно сделать ввод значений, стоящих через пробел:

Код
./a.out 8/4 3.14/ 8  1   /    2
и так далее

Добавлено через 1 минуту
Но это чревато тем, что можно просто не заметить ошибку пользователя.

Добавлено через 1 минуту
тестировал с этими параметрами:

Код
./a.out 1/2 3/4 5/8 1 2 3.94/9.71 /41/22 42/23
MishQa
 Аватар для MishQa
1 / 1 / 0
Регистрация: 12.11.2011
Сообщений: 11
12.11.2011, 17:03  [ТС]     Парсинг значений из командной строки #3
Спасибо большое, ща попробую разобраться. А насчет еще проще, это как?
Просто я помню у меня когда-то давно была задача очень похожая, я там прям в конструктор передавал дробь в таком виде 1/2 и очень быстро там это парсил. Но совершенно не могу вспомнить как, а это было на лабах там и сторонних библиотек то никаких небыло
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
12.11.2011, 17:07     Парсинг значений из командной строки #4
MishQa, проще это в сишном варианте без std::istringstream, например. Но я не вижу смысла: во-первых придётся попрощаться с исключениями, выбрасываемыми std::istringstream, а во вторых мешать си и с++ это некрасиво. С другой стороны, можно "сшить" все argv в одну строку. Тогда парсить можно будет не смотря на пробелы. Если оно имеет смысл.

Добавлено через 54 секунды
Сторонние библиотеки тут не имеет смысла привлекать.
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,390
12.11.2011, 17:36     Парсинг значений из командной строки #5
MishQa, getopt, насколько я знаю, описывается стандартом POSIX и не распространен под виндой и виндовыми компиляторами.
boost.spirit не предназначен для разбора аргументов командной строки - для этого есть есть boost.program_options.
Но дело не в этом. Для того, чтобы решить данную задачу, нужно просто разобрать строки, переданные в качестве параметра, ни getopt, ни, прости Г-споди, буст, тут не нужны. Вот пример на С:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
 
struct ratio
{
    int numerator;
    size_t denominator;
};
 
int parse_ratio(const char*, struct ratio*);
 
int main(int argc, char* argv[])
{
    struct ratio r;
    int i;
    
    
    if(argc == 1)
        puts("No input arguments specified");
    else
    {
    for(i = 1; i < argc; ++i)
    {
        if(parse_ratio(argv[i], &r) != 0)
        printf("Ratio %d/%u\n", r.numerator, r.denominator);
        else
        printf("`%s': no parse\n", argv[i]);
    }
    }
 
    exit(0);
}
 
int parse_ratio(const char* str_repr, struct ratio* pr)
{
    int ret = sscanf(str_repr, "%d / %u", &pr->numerator, &pr->denominator);
 
    return ret == 2;
}
Пример использования:
Код
[nameless@desktop c]$ ./sample -1/2 1/3 ' 3/ 4' ' -4 / 5 ' b 12.5
Ratio -1/2
Ratio 1/3
Ratio 3/4
Ratio -4/5
`b': no parse
`12.5': no parse
[nameless@desktop c]$
MishQa
 Аватар для MishQa
1 / 1 / 0
Регистрация: 12.11.2011
Сообщений: 11
12.11.2011, 17:57  [ТС]     Парсинг значений из командной строки #6
Nameless One, Спасибо, наверное воспользую твоим вариантом, так как он для меня наиболее понятен, но я хочу так же разобраться с вариантом talis'a.

talis, заполни пожалуйста мои провалы=) буду тебе очень благодарен.
по порядку прям...

в мейне:
создаем вектор,
проходимся по всем аргументам командной строки и записываем их в волшебный для меня поток...
я прочитал про него и понял только то что этот метод читает из объекта, и ничего не понял насчет второго аргумекнта(std::istringstream::in) передаваемого в конструктор, не мог бы мне разжувать зачем он? *
Далее перегруженным оператором >> в структуру суем значение, оператор >> сам парсит, НО
скажи плз что вот это ?
if( !(is >> temp.a) || is.get() != '/' || !(is >> temp.b) )
throw std::runtime_error( "bat ratio format" ); **

И далее идем от начала до конца вектора и печатаем
std::cout << it->a << '/' << it->b << "; ";
it->a что это такое? итератор IT нас как бы идет по объектам засунутым в вектор? а '->' извлекает из структуры/класса , на котором установлен итератор данные? ***

объясни 3 пунктика всего, если лень , то я пойму) спасибо всем, отличный форум, возмолжно у меня привьется любовь к форумам теперь)
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
12.11.2011, 18:10     Парсинг значений из командной строки #7
std::istringstream::in определяет режим работы потока. В данном случае это поток "ввода".

if( !(is >> temp.a) || is.get() != '/' || !(is >> temp.b) )

слева направо выполняются действия: ввода из потока в temp.a. Если нет ошибки - чтение следующего символа. Если он == '/' - ввод из потока в temp.b. Если нет ошибки - идём дальше. Если на одной из операций произошла ошибка, то

throw std::runtime_error( "bat ratio format" );

выбрасывается исключение с текстом "bad ratio format". Кстати, у меня там очепятка Это исключение ловится в блоке try{ ... } catch( std::runtime_error &e ){ ... } в мэйне.



Цитата Сообщение от MishQa Посмотреть сообщение
И далее идем от начала до конца вектора и печатаем
std::cout << it->a << '/' << it->b << "; ";
it->a что это такое?
it - это объект-итератор, обходящий вектор. it-> - это доступ к элементу текущего элемента вектора. Это как array[i].a и array[i].b с простыми массивами. Кстати, std::vector умеет и индексировать элементы как обычный массив, так что вместо итераторов можно было бы использовать счётчик, возможно было бы лучше и быстрее. Просто привычка.

Если что не понятно - спрашивайте. Удачи

Добавлено через 1 минуту
Альтернативный синтаксис у итератора - *(it).a и *(it).b. Как разыменовывание указателя на элемент массива.

Добавлено через 1 минуту

Не по теме:

Nameless One, кстати, getopt поддерживается MinGW.



Добавлено через 59 секунд
Цитата Сообщение от MishQa Посмотреть сообщение
проходимся по всем аргументам командной строки и записываем их в волшебный для меня поток...
Это тот же std::cin, только читающий не со стандартного потока ввода, а из std::string или сишной строки.
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,390
12.11.2011, 18:16     Парсинг значений из командной строки #8
MishQa, объекты istream в C++ имеют оператор void*, который используется для приведения к типу void*, где нулевой указатель сигнализирует об ошибке чтения/преобразования (на основе неявно установленных флагов), а ненулевой - об успешности операции. Перегруженный оператор >> возвращает объект istream. Что мы видем здесь?

C++
1
2
if( !(is >> temp.a) || is.get() != '/' || !(is >> temp.b) )
 throw std::runtime_error( "bat ratio format" );
сначала идет чтение числителя дроби, после чего возвращаемый объект потока неявно приводится к типу void* для того, чтобы проверить успешность чтения. После чего из входного потока читается единичный символ (и проверяется, что он равен '/'), затем по аналогии с числителем читается знаменатель. Следовательно, условие имеет вид (на псевдокоде):
Код
Если(не(успешно чтение числителя) или (прочитанный символ не равен '/') или не(успешно чтение знаменателя))
   то выбрасываем исключение
т.е. если хоть один элемент считан неправильно, то получаем ошибку

Цитата Сообщение от MishQa Посмотреть сообщение
И далее идем от начала до конца вектора и печатаем
C++
1
std::cout << it->a << '/' << it->b << "; ";
it->a что это такое? итератор IT нас как бы идет по объектам засунутым в вектор? а '->' извлекает из структуры/класса , на котором установлен итератор данные? ***
да

Не по теме:

Цитата Сообщение от talis Посмотреть сообщение
Nameless One, кстати, getopt поддерживается MinGW.
неудивительно, т.к. MinGW - это порт GCC (а GCC, насколько я знаю, полностью POSIX-совместим)

MishQa
 Аватар для MishQa
1 / 1 / 0
Регистрация: 12.11.2011
Сообщений: 11
12.11.2011, 18:39  [ТС]     Парсинг значений из командной строки #9
Спасибо=) собрав ваши посты, я вроде во всем разобрался кроме...
if( !(is >> temp.a) || is.get() != '/' || !(is >> temp.b) )
тоесть из потока is вводится в темп + еще и возвращается 0 либо 1 в зависимости от успеха?
И еще как он понимает is>>temp.a что нужно взять только 123214 из 123214/7 например, если считывание идет слева направо...Как он узнает тобишь где остановиться?
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
12.11.2011, 18:40     Парсинг значений из командной строки #10
temp.a - типа double, соответственно вводится значение типа double. слэш не входит в алфавит символов формата, поэтому на нём ввод закончится.
Nameless One
Эксперт С++
 Аватар для Nameless One
5753 / 3402 / 255
Регистрация: 08.02.2010
Сообщений: 7,390
12.11.2011, 18:47     Парсинг значений из командной строки #11
Цитата Сообщение от MishQa Посмотреть сообщение
тоесть из потока is вводится в темп + еще и возвращается 0 либо 1 в зависимости от успеха?
ноль либо не ноль.

Цитата Сообщение от MishQa Посмотреть сообщение
И еще как он понимает is>>temp.a что нужно взять только 123214 из 123214/7 например, если считывание идет слева направо...Как он узнает тобишь где остановиться?
т.к. оператор >> перегружен и для double (которому принадлежит a и b), то при компиляции "is >> temp.a" компилятор использует именно версию для double. Символ '/' не является валидным в строковом представлении double, поэтому он и не читается из входного потока при выполнении "is >> temp.a"

Добавлено через 3 минуты

Не по теме:

да что такое, опять меня опередили...

talis
12.11.2011, 18:49
  #12

Не по теме:

Цитата Сообщение от Nameless One Посмотреть сообщение
да что такое, опять меня опередили...
Да меня вот тоже постоянно опередить норовят, просто беда какая-то. Но ничего, держимся пока, и вы держитесь

ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
12.11.2011, 18:58     Парсинг значений из командной строки #13
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
 
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
 
struct ratio
{
   int a, b;
};
 
int main(int argc, char* argv[])
{
    std::vector<ratio> rat_vec;
    std::for_each(argv + 1, argv + argc, [&rat_vec](const char* value) 
    {
         std::vector<std::string> vec;
         boost::split(vec, value, [](char c) {return c == '/';}, boost::token_compress_on);
         rat_vec.push_back({boost::lexical_cast<int>(vec.at(0)), boost::lexical_cast<int>(vec.at(1))}); 
    });
    std::for_each(rat_vec.begin(), rat_vec.end(), [](const ratio& rat) {std::cout << rat.a << '/' << rat.b << std::endl;});
}
Bash
1
2
3
4
5
forever@lavroffff:~/Programs$ g++ -o map_test map_test.cpp -I/usr/include/boost -std=c++0x
forever@lavroffff:~/Programs$ ./map_test 1/2 3/4 5/6
1/2
3/4
5/6
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.11.2011, 19:47     Парсинг значений из командной строки
Еще ссылки по теме:

C++ Парсинг опций командной строки
C++ Слияних 2 файлов в третий + строки + использование командной строки
C++ Аргументы командной строки

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
MishQa
 Аватар для MishQa
1 / 1 / 0
Регистрация: 12.11.2011
Сообщений: 11
12.11.2011, 19:47  [ТС]     Парсинг значений из командной строки #14
ForEveR, тоже интересный вариант, обязательно разберу, балгодарю=)
Yandex
Объявления
12.11.2011, 19:47     Парсинг значений из командной строки
Ответ Создать тему
Опции темы

Текущее время: 10:59. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru