【问题标题】: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"

标签: c++ string algorithm replace c++17


【解决方案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;
            }
        }
    }
    

    我不太确定它是否比公认的答案更好,但至少现在我知道如何使用 &lt;regex&gt;... 我认为必须有更好的方法来处理为每个令牌,但我看不到任何解决问题的方法。

    【讨论】:

    • 它不工作。它进入一个无限循环。你试过运行一次吗?
    • @risingStark 哎呀,你是对的!如果您有一个未在环境中定义的令牌,它将继续尝试替换它......叹息......我的测试是ideone.com/BoG8dB。不幸的是,我错过了那个案例。
    • @risingStark 查看新版本。
    猜你喜欢
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 2016-02-04
    • 1970-01-01
    • 2014-06-03
    • 2020-07-22
    • 1970-01-01
    相关资源
    最近更新 更多