我已经用了很长时间了,效果很好。
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
class Utility {
public:
Utility() = delete;
Utility( const Utility& ) = delete;
Utility& operator=( const Utility& ) = delete;
static std::string trim( const std::string& str,
const std::string elementsToTrim = " \t\n\r" );
static std::vector<std::string> splitString( const std::string& stringToSplit,
const std::string& strDelimiter,
const bool keepEmpty = true );
};
#endif // !UTILITY_H
Utlity.cpp
#include "Utility.h"
#include <vector>
#include <string>
#include <algorithm>
std::string Utility::trim( const std::string& str,
const std::string elementsToTrim ) {
std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
if ( firstIndex == std::string::npos )
return std::string(); // Nothing Left
std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
return str.substr( firstIndex, lastIndex - firstIndex + 1 );
}
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit,
const std::string& strDelimiter,
const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() )
vResult.push_back( strTemp );
if ( itSubStrEnd == strStringToSplit.end() )
break;
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
}
main.cpp
#include <iostream>
#include <vector>
#include <string>
#include "Utility.h"
int main() {
std::vector<std::tring> result;
std::string str( "This|is;a|test." );
std::cout << str << std::endl;
result = Utility::splitString( str, ";" );
str.clear();
for ( auto& s : result )
str += s + " ";
std::cout << str << std::endl;
result.clear();
result = Utility::splitString( str, "|" );
str.clear();
for ( auto& s : result )
str += s + " ";
std::cout << str << std::endl;
system( "PAUSE" );
return 0;
}
使这个 splitString 函数如此出色的原因在于,假设我有一个字符串模式,并且我想消除字符串中的字符集;您可以在上面的 main 中重用相同的向量和字符串并运行:
{
// [...]
str.clear();
result.clear();
str = std::string( "cruelcruelhellocruelcruelmadryochcruel" );
result = Utility::splitString( str, "cruel" );
str.clear();
for ( auto& s : result )
str += s + " ";
str = Utility::trim( str );
std::cout << str << std::endl;
system( "PAUSE" );
return 0;
}
它可以消除字符串中的字符集或字符串集。快速示例 - 结果。
test string - wellaaabbbdone
splitString( s, "a" ) = "well bbbdone"
"aa" = "well abbbdone"
"aaa" = "well bbbdone";
"ab" = "wellaa bbdone";
"aabb" = "wella bdone";
"aaabbb" = "well done";
// without calling trim.* This may not be accurate; I did add it & removed it
// several times; but you should still get the idea.
您可以轻松地将stringstream 替换到此 splitString() 算法中,并在需要时使用其 str() 函数。
编辑 - 用户:Christian Hackl 抱怨我在一个类中包含所有相关的静态函数,并说要使用命名空间,因为这是 C++ 而不是 Java。就我个人而言,我看不出有什么大不了的,但如果有问题,您可以删除包装类 Utility 并将所有通常相关的独立函数放在 namespace 中:
// Utility.h
#ifndef UTILITY_H
#define UTILITY_H
namespace util {
// function declarations;
} // namespace util
#endif // !UTILITY_H
//===================================
// Utility.cpp
#include "Utility.h"
// One of tree ways here:
// First:
using namespace util;
// function definitions;
// Second:
util::function definitions
// Third:
namespace util {
// function definitions
} // namespace util
最后说明:
在我上面的原始类Utility 中包含用于处理字符串的静态方法,我出于特定原因将它们放在一个类中,而不是仅仅驻留在命名空间中的独立函数。
用户——Christian Hackl 表示:
“从未创建过 Util 的对象;它有点像命名空间。” - 然后使用实际的命名空间。正如我所说,这不是 Java。
我会反对。别弄错我的意思;命名空间是 C++ 的重要组成部分,应该在适当的时候相应地使用。但是,在某些情况下,命名空间不够用,语言和编译器所需的机制需要类或结构的形式。我这是什么意思?这是一个稍微高级一点的主题,但非常简单。你看,在我上面的课程中,我提供了;我只展示了这个类的几个函数。我有大约十几个其他功能,其中一些是功能模板。因此,同样的论点可以说是在命名空间中有独立的函数模板。这不是我班的情况。我的类有几个私有函数模板,用于将字符串作为输入并将其转换为默认的基本类型(int、unsigned、float、double 等)。我什至有将字符串转换为glm::vec2、vec3、vec4、glm::ivec2、ivec3、ivec4、glm::mat3、glm::mat4 的函数,它们遵循相同的约定。
这些转换函数调用一个函数模板来获取该类型的值,但它依赖于函数模板的特化;这不能在命名空间中完成。如果您尝试在没有类或结构的情况下执行此操作;代码将编译,但您将收到链接器错误,因为模板的参数列表不能接受命名空间;但它可以接受类、结构或整数类型。
这是一个伪例子:
{ // header file
...
static int converToInt( const std::string& str );
static float convertToFloat( const std::string& str );
private:
template<typename T>
bool hasValue( const std::string& str, T* pValue );
template<typename T>
T getValue( const std::string );
}
// *.inl file
template<typename T>
bool Utility::hasValue( const std::string& str, T* pValue ) {
// string manipulation
pValue[0] = getValue<T>( str );
// check some condition return true or false
}
// *.cpp file
int convertToInt( const std::string& str ) {
// return converted int from string
}
float convertToFloat( const std::string& str ) {
// return converted float from string
}
template<>
int getValue( const std::string& ) {
return int that was converted from (convertToInt)
}
template<>
float getValue( const std::string& ) {
return float that was converted from (convertToFloat)
}
您不能在类之外执行此操作,只能在命名空间中执行此操作。这就是为什么我在原始演示中的上述函数是不可构造类中的静态方法的原因。
这里需要这种模式的原因是getValue()函数在所有情况下都将字符串作为参数,但唯一的区别是返回类型。在 C++ 和大多数其他语言中,不能单独对返回类型执行重载解析。它最终可能是一个模棱两可的调用,或者如果返回类型从未使用过;编译器甚至可能不会调用该函数。所以为了模拟这个;是函数需要是模板类型的地方;并且它们需要包含在一个类中以专门化这些功能,否则您不能专门化仅位于命名空间中的功能模板。
关于this is C++ 而不是Java 的整个评论确实是一个糟糕的评估。为什么?不要以任何方式贬低用户的形状或形式,但这种说法似乎完全有偏见。第一的;将静态方法放入没有任何成员的结构或类中并使构造函数私有是完全合法且有效的 C++ 代码。
它与 Java 无关;坦率地说,我从来没有用 Java 编写过任何东西。我只用 C 和 C++ 编程过。很多年前,当我在 90 年代后期上高中时,我可能已经在 Visual Basic 和 C# 中完成了一些小型应用程序,但在过去的近 20 年中,95% 都是 C++。你是对的,这是 C++ 而不是 Java。
欢迎来到具有多态行为、循环模板、模板元编程、泛型编程的模板编程世界,同时仍然支持在静态类型语言中既是过程范式又是面向对象范式的角色,现在支持更丰富的功能,例如作为自动类型推导、lambda、基于范围的循环、可变参数模板等等......!