【问题标题】:Concatenating strings in C++在 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 的第一行。

标签: c++ linux g++


【解决方案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 的参数数量不匹配,三个。给你减一,如果我可以投票的话。
        猜你喜欢
        • 1970-01-01
        • 2010-12-16
        • 2012-01-04
        • 1970-01-01
        • 2013-08-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多