парсинг строки в структуру массивов символов в стиле c - PullRequest
5 голосов
/ 17 марта 2012

У меня есть проект Visual Studio 2008 C ++, где мне нужно проанализировать строку в структуре массивов символов в стиле c.Какой самый элегантный / эффективный способ сделать это?

Вот мое текущее (работающее) решение:

struct Foo {
    char a[ MAX_A ];
    char b[ MAX_B ];
    char c[ MAX_C ];
    char d[ MAX_D ];
};

Func( const Foo& foo );

std::string input = "abcd@efgh@ijkl@mnop";
std::vector< std::string > parsed;
boost::split( parsed, input, boost::is_any_of( "@" ) );

Foo foo = { 0 };
parsed[ 1 ].copy( foo.a, MAX_A );
parsed[ 2 ].copy( foo.b, MAX_B );
parsed[ 3 ].copy( foo.c, MAX_C );
parsed[ 4 ].copy( foo.d, MAX_D );

Func( foo );

Ответы [ 3 ]

5 голосов
/ 17 марта 2012

Вот моя ( проверенная сейчас ) идея:

#include <vector>
#include <string>
#include <cstring>

#define MAX_A 40
#define MAX_B 3
#define MAX_C 40
#define MAX_D 4

struct Foo {
    char a[ MAX_A ];
    char b[ MAX_B ];
    char c[ MAX_C ];
    char d[ MAX_D ];
};

template <std::ptrdiff_t N>
const char* extractToken(const char* inIt, char (&buf)[N])
{
    if (!inIt || !*inIt)
        return NULL;

    const char* end = strchr(inIt, '@');
    if (end)
    {
        strncpy(buf, inIt, std::min(N, end-inIt));
        return end + 1;
    } 
    strncpy(buf, inIt, N);
    return NULL;
}

int main(int argc, const char *argv[])
{
    std::string input = "abcd@efgh@ijkl@mnop";

    Foo foo = { 0 };

    const char* cursor = input.c_str();
    cursor = extractToken(cursor, foo.a);
    cursor = extractToken(cursor, foo.b);
    cursor = extractToken(cursor, foo.c);
    cursor = extractToken(cursor, foo.d);
}

[Редактировать] Тесты

Добавление небольшого тестового кода

template <std::ptrdiff_t N>
std::string display(const char (&buf)[N])
{
    std::string result;
    for(size_t i=0; i<N && buf[i]; ++i)
       result += buf[i];
    return result; 
}

int main(int argc, const char *argv[])
{
    std::string input = "abcd@efgh@ijkl@mnop";

    Foo foo = { 0 };

    const char* cursor = input.c_str();
    cursor = extractToken(cursor, foo.a);
    cursor = extractToken(cursor, foo.b);
    cursor = extractToken(cursor, foo.c);
    cursor = extractToken(cursor, foo.d);

    std::cout << "foo.a: '" << display(foo.a) << "'\n";
    std::cout << "foo.b: '" << display(foo.b) << "'\n";
    std::cout << "foo.c: '" << display(foo.c) << "'\n";
    std::cout << "foo.d: '" << display(foo.d) << "'\n";
}

Выходы

foo.a: 'abcd'
foo.b: 'efg'
foo.c: 'ijkl'
foo.d: 'mnop'

Посмотреть вживую http://ideone.com/KdAhO

1 голос
/ 17 марта 2012

с использованием регулярного выражения будет выглядеть следующим образом (в C ++ 11 вы можете перевести это в boost или tr1 для VS2008):

// Assuming MAX_A...MAX_D are all 10 in our regex

std::cmatch res;
if(std::regex_match(input.data(),input.data()+input.size(),
                    res,
                    std::regex("([^@]{0,10})([^@]{0,10})([^@]{0,10})([^@]{0,10})")))
{
    Foo foo = {};
    std::copy(res[1].first,res[1].second,foo.a);
    std::copy(res[2].first,res[2].second,foo.b);
    std::copy(res[3].first,res[3].second,foo.c);
    std::copy(res[4].first,res[4].second,foo.d);
}

Вы, вероятно, должны создать шаблон, используя строку формата и действительные переменные MAX_*, а не жестко кодировать значения в регулярном выражении, как я сделал здесь, и вы также можете захотеть скомпилировать регулярное выражение один раз и сохранить его вместо воссоздания это каждый раз.

Но в противном случае этот метод позволяет избежать создания дополнительных копий строковых данных. char * s, хранящиеся в каждом подспаривании в res, являются указателем непосредственно в буфер входной строки, поэтому единственная копия находится непосредственно из входной строки в конечный объект foo.

1 голос
/ 17 марта 2012

А как насчет редизайна Foo?

struct Foo {
  std::array<std::string, 4> abcd;
  std::string a() const { return abcd[0]; }
  std::string b() const { return abcd[1]; }
  std::string c() const { return abcd[2]; }
  std::string d() const { return abcd[3]; }
};


boost::algorithm::split_iterator<std::string::iterator> end,
    it = boost::make_split_iterator(input, boost::algorithm::first_finder("@"));
std::transform(it, end, foo.abcd.begin(),
               boost::copy_range<std::string, decltype(*it)>);
...