2

我有很多必须用 C++ 编写的 C# 代码。我在 C++ 方面没有太多经验。

我正在使用Visual Studio 2012进行构建。该项目是 C++ 中的静态库(不在 C++/CLI 中)。

在许多地方,他们使用String.Format,如下所示:

C#

String.Format("Some Text {0}, some other Text {1}", parameter0, parameter1);

现在,我知道以前有人问过类似的问题,但我不清楚什么是最标准/最安全的方法。

使用sprintfprintf之类的东西是否安全?我读到一些人提到他们不标准。像这样的东西?(这是 C++ 的方式,还是更多的 C 方式?)

C++(或者是C?)

char buffer [50];
int n, a=5, b=3;
n=sprintf (buffer, "Some Text %d, some other Text %d", a, b);

其他人建议做你自己的课,我看到了许多不同的实现。

目前,我有一个使用std::to_stringostringstreamstd::string.replacestd::string.findTemplates的类。我的课程相当有限,但对于我在 C# 代码中的情况,它可以工作。现在我不知道这是最有效的方法(甚至根本不正确):

C++

template <typename T>
static std::string ToString(T Number)
{
    std::ostringstream stringStream;
    stringStream << Number;
    std::string string = stringStream.str();
    return string;
};

template <typename T,unsigned S> 
static std::string Format(const std::string& stringValue, const T (&parameters)[S])
{ 
    std::string stringToReturn = std::string(stringValue);

    for (int i = 0; i < S; ++i)
    {
        std::string toReplace = "{"+ std::to_string(i) +"}";
        size_t f = stringToReturn.find(toReplace);
        if(std::string::npos != f)
            stringToReturn.replace(f, toReplace.length(), ToString(parameters[i]));
    }

    return stringToReturn;      
};

//I have some other overloads that call the Format function that receives an array.
template <typename T>
    static std::string Format(const std::string& stringValue, const T parameter, const T parameter2)
    {
        T parameters[] = {parameter, parameter2};
        return Format(stringValue, parameters);
    };

而且我需要我的代码同时在LinuxWindows中工作,所以我需要不同的编译器来构建它,这就是为什么我需要确保我使用的是标准方式。而且我的环境不能这么容易更新,所以我不能使用C++11。我也不能使用Boost,因为我不确定是否能够在需要它工作的不同环境中添加库。

在这种情况下我可以采取的最佳方法是什么?

4

3 回答 3

2

这是我为此目的而编写的一个单头库:fakeformat

测试:

REQUIRE(ff::format("{2}ff{1}").with('a').also_with(7).now()=="7ffa");

该库是可配置的,因此您可以从 0 开始参数索引。您还可以编写一个包装器,使其看起来与String.Format.

建立在 linux 上,不需要 c++11。

还没有标准的方法...

或者,您可以使用Boost.Locale格式

在这里,索引从 0 开始:

#include ...

struct dotnet_config {
  static const char scope_begin='{';
  static const char scope_end='}';
  static const char separator=',';
  static const char equals='=';
  static const size_t index_begin=0;
  static bool string_to_key(std::string const& to_parse,int& res) {
    std::istringstream ss(to_parse);
    ss.imbue(std::locale::classic());
    ss >> res;
    if (!ss.fail() && ss.eof())
      return true;
    return false;
  }
};

template <typename T1>
std::string Format (std::string const& format_string,T1 p1) {
 return ff::formatter<dotnet_config>(format_string).with(p1).now();
}

template <typename T1,typename T2>
std::string Format (std::string const& format_string,T1 p1,T2 p2) {
 return ff::formatter<dotnet_config>(format_string).with(p1).with(p2).now();
}

int main() {
  std::cout<<Format("test={0}",42)<<std::endl;
  std::cout<<Format("{0}!={1}",33,42)<<std::endl;
  return 0;
}

输出:

test=42
33!=42
于 2013-11-08T13:23:19.357 回答
1

如果您只有非对象类型(或者您手动将它们转换为 C 字符串,或将它们转换为字符串然后调用c_str() 成员函数),则 sprintf 可以工作。您可能需要snprintf提供的针对缓冲区溢出的额外保护。

如果您愿意学习更多来做您必须做的事情,您可以使用Boost Format 库。我相信您可以编写一个脚本来将 String.format 调用转换为 Boost 的语法。

如果您不能使用 Boost,并且不能使用 C++11,则必须使用 sprintf 并注意缓冲区溢出(如果您可以依赖编译器拥有它,则可能是 snprintf)。您可能想要编写一个脚本来包装所有参数,以便它们都转换为字符串:

String.Format("Some Text {0}, some other Text {1}", to_printf(p0), to_printf(p1));

另外,请注意 C 的格式不使用大括号。所以这是个大问题。您可能需要实现自己的可变参数函数


如果一切都很简单{0},您可能可以编写一个脚本来替换 String.Format 的大多数实例(而不是更复杂的实例),例如

`mystring = "Some Text "+tostring(p0)+", some other Text "+tostring(p1);`

这不是最有效的方法,但除非您每秒执行数千种格式,否则很可能无关紧要。或者可能稍微更有效(没有中间字符串):

`"mystring = static_cast<std::ostringstream&>(std::ostringstream().flush()<<Some Text "<<p0<<", some other Text "<<p1).str();`,

这创造了一个临时的。flush 类型的技巧让编译器认为它不是临时的,这解决了一个关于无法使用 non-member 的特定问题operator<<

于 2013-11-08T13:27:26.627 回答
0

为什么不使用<<运算符来格式化字符串?

string strOutput;
stringstream strn;
int i = 10;
float f = 20.0f;
strn << "Sally scored "<<i<< " out of "<<f << ". She failed the test!";
strn >> strOutput;
cout << strOutput; 
于 2013-11-08T13:18:58.817 回答