Обновлено В качестве «спасибо» за начисление бонуса я пошел и реализовал 4 функции, которые я изначально пропустил как «Вы не будете нуждаться в этом».
теперь поддерживает частично заключенные в кавычки столбцы
Это проблема, о которой вы сообщили: например, с разделителем ,
только test,"one,two",three
будет действительнымне test,one","two","three
. Теперь оба они приняты.
теперь поддерживает пользовательские выражения разделителя
В качестве разделителей можно указывать только отдельные символы.Теперь вы можете указать любое выражение анализатора Spirit Qi в качестве разделителя rule .Например,
splitInto(input, output, ' '); // single space
splitInto(input, output, +qi.lit(' ')); // one or more spaces
splitInto(input, output, +qi.lit(" \t")); // one or more spaces or tabs
splitInto(input, output, (qi::double_ >> !'#') // -- any parse expression
Примечание это меняет поведение при перегрузке по умолчанию
В старой версии по умолчанию повторяющиеся пробелы рассматривались как один разделитель.Теперь вам нужно явно указать, что ( 2 nd пример ), если вы хотите.
теперь поддерживает кавычки("") внутри значений в кавычках (вместо того, чтобы просто заставить их исчезнуть)
См. Пример кода.Довольно просто, конечно.Обратите внимание, что последовательность ""
вне конструкции в кавычках все еще представляет пустую строку (для совместимости, например, с существующими форматами вывода CSV, которые избыточно заключают в кавычки пустые строки)
поддержка диапазонов повышения в дополнение к контейнерам в качестве входных данных (например, char [])
Ну, вам это не понадобится (но это было довольно удобно для меня, чтобы просто иметь возможность писать splitInto("a char array", ...)
:)
Как я и ожидал наполовину, вам понадобилось понадобится частично цитируемых полей (см. Ваш комментарий 1 . Ну, здесьвы (узкое место заставляло его работать одинаково в разных версиях Boost)).
Введение
Случайные заметки и замечания для читателя:
Требуемые версии / проверено
Это было скомпилировано с использованием
- gcc 4.4.5,
- gcc 4.5.1 и
- gcc 4.6.1.
Работает (проверено) против
- boost 1.42.0 (возможно, и более ранних версий) на всем протяжении вплоть до
- boost 1.47.0.
Примечание : Сглаживание выходных контейнеров, похоже, работает только для Spirit V2.5 (повышение 1.47.0). (это может быть что-то простое, например, требуется дополнительное включение для более старых версий?)
Код!
//#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_DEBUG_PRINT_SOME 80
// YAGNI #4 - support boost ranges in addition to containers as input (e.g. char[])
#define SUPPORT_BOOST_RANGE // our own define for splitInto
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp> // for pre 1.47.0 boost only
#include <boost/spirit/version.hpp>
#include <sstream>
namespace /*anon*/
{
namespace phx=boost::phoenix;
namespace qi =boost::spirit::qi;
namespace karma=boost::spirit::karma;
template <typename Iterator, typename Output>
struct my_grammar : qi::grammar<Iterator, Output()>
{
typedef qi::rule<Iterator> delim_t;
//my_grammar(delim_t const& _delim) : delim(_delim),
my_grammar(delim_t _delim) : delim(_delim),
my_grammar::base_type(rule, "quoted_delimited")
{
using namespace qi;
noquote = char_ - '"';
plain = +((!delim) >> (noquote - eol));
quoted = lit('"') > *(noquote | '"' >> char_('"')) > '"';
#if SPIRIT_VERSION >= 0x2050 // boost 1.47.0
mixed = *(quoted|plain);
#else
// manual folding
mixed = *( (quoted|plain) [_a << _1]) [_val=_a.str()];
#endif
// you gotta love simple truths:
rule = mixed % delim % eol;
BOOST_SPIRIT_DEBUG_NODE(rule);
BOOST_SPIRIT_DEBUG_NODE(plain);
BOOST_SPIRIT_DEBUG_NODE(quoted);
BOOST_SPIRIT_DEBUG_NODE(noquote);
BOOST_SPIRIT_DEBUG_NODE(delim);
}
private:
qi::rule<Iterator> delim;
qi::rule<Iterator, char()> noquote;
#if SPIRIT_VERSION >= 0x2050 // boost 1.47.0
qi::rule<Iterator, std::string()> plain, quoted, mixed;
#else
qi::rule<Iterator, std::string()> plain, quoted;
qi::rule<Iterator, std::string(), qi::locals<std::ostringstream> > mixed;
#endif
qi::rule<Iterator, Output()> rule;
};
}
template <typename Input, typename Container, typename Delim>
bool splitInto(const Input& input, Container& result, Delim delim)
{
#ifdef SUPPORT_BOOST_RANGE
typedef typename boost::range_const_iterator<Input>::type It;
It first(boost::begin(input)), last(boost::end(input));
#else
typedef typename Input::const_iterator It;
It first(input.begin()), last(input.end());
#endif
try
{
my_grammar<It, Container> parser(delim);
bool r = qi::parse(first, last, parser, result);
r = r && (first == last);
if (!r)
std::cerr << "parsing failed at: \"" << std::string(first, last) << "\"\n";
return r;
}
catch (const qi::expectation_failure<It>& e)
{
std::cerr << "FIXME: expected " << e.what_ << ", got '";
std::cerr << std::string(e.first, e.last) << "'" << std::endl;
return false;
}
}
template <typename Input, typename Container>
bool splitInto(const Input& input, Container& result)
{
return splitInto(input, result, ' '); // default space delimited
}
/********************************************************************
* replaces '\n' character by '?' so that the demo output is more *
* comprehensible (see when a \n was parsed and when one was output *
* deliberately) *
********************************************************************/
void safechars(char& ch)
{
switch (ch) { case '\r': case '\n': ch = '?'; break; }
}
int main()
{
using namespace karma; // demo output generators only :)
std::string input;
#if SPIRIT_VERSION >= 0x2050 // boost 1.47.0
// sample invocation: simple vector of elements in order - flattened across lines
std::vector<std::string> flattened;
input = "actually on\ntwo lines";
if (splitInto(input, flattened))
std::cout << format(*char_[safechars] % '|', flattened) << std::endl;
#endif
std::list<std::set<std::string> > linewise, custom;
// YAGNI #1 - now supports partially quoted columns
input = "partially q\"oute\"d columns";
if (splitInto(input, linewise))
std::cout << format(( "set[" << ("'" << *char_[safechars] << "'") % ", " << "]") % '\n', linewise) << std::endl;
// YAGNI #2 - now supports custom delimiter expressions
input="custom delimiters: 1997-03-14 10:13am";
if (splitInto(input, custom, +qi::char_("- 0-9:"))
&& splitInto(input, custom, +(qi::char_ - qi::char_("0-9"))))
std::cout << format(( "set[" << ("'" << *char_[safechars] << "'") % ", " << "]") % '\n', custom) << std::endl;
// YAGNI #3 - now supports quotes ("") inside quoted values (instead of just making them disappear)
input = "would like ne\"\"sted \"quotes like \"\"\n\"\" that\"";
custom.clear();
if (splitInto(input, custom, qi::char_("() ")))
std::cout << format(( "set[" << ("'" << *char_[safechars] << "'") % ", " << "]") % '\n', custom) << std::endl;
return 0;
}
Выходные данные
Выходные данные из примера, как показано:
actually|on|two|lines
set['columns', 'partially', 'qouted']
set['am', 'custom', 'delimiters']
set['', '03', '10', '13', '14', '1997']
set['like', 'nested', 'quotes like "?" that', 'would']
Обновление Выходные данные для ранее неудачного теста:
--server=127.0.0.1:4774/|--username=robota|--userdescr=robot A ? I am cool robot ||--robot|>|echo.txt
1 Должен признаться, я посмеялся, когда прочитал, что «он разбился» [ sic ] Это очень похоже на моих конечных пользователей. Просто чтобы быть точным: сбой является неисправимой ошибкой приложения. То, с чем вы столкнулись, было обработанной ошибкой, и с вашей точки зрения было не более чем «неожиданным поведением». В любом случае, это исправлено:)