我想创建一个函数,将字符串拆分为长度相等n
的字符串,并返回一个字符向量。
e.g。 F('atgctgttg',n=5)
应该返回
'atgct','tgctg','gctgt','ctgtt','tgttg'
我尝试了两种不同的功能:
// [[Rcpp::export]]
CharacterVector f( const std::string str, const int n ) {
int lim = str.length() - n + 1;
CharacterVector result( lim );
for ( int j = 0; j < lim; j++ )
{
result[j] = str.substr( j, n );
}
return result;
}
和
// [[Rcpp::export]]
CharacterVector f1( const std::string str, const int n ) {
const int lim = str.length();
const int n1 = n - 1;
CharacterVector result( lim - n1 );
int j = 1;
std::string tmp = str.substr( 0, n );
result[0] = tmp;
for ( int i = n; i < lim; i++ )
{
tmp.erase( 0, 1 );
tmp.push_back( str[i] );
result[j] = tmp;
j++;
}
return result;
}
我也尝试使用迭代器,但它并不比函数f1
快。
请注意,Rcpp将输入转换为引用变量。
有更快的方法吗?
答案 0 :(得分:1)
我将使用的方法是创建一个迭代器到字符串的开头,一个迭代器到第一个子字符串的结尾。然后使用std::vector
使用emplace_back()
在作为子字符串的向量的末尾构造一个字符串。然后递增两个迭代器,直到结束。
std::vector<std::string> splitString(const std::string& str, std::size_t len)
{
if (len >= str.size())
return { str };
auto it = str.begin();
auto end = it + len;
std::vector<std::string> strings;
while (end != str.end())
{
strings.emplace_back(it, end);
++end;
++it;
}
// have to do this to get the last string since end == str.end()
strings.emplace_back(it, end);
return strings;
}
答案 1 :(得分:1)
编译器会将您的 string _text = Convert.ToString(btn.Text);
int iSelectionStart = txtCal.SelectionStart;
string sBefore = txtCal.Text.Substring(0, iSelectionStart);
string sAfter = txtCal.Text.Substring(iSelectionStart + txtCal.SelectionLength);
txtCal.Text = sBefore + _text + sAfter;
txtCal.SelectionStart = iSelectionStart;
txtCal.SelectionLength = _text.Length;
函数转换为最快的代码,如果您更改为通过引用复制:f
虽然您未能看到速度提升,但您可以通过取消CharacterVector f(const std::string& str, const int n)
并使用CharacterVector
来简化您的流程:
vector<string>
如果您可以使用const string str("atgctgttg");
const int n = 5; // Assumed positive number smaller than str.size()
const int n1 = n - 1;
vector<string> result(str.size() - n1);
transform(str.cbegin(), str.cend() - n1, result.begin(), [n](const auto& i) {return string(&i, n);});
代替array
,可以看到速度提升的一种方式:
string
但到目前为止,执行此操作的最快(最好)方法只是处理原始const string str("atgctgttg");
const int n1 = N - 1;
vector<array<char, N>> result(str.size() - n1);
transform(str.cbegin(), str.cend() - n1, result.begin(), [](const auto& i) {
array<char, N> result;
copy_n(&i, N, result.begin());
return result;
});
和而不是将其分解为string
的数组。这需要在后端进行更多工作,因为您需要使用c-strings而不是string
s。例如,我已使用std::string
打印我的所有for (auto& i : result) cout << string(i.data(), N) << endl;
,但如果您没有使用vector
,则可以打印出:vector
显然需要更多工作,但如果你的for (auto i = str.cbegin(); i != str.cend() - n1; ++i) printf("%.*s\n", n, &*i);
很大,你会发现它更快。
答案 2 :(得分:0)
首先,您的功能签名存在问题:
CharacterVector f( const std::string str, const int n )
您通过值传递string
,在函数的每次调用中都会有一个字符串副本(除非您使用C ++ 11传递可移动字符串)。最好通过const引用const std::string& str
传递字符串。
关于这个问题,我想到了两个可能的答案。
代码(已测试:GCC 4.9.2与C ++ 11)
#include <iostream>
#include <vector>
struct string_ref {
const char* start;
const char* end;
};
// [[Rcpp::export]]
std::vector<string_ref> f(std::string&&, const int) = delete; // disallow calls with temporaries
// [[Rcpp::export]]
std::vector<string_ref> f(const std::string& str, const int n) {
int lim = str.length() - n + 1;
std::vector<string_ref> result(lim);
for (int j = 0; j < lim; j++) {
result[j] = { &str[j], &str[j + n] };
}
return result;
}
int main() {
std::string input{"atgctgttg"};
auto result = f(input, 5);
for (const auto r : result) {
std::cout << std::string(r.start, r.end) << std::endl;
}
return 0;
}
许多解析文本的库使用此方法(例如:词法分析器,正则表达式引擎等)。对于C ++ 17,有一个建议类型std::string_view,用于引用部分或全部字符串字符。
根据代码中的注释,您正在实现在R中使用的功能(完全不知道),在这种情况下,第二个解决方案可能会带来内存访问问题(输入字符串内存需要使用子串指针时可访问和实时)。如果输入字符串是在R中创建并调用F
,则返回指针很可能是有效的,则需要测试更好的证据。
问题中的代码2示例。第一个是更快的,因为在每个循环的第二个中,有一个字符的擦除和push_back(在大多数STL实现中擦除第一个字符很可能需要字符串的所有其他字符的副本) ,在某些情况下,push_back可能需要扩展字符串的内存。