将字符串解析为 c 样式字符数组的结构
Posted
技术标签:
【中文标题】将字符串解析为 c 样式字符数组的结构【英文标题】:parsing a string to a structure of c-style character arrays 【发布时间】:2012-03-16 20:02:09 【问题描述】:我有一个 Visual Studio 2008 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 );
【问题讨论】:
我看不出这个解决方案有什么问题,除了如果你会经常添加到你的结构中,那么它可能是维护的痛苦,因为分配给 foo.a、foo.b 等没有不遵循 DRY 原则。你认为你会经常添加到这个结构中吗? 我不认为我会经常添加它。我最关心的是我复制这些字符串的次数。有没有办法减少这种情况? 我不这么认为。如果您要复制一个字符串,您可以简单地将其复制到结构的起始地址。在您的情况下,它会起作用,因为您的结构中只有字符并且它们是单字节对齐的。虽然我不建议这样做,因为我认为它很难看。但是无论如何你都不能这样做,因为你是从向量复制的,所以你必须一个一个地引用每个字符串。我认为你有什么是好的。 你有没有考虑把整个东西放到一个单独的函数中,例如Foo makeFooFromString(std::string &input)
【参考方案1】:
这是我(现已测试)的想法:
#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
上观看直播【讨论】:
添加了一个测试。在 http://ideone.com/KdAhO 上观看直播【参考方案2】:重新设计 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)>);
【讨论】:
想出一个完全不同的问题,然后回答它……怎么样?【参考方案3】:使用正则表达式看起来像这样(在 C++11 中,您可以将其转换为 VS2008 的 boost 或 tr1):
// 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
中的每个子匹配中保存的res
是直接指向输入字符串缓冲区的指针,因此唯一的副本是直接从输入字符串到最终的 foo 对象。
【讨论】:
以上是关于将字符串解析为 c 样式字符数组的结构的主要内容,如果未能解决你的问题,请参考以下文章