最好的扩展方式是什么
${MyPath}/filename.txt to /home/user/filename.txt
或
%MyPath%/filename.txt to c:\Documents and settings\user\filename.txt
无需遍历路径字符串直接查找环境变量? 我看到 wxWidgets 有一个 wxExpandEnvVars 函数。在这种情况下我不能使用 wxWidgets,所以我希望找到一个 boost::filesystem 等效或类似的。我仅以主目录为例,我正在寻找通用路径扩展。
对于 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 的扩展(这可能对您的特定情况有用,也可能没有用)。
如果你有幸使用 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;
}
这种方法的一个优点是它可以很容易地适应语法变化并处理宽字符串。 (在 OS X 上使用带有标志 -std=c++0x 的 Clang 进行编译和测试)
ExpandEnvironmentStrings
。还不确定 Unix 等价物。
简单便携:
#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
由于问题被标记为“wxWidgets”,您可以使用
wxConfig使用的
wxExpandEnvVars()
函数进行环境变量扩展。不幸的是,函数本身没有记录,但它基本上做了你认为它应该做的事情,并在所有平台上扩展了 $VAR
、$(VAR)
或 ${VAR}
以及仅在 Windows 下的 %VAR%
。
在C/C++语言中,下面是我在Unix下解析环境变量的做法。 fs_parm 指针将包含要扩展的可能环境变量的文件规范(或文本)。 wrkSpc 指向的空间必须是 MAX_PATH+60 个字符长。回显字符串中的双引号是为了防止处理通配符。大多数默认 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() 函数。
这是我用的:
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;
}
使用 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 (它也适用于嵌套扩展)
我需要能够解析嵌套的 env 变量,同时保留那些在环境中找不到的变量以供另一个解析器处理,所以我根据@sfkleach 的出色回答想到了这个:
#include <string>
#include <regex>
// Update the input string.
void autoExpandEnvironmentVariables(std::string& text) {
using namespace std;
static regex envRegex("\\$\\{?(\\w+)\\}?", regex::ECMAScript);
// 0,1 indicates to get the full match + first subgroup
sregex_token_iterator matchIter(text.begin(), text.end(), envRegex, {0, 1});
for (sregex_token_iterator end; matchIter != end; ++matchIter) {
const string match = matchIter->str();
const string envVarName = (++matchIter)->str();
// Search for env var and replace if found
const char * s = getenv(envVarName.c_str());
if (s != nullptr) {
string value(s);
// Handle nested env vars
autoExpandEnvironmentVariables(value);
// Since we're manipulating the string, do a new find
// instead of using original match info
size_t pos = text.find(match);
if (pos != string::npos) {
text.replace(pos, match.length(), value);
}
}
}
}