Расширять имена файлов, которые имеют переменные среды на своем пути
Какой лучший способ расширения
${MyPath}/filename.txt to /home/user/filename.txt
или
%MyPath%/filename.txt to c:\Documents and settings\user\filename.txt
без прохождения строки пути, непосредственно ищущей переменные environement?
Я вижу, что wxWidgets имеет функцию wxExpandEnvVars. Я не могу использовать wxWidgets в этом случае, поэтому я надеялся найти эквивалент boost:: filesystem или аналогичный. Я использую только домашний каталог в качестве примера, я ищу расширение пути общего назначения.
Ответы
Ответ 1
Для систем 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;
}
Похоже, что он даже сделает глобальные расширения (которые могут быть полезны или не могут быть полезны для вашей конкретной ситуации).
Ответ 2
В Windows вы можете использовать ExpandEnvironmentStrings
. Не уверен насчет эквивалента Unix.
Ответ 3
Простая и портативная:
#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 *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
Ответ 4
Если у вас есть роскошь использования С++ 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;
}
Преимущество такого подхода заключается в том, что его можно легко адаптировать, чтобы справиться с синтаксическими вариациями и иметь дело с широкими строками. (Скомпилирован и протестирован с использованием Clang на OS X с флагом -std = С++ 0x)
Ответ 5
В языке C/С++, вот что я делаю для разрешения переменных окружения в Unix. Указатель fs_parm будет содержать спецификацию (или текст) возможных переменных окружения, подлежащих расширению. Пространство, которое указывает wrkSpc, должно быть MAX_PATH + 60 символов длинным. Двойные кавычки в эхо-строке предназначены для предотвращения обработки диких карт. Большинство стандартных оболочек должны иметь возможность справиться с этим.
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().
Ответ 6
Это то, что я использую:
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;
}
Ответ 7
Поскольку вопрос помечен как "wxWidgets", вы можете использовать функцию wxExpandEnvVars()
, используемую wxConfig для расширения своей среды. К сожалению, сама эта функция не документирована, но в основном делает то, что вам кажется, и расширяет любые вхождения $VAR
, $(VAR)
или ${VAR}
на всех платформах, а также %VAR%
только под Windows.
Ответ 8
Используя 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(Он также работает с вложенными расширениями)
Ответ 9
Как вопрос отмечен С++, вы можете использовать стандартный getenv с оттенком стандартного С++ 11 regex, чтобы заменить переменную на ее значение.