在 C++ 中连接字符串
Posted
技术标签:
【中文标题】在 C++ 中连接字符串【英文标题】:Concatenating strings in C++ 【发布时间】:2010-01-21 21:26:47 【问题描述】:我是一个相当缺乏经验的 C++ 程序员,所以这个问题可能是相当基本的。我正在尝试获取我的 copula 的文件名:
string MonteCarloBasketDistribution::fileName(char c)
char result[100];
sprintf(result, "%c_%s(%s, %s).csv", copula.toString().c_str(), left.toString().c_str(), right.toString().c_str());
return string(result);
用于:
MonteCarloBasketDistribution::MonteCarloBasketDistribution(Copula &c, Distribution &l, Distribution &r): copula(c), left(l), right(r)
//.....
ofstream funit;
funit.open (fileName('u').c_str());
ofstream freal;
freal.open (fileName('r').c_str());
但是,创建的文件具有垃圾名称,主要由奇怪的字符组成。知道我做错了什么以及如何解决吗?
【问题讨论】:
什么是“系词”?具体是什么类型的? sprintf 的第一个格式说明符已损坏。 @Neil:copula 属于 Copula 类型。 :P 见第二个 sn-p 的第一行。 【参考方案1】:sprintf 有 4 个占位符,而您只提供 3 个参数。
我建议:
string MonteCarloBasketDistribution::fileName(char c)
std::ostringstream result;
result << c <<"_"<<copula<<'('<<left<<", "<<right<<").csv";
return result.str();
您的sprintf
对于缓冲区溢出不安全,请使用 C99 snprintf
或 std::stringstream
【讨论】:
基本同意,只是因为他呈现的一切都是基于角色的,所以流有点过头了。 重点是C++中的toString()
是<<
。提供toString()
成员是错误的——它不是Java 或C#。 iostream 更加灵活。
使用sprintf
或流的决定在很大程度上是一个选择问题。就个人而言,我讨厌流。我发现sprintf
更简洁,更容易完全控制输出。
@John:也许不是普遍的,但有证据表明通用代码是完成任务的最佳方法之一。尤其是在 C++ 中。 :)
可以使用*printf
familiy,但是使用snprintf
而不是sprintf
... Whitch 更安全,但是 Oooops... MSVC 不支持 C99 snprintf...所以如果你在 MSVC 上使用 iostreams
【参考方案2】:
由于您要处理的所有内容都是基于字符的,因此使用 sprintf 有点愚蠢。
C++ 程序员应该做的还有更多类似的事情:
std::string result = copula.toString() + "(" + left.toString() + ","
+ right.toString() + ")";
【讨论】:
需要注意的是,在某些情况下使用 ostringstream 是一个更好的主意。在这种情况下,这段代码可能是正确的。 “应该”是非常主观的和风格问题。 有时有一个单独且清晰的格式说明符很有用,即使您只处理字符串也是如此。我发现冗长杂乱的字符串连接序列让眼睛很痛苦。使用“%c_%s(%s, %s).csv”之类的内容,您可以立即看到格式化规则是什么。通过使用 Boost.Format,您可以获得格式说明符的好处,而不会出现丢失/无效参数的危险。 @Emile:一旦我接受了培训,我承认 C 格式规范是最容易阅读的选项。但是,它们非常容易出错(至少对我而言),除非在极端情况下,否则我已经发誓不再使用它们。我浪费 小时 来追踪所谓的错误数据并不少见,这些数据最终只是格式错误,就像 Grzenio 上面所做的那样。 @TED:如果不是 Boost.Format,我会 100% 同意你的看法。除非万不得已,否则我不会用 10 英尺长的杆子碰 snprintf。 :-)【参考方案3】:您的格式字符串中有四个说明符,但只提供了三个附加参数。
【讨论】:
【参考方案4】:由于您似乎在 std::string 上工作,因此您根本不需要使用 sprintf。 std::string 具有易于使用的重载运算符,因此您可以使用 += 连接字符串。我认为 + 也可以,所以只需将 std::strings 一起“添加”即可。
【讨论】:
【参考方案5】:唯一看起来完全崩溃的是,您只向 sprintf 传递了 3 个数据参数,但它需要 4 个(%c、%s、%s、%s)
【讨论】:
【参考方案6】:Boost 有一个formatting library,它比 printf 和朋友们更安全。
#include <boost/format.hpp>
string MonteCarloBasketDistribution::fileName(char c)
return boost::str( boost::format("%c_%s(%s, %s).csv")
% c % copula.toString() % left.toString() % right.toString() );
或者,或者:
#include <boost/format.hpp>
string MonteCarloBasketDistribution::fileName(char c)
return boost::str( boost::format("%1%_%2%(%3%, %4%).csv")
% c % copula.toString() % left.toString() % right.toString() );
对于后一个示例,Boost 通过“查看”通过 % 运算符传入的参数类型知道 %1% 是一个字符,而 %2% 到 %4% 是字符串。
【讨论】:
:O Boost 总是让我感到惊讶。 当语言或标准库中似乎缺少一个普遍有用的特性时,很可能它已经在 Boost 中实现了。 :-) boost 如何处理第一个参数是 %c 并且传递给它的是字符串的地址,而不是字符?【参考方案7】:假设toString()
返回一个std::string
,
这个:
sprintf(结果, "%c_%s(%s, %s).csv", copula.toString().c_str(), left.toString().c_str(), right.toString().c_str());
...应该是:
sprintf(result, "%s_%s(%s, %s).csv", copula.toString().c_str(), left.toString().c_str(), right.toString().c_str());
【讨论】:
约翰,你可能是正确的,%s 应该是第一个而不是 %c,但也许他只想要字符串的第一个字符。如果是这样,他仍然是错的——他传递的是字符串的地址而不是第一个字符。当说明符需要四个时,您在这里真正错过的参数数量与传递给 sprintf 的参数数量不匹配,三个。给你减一,如果我可以投票的话。以上是关于在 C++ 中连接字符串的主要内容,如果未能解决你的问题,请参考以下文章