在 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 snprintfstd::stringstream

【讨论】:

基本同意,只是因为他呈现的一切都是基于角色的,所以流有点过头了。 重点是C++中的toString()&lt;&lt;。提供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++ 中连接字符串的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中连接字符串和数字?

如何在 C++ 中连接字符串和整数? [复制]

在宏中连接字符串 - C++

GCC 如何连接多个 C++ std::string 变量?

在 C++ 中连接 char 数组

将带有时间戳的向量整数连接为C++中的字符串?