【问题标题】:expand file names that have environment variables in their path展开路径中包含环境变量的文件名
【发布时间】:2010-12-26 12:54:41
【问题描述】:

什么是最好的扩展方式

${MyPath}/filename.txt to /home/user/filename.txt

%MyPath%/filename.txt to c:\Documents and settings\user\filename.txt

不遍历路径字符串直接查找环境变量? 我看到 wxWidgets 有一个wxExpandEnvVars 函数。在这种情况下我不能使用 wxWidgets,所以我希望找到一个 boost::filesystem 等效或类似的。我只是以主目录为例,我正在寻找通用路径扩展。

【问题讨论】:

    标签: c++ wxwidgets boost-filesystem


    【解决方案1】:

    在 Windows 上,您可以使用ExpandEnvironmentStrings。还不确定是否有 Unix 等价物。

    【讨论】:

    • 谢谢罗伯。这让我成功了一半。我想我会研究非 Windows 案例的解析方法。
    【解决方案2】:

    对于 UNIX(或至少 POSIX)系统,请查看 wordexp

    #include <iostream>
    #include <wordexp.h>
    using namespace std;
    int main() {
      wordexp_t p;
      char** w;
      wordexp( "$HOME/bin", &p, 0 );
      w = p.we_wordv;
      for (size_t i=0; i<p.we_wordc;i++ ) cout << w[i] << endl;
      wordfree( &p );
      return 0;
    }
    

    它似乎甚至会进行类似 glob 的扩展(这可能对您的特定情况有用,也可能没有用)。

    【讨论】:

    • 对我来说,这是另一半。上述解决方案适用于 Posix 和 Window。
    • 如果可以的话,我会投三票。使用系统提供的设施的很好的答案,这些设施肯定比正则表达式更不容易出错。
    • 注:这在引擎盖下使用 /bin/sh 并分叉进程来实现结果。
    • 请注意 wordexp 删除了引号,删除了空格等。通常它可以工作,但之后你的字符串会严重不同。例如,文件名中有不匹配的单引号是可以的,但 wordexp 会删除它。
    【解决方案3】:

    在 C/C++ 语言中,这是我在 Unix 下解析环境变量的方法。 fs_parm 指针将包含要扩展的可能环境变量的文件规范(或文本)。 wrkSpc 指向的空间必须是 MAX_PATH+60 个字符长。 echo 字符串中的双引号是为了防止处理通配符。大多数默认 shell 应该能够处理这个问题。

    
       FILE *fp1;
    
       sprintf(wrkSpc, "echo \"%s\" 2>/dev/null", fs_parm);
       if ((fp1 = popen(wrkSpc, "r")) == NULL || /* do echo cmd     */
           fgets(wrkSpc, MAX_NAME, fp1) == NULL)/* Get echo results */
       {                        /* open/get pipe failed             */
         pclose(fp1);           /* close pipe                       */
         return (P_ERROR);      /* pipe function failed             */
       }
       pclose(fp1);             /* close pipe                       */
       wrkSpc[strlen(wrkSpc)-1] = '\0';/* remove newline            */
    
    

    对于 MS Windows,使用 ExpandEnvironmentStrings() 函数。

    【讨论】:

      【解决方案4】:

      这是我用的:

      const unsigned short expandEnvVars(std::string& original)
      {
          const boost::regex envscan("%([0-9A-Za-z\\/]*)%");
          const boost::sregex_iterator end;
          typedef std::list<std::tuple<const std::string,const std::string>> t2StrLst;
          t2StrLst replacements;
          for (boost::sregex_iterator rit(original.begin(), original.end(), envscan); rit != end; ++rit)
              replacements.push_back(std::make_pair((*rit)[0],(*rit)[1]));
          unsigned short cnt = 0;
          for (t2StrLst::const_iterator lit = replacements.begin(); lit != replacements.end(); ++lit)
          {
              const char* expanded = std::getenv(std::get<1>(*lit).c_str());
              if (expanded == NULL)
                  continue;
              boost::replace_all(original, std::get<0>(*lit), expanded);
              cnt++;
          }
          return cnt;
      }
      

      【讨论】:

        【解决方案5】:

        由于问题被标记为“wxWidgets”,您可以使用wxConfig 使用的wxExpandEnvVars() 函数进行环境变量扩展。不幸的是,该函数本身没有记录,但它基本上可以执行您认为应该执行的操作,并扩展所有平台上出现的 $VAR$(VAR)${VAR} 以及仅在 Windows 下出现的 %VAR%

        【讨论】:

          【解决方案6】:

          简单便携:

          #include <cstdlib>
          #include <string>
          
          static std::string expand_environment_variables( const std::string &s ) {
              if( s.find( "${" ) == std::string::npos ) return s;
          
              std::string pre  = s.substr( 0, s.find( "${" ) );
              std::string post = s.substr( s.find( "${" ) + 2 );
          
              if( post.find( '}' ) == std::string::npos ) return s;
          
              std::string variable = post.substr( 0, post.find( '}' ) );
              std::string value    = "";
          
              post = post.substr( post.find( '}' ) + 1 );
          
              const char *v = getenv( variable.c_str() );
              if( v != NULL ) value = std::string( v );
          
              return expand_environment_variables( pre + value + post );
          }
          

          expand_environment_variables( "${HOME}/.myconfigfile" ); 产生/home/joe/.myconfigfile

          【讨论】:

          • 请注意,这只扩展了一个环境变量。这个例子没问题。如果您需要在一个字符串中展开多个环境变量,请在 post 上递归调用 expane_environment_variables。
          • 注意:你调用getenv(variable.c_str())两次以获得相同的变量,更好地存储结果。
          • 你在 const* v 之后缺少“char”
          【解决方案7】:

          使用 Qt,这对我有用:

          #include <QString>
          #include <QRegExp>
          
          QString expand_environment_variables( QString s )
          {
              QString r(s);
              QRegExp env_var("\\$([A-Za-z0-9_]+)");
              int i;
          
              while((i = env_var.indexIn(r)) != -1) {
                  QByteArray value(qgetenv(env_var.cap(1).toLatin1().data()));
                  if(value.size() > 0) {
                      r.remove(i, env_var.matchedLength());
                      r.insert(i, value);
                  } else
                      break;
              }
              return r;
          }
          

          expand_environment_variables(QString("$HOME/.myconfigfile"));产生 /home/martin/.myconfigfile (它也适用于嵌套扩展)

          【讨论】:

            【解决方案8】:

            如果您有幸使用 C++11,那么正则表达式非常方便。我编写了一个用于就地更新的版本和一个声明性版本。

            #include <string>
            #include <regex>
            
            // Update the input string.
            void autoExpandEnvironmentVariables( std::string & text ) {
                static std::regex env( "\\$\\{([^}]+)\\}" );
                std::smatch match;
                while ( std::regex_search( text, match, env ) ) {
                    const char * s = getenv( match[1].str().c_str() );
                    const std::string var( s == NULL ? "" : s );
                    text.replace( match[0].first, match[0].second, var );
                }
            }
            
            // Leave input alone and return new string.
            std::string expandEnvironmentVariables( const std::string & input ) {
                std::string text = input;
                autoExpandEnvironmentVariables( text );
                return text;
            }
            

            这种方法的一个优点是它可以很容易地适应句法变化并处理宽字符串。 (在带有标志 -std=c++0x 的 OS X 上使用 Clang 编译和测试)

            【讨论】:

            • g++ 4.9.3 (Ubuntu) 无法编译,它与迭代器和 const_iterator 之间的转换有关。我必须更改 text.replace( match[0].first, match[0].second, var ); to text.replace(match.position(0), match.length(0), var);
            • gcc version 5.4.0 (Ubuntu)g++ -std=c++11 -c -Wall expand.cpp 尝试过,但没有收到任何错误。
            • 如果您可以包含输出(或将其发送给我),那就太好了。抱歉,我无法在 Ubuntu 18.04 上复制任何错误。
            • 不适用于 gcc 4.8 及更低版本,因为 regex 表达式是在 gcc 4.9 stackoverflow.com/questions/12530406/… 上实现的
            猜你喜欢
            • 2023-03-07
            • 2015-06-20
            • 2013-09-16
            • 1970-01-01
            • 2013-07-26
            • 1970-01-01
            • 2022-07-10
            相关资源
            最近更新 更多