替换字符串中的标记
Posted
技术标签:
【中文标题】替换字符串中的标记【英文标题】:Replacing tokens inside string 【发布时间】:2021-03-30 19:39:31 【问题描述】:我想要做的是用类似语法[[VULKAN_SDK]]
在字符串中替换所有标记。我想获取括号的内容(在本例中为VULKAN_SDK
),并通过传递括号的内容将整个令牌替换为我从getenv
获得的值。示例 [[VULKAN_SDK]]
被 C:\VulkanSDK\1.2.148.1
(系统环境变量)替换。
如果环境变量不存在,则不应替换令牌。
这是我的代码:
void replaceSystemEnviromentTokens(std::string& var)
static const char* tokenOpening = "[[";
static const char* tokenClosure = "]]";
auto getEnvVal = [](const char* var)
char * val = std::getenv(var);
return val == NULL ? std::string() : std::string(val);
;
std::size_t temp = var.find(tokenOpening, 0);
while(temp != std::string::npos)
const std::size_t beginning = temp;
const std::size_t end = var.find(tokenClosure, beginning);
temp = var.find(tokenOpening, beginning + 1);
if(end == std::string::npos)
break;
if(temp != std::string::npos)
if(end > temp)
continue;
const std::size_t tokenSize = end - (beginning + strlen(tokenOpening));
const std::string token = var.substr(beginning + strlen(tokenOpening), tokenSize);
const std::string translatedToken = getEnvVal(token.c_str());
if(!translatedToken.empty())
const std::size_t currentSize = var.length();
var.replace(beginning, end, translatedToken);
if(temp != std::string::npos)
temp += currentSize - var.length();
if(temp > var.length())
temp = std::string::npos;
测试:
int main(int argc, char** argv)
std::string test = "this should be converted: [[VULKAN_SDK]] this should not be converted: VULKAN_SDK]] this: [[VULKAN_SDK]]";
replaceSystemEnviromentTokens(test);
std::cout << test << std::endl;
当前行为:this should be converted: C:\VulkanSDK\1.2.148.1erted: VULKAN_SDK]] this: [[VULKAN_SDK]]
预期行为:this should be converted: C:\VulkanSDK\1.2.148.1 this should not be converted: VULKAN_SDK]] this: C:\VulkanSDK\1.2.148.1
【问题讨论】:
为什么不用c++17内置的replace()
函数呢?
会有test = "this should be replaced [[vul[kan]]"?
这样的字符串在这种情况下会发生什么?我想问的是,'['
或 ']'
字符会像任何其他普通字符一样出现在字符串中吗?
或test = "abc [[vul[[kan]] xyz"
或test = "abc [[[kan]] xyz"
?
或者如果test = "abc [[vul [[jjk]] hkj [[hjh]] [[nj [[ hjgk[[ ]]ghjg[[gfgh]] ]] ]] [[kan]] xyz"
【参考方案1】:
使用 C++17 中内置的 replace()
函数,您可以用最少的代码完成上述任务。
如果给定字符串 test = "abc [[vul [[hjh]] [[nj [[ hg[[gfgh]] ]]m ]]xyz"
或 "abc [[vul[[kan]] xyz"
或类似类型,那么您可以将其视为使用堆栈解决的 balanced parenthesis 问题的变体。
void replaceSystemEnviromentTokens(std::string& var)
static const char* tokenOpening = "[[";
static const char* tokenClosure = "]]";
auto getEnvVal = [](const char* var)
char * val = std::getenv(var);
return val == NULL ? std::string() : std::string(val);
;
std::stack<int> s;
for(int i =0; i<var.size();i++)
if(var[i]=='[' && (i+1)<var.size() && var[i+1]=='[')
s.push(i);
i++;
if(var[i]==']' && (i+1)<var.size() && var[i+1]==']')
if(!s.empty())
// This means we have encountered a [[ before as well
// And using the stack, we would get the most recent [[
int a = s.top();
s.pop();
// A corner case of [[]] can be checked here i.e empty string
// The case of [[ ]] can also be checked but for now it is omitted
int beg = a + strlen(tokenOpening);
const std::size_t tokenSize = i - beg;
if(tokenSize > 0)
// Non-empty token enclosed between [[____]]
const std::string token = var.substr(beg, tokenSize);
const std::string translatedToken = getEnvVal(token.c_str());
if(!translatedToken.empty())
var.replace(a, tokenSize + strlen(tokenOpening) + strlen(tokenClosure), translatedToken);
/*
Here you need to decide what to initialize i with here
If the translatedToken itself would bring more [[___]] pairs
and you would want to replace their tokens as well
then i = a-1, if not then i = a + translatedToken.size()-1
*/
i = a + translatedToken.size()-1;
【讨论】:
【参考方案2】:编辑:risingStark 发现原始版本无法正常工作!这是一个看起来肯定比原来的更丑的修复,但至少它有效。我真的很讨厌从迭代器开始的需要,但我不能很快找到更好的方法。在这里试试这个:https://ideone.com/Hcb4BM
我想用正则表达式来做这件事,但我花了很长时间才明白它们在 C++ 中是如何工作的。不管怎样,结果是这样的:
void replaceSystemEnviromentTokens(std::string& var)
static std::string tokenOpening = "\\[\\[";
static std::string tokenClosure = "\\]\\]";
auto getEnvVal = [](const std::string& var)
char* val = getenv(var.c_str());
return std::string(val ? val : "");
;
std::regex re(tokenOpening + "(\\S+)" + tokenClosure);
std::sregex_iterator it(var.begin(),var.end(),re);
std::sregex_iterator end;
while (it != end)
auto& m = *it;
auto value = getEnvVal(m.str(1));
if (!value.empty())
std::regex re_replace(tokenOpening + m.str(1) + tokenClosure);
var = regex_replace(var, re_replace, value);
it = std::sregex_iterator(var.begin(), var.end(), re);
else
++it;
我不太确定它是否比公认的答案更好,但至少现在我知道如何使用 <regex>
... 我认为必须有更好的方法来处理为每个令牌,但我看不到任何解决问题的方法。
【讨论】:
它不工作。它进入一个无限循环。你试过运行一次吗? @risingStark 哎呀,你是对的!如果您有一个未在环境中定义的令牌,它将继续尝试替换它......叹息......我的测试是ideone.com/BoG8dB。不幸的是,我错过了那个案例。 @risingStark 查看新版本。以上是关于替换字符串中的标记的主要内容,如果未能解决你的问题,请参考以下文章
替换字符串中的 html 标记,但保留文本并用自定义标记重新换行