对于我的优化,我想在 Rcpp 中获得一个体面的toupper
。我对C++很陌生,至于知道我已经做到了:
#include <Rcpp.h>
using namespace Rcpp;
void C_toupper_String(const String& s) {
for (char *p =(char *)s.get_cstring();*p!=0;p++) *p = toupper(*p);
}
// [[Rcpp::export]]
StringVector C_toupper(StringVector const& vecteur) {
StringVector res=clone(vecteur);
for (int i(0); i < res.size(); ++i) {
C_toupper_String(res[i]);
}
return res;
}
/*** R
teststring <- "HeY I arNaud"
C_toupper(teststring)
toupper(teststring)
identical(C_toupper(teststring),toupper(teststring))
*/
但是,它不能正常工作。
> C_toupper(teststring)
[1] "HEY I ARNAUD"
> toupper(teststring)
[1] "HEY I ARNAUD"
> identical(C_toupper(teststring),toupper(teststring))
[1] FALSE
怎么了?如果可能的话,我不想将String
转换为std::string
,因为我想了解正在发生的事情:进入C++的目的是能够避免复制和转换。
谢谢
阿诺德
为什么这两个字符串不测试identical
的问题很难解释 - 这两个字符串在检查它们的原始字节(通过charToRaw
(时看起来肯定相同,它们不携带属性,并且它们没有编码集。所以,真的,它们应该是相同的。
为了解开这个谜团,我们需要了解你的C++代码实际上在做什么。更具体地说,C_toupper_String
中的C型演员正在做什么。由于它们的危险性,您永远不应该使用 C 型铸型。您的代码遇到问题纯粹是因为该强制转换。
为什么?因为String::get_cstring
返回char const*
.你把它抛弃给char*
,从而抛弃它的const
性。这可能是安全的,但前提是基础存储未const
。否则,它是未定义的行为 (UB(。由于代码重写(例如优化(,UB的效果很难预测。在这种情况下,它似乎生成的代码弄乱了 R 字符串内部。
您根本无法就地修改Rcpp::String
对象,他们不允许这样做。但是,如果您只是想避免复制,那么您的代码无论如何都无法实现其目标,因为您的C_toupper
函数在第一步中显式复制输入。
正如Dirk所说,解决这个问题的正确方法是使用可用的Rcpp API。在字符串修改的情况下,这意味着将您的输入转换为std::string
,执行修改,然后转换回来。这确实会复制,但您当前的代码也会复制。下面是编写此代码的一种好方法:
#include <Rcpp.h>
#include <cctype>
#include <string>
// [[Rcpp::export]]
Rcpp::StringVector C_toupper(Rcpp::StringVector const& vec) {
std::vector<std::string> res(vec.begin(), vec.end());
for (std::string& str : res) {
for (char& c : str) c = std::toupper(c);
}
return Rcpp::wrap(res);
}
请注意,这有时会产生错误的结果,因为std::toupper
根本无法处理某些 Unicode 特征。R的toupper
做得更好,但也有一些问题。一个正确的解决方案是使用 {stringr} 或 {stringi} 包。
为什么不使用C++库的单行代码? 除非你真的有C++经验,否则你可能不太可能击败它。 以下代码只是缩进在此处显示,它是我此处 R 会话中的一行。
R> Rcpp::cppFunction("std::string tU(std::string s) { std::string u(s);
for (unsigned int i=0; i<u.length(); i++) u[i] = std::toupper(u[i]); return(u); }")
R> tU("aBcDe")
[1] "ABCDE"
R>