java 在文本中展开环境变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4752817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 07:52:07  来源:igfitidea点击:

Expand environment variables in text

javaregex

提问by Michael Smith

I'm trying to write a function to perform substitutions of environment variables in java. So if I had a string that looked like this:

我正在尝试编写一个函数来在 java 中执行环境变量的替换。所以如果我有一个看起来像这样的字符串:

User ${USERNAME}'s APPDATA path is ${APPDATA}.

用户 ${USERNAME} 的 APPDATA 路径是 ${APPDATA}。

I want the result to be:

我希望结果是:

User msmith's APPDATA path is C:\Users\msmith\AppData\Roaming.

用户 msmith 的 APPDATA 路径是 C:\Users\msmith\AppData\Roaming。

So far my broken implementation looks like this:

到目前为止,我损坏的实现如下所示:

public static String expandEnvVars(String text) {        
    Map<String, String> envMap = System.getenv();
    String pattern = "\$\{([A-Za-z0-9]+)\}";
    Pattern expr = Pattern.compile(pattern);
    Matcher matcher = expr.matcher(text);
    if (matcher.matches()) {
        for (int i = 1; i <= matcher.groupCount(); i++) {
            String envValue = envMap.get(matcher.group(i).toUpperCase());
            if (envValue == null) {
                envValue = "";
            } else {
                envValue = envValue.replace("\", "\\");
            }
            Pattern subexpr = Pattern.compile("\$\{" + matcher.group(i) + "\}");
            text = subexpr.matcher(text).replaceAll(envValue);
        }
    }
    return text;
}

Using the above sample text, matcher.matches()returns false. However if my sample text, is ${APPDATA}it works.

使用上面的示例文本,matcher.matches()返回 false。但是,如果我的示例文本${APPDATA}有效。

Can anyone help?

任何人都可以帮忙吗?

回答by jjnguy

You don't want to use matches(). Matches will try to match the entire input string.

你不想使用matches(). 匹配将尝试匹配整个输入字符串。

Attempts to match the entire region against the pattern.

尝试将整个区域与模式匹配。

What you want is while(matcher.find()) {. That will match each instance of your pattern. Check out the documentation for find().

你想要的是while(matcher.find()) {. 这将匹配您的模式的每个实例。查看 的文档find()

Within each match, group 0will be the entire matched string (${appdata}) and group 1will be the appdatapart.

在每个匹配,group 0将是整个匹配的字符串(${appdata})和group 1将成为appdata一部分。

Your end result should look something like:

您的最终结果应该类似于:

String pattern = "\$\{([A-Za-z0-9]+)\}";
Pattern expr = Pattern.compile(pattern);
Matcher matcher = expr.matcher(text);
while (matcher.find()) {
    String envValue = envMap.get(matcher.group(1).toUpperCase());
    if (envValue == null) {
        envValue = "";
    } else {
        envValue = envValue.replace("\", "\\");
    }
    Pattern subexpr = Pattern.compile(Pattern.quote(matcher.group(0)));
    text = subexpr.matcher(text).replaceAll(envValue);
}

回答by rfeak

If you don't want to write the code for yourself, the Apache Commons Lang library has a class called StrSubstitutor. It does exactly this.

如果您不想自己编写代码,Apache Commons Lang 库有一个名为StrSubstitutor的类。它正是这样做的。

回答by user2161699

The following alternative has the desired effect without resorting to a library:

以下替代方案无需求助于库即可达到预期效果:

  • reads the map of environment variables once at startup
  • on calling expandEnvVars()takes the text with potential placeholders as an argument
  • the method then goes through the map of environment variables, one entry at at time, fetches the entry's key and value
  • and tries to replace any occurrence of ${<key>}in the text with <value>, thereby expanding the placeholders to their current values in the environment
  • 在启动时读取一次环境变量的映射
  • on callexpandEnvVars()将带有潜在占位符的文本作为参数
  • 然后该方法遍历环境变量的映射,一次一个条目,获取条目的键和值
  • 并尝试用 替换${<key>}文本中出现的任何<value>,从而将占位符扩展到它们在环境中的当前值
    private static Map<String, String> envMap = System.getenv();        
    public static String expandEnvVars(String text) {        
        for (Entry<String, String> entry : envMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            text = text.replaceAll("\$\{" + key + "\}", value);
        }
        return text;
    }

回答by lexx9999

Based on the accepted answer but without nested pattern replacement. Supports also default value and underscores in env-name: ${env-var[:default]}

基于接受的答案,但没有嵌套模式替换。还支持 env-name 中的默认值和下划线:${env-var[:default]}

  static String substituteEnvVars(String text) {
        StringBuffer sb = new StringBuffer();
        String pattern = "\$\{([A-Za-z0-9_]+)(?::([^\}]*))?\}";
        Pattern expr = Pattern.compile(pattern);
        Matcher matcher = expr.matcher(text);
        while (matcher.find()) {
            final String varname = matcher.group(1);
            String envValue = System.getenv(varname);
            if (envValue == null) {
                envValue = matcher.group(2);
                if (envValue == null)
                    envValue = "";
            }
            matcher.appendReplacement(sb, envValue);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

In additon variables are exactly substituted once. Text "${VAR}${X}" with VAR=${X} and X=x will be return "${X}x" not "xx".

此外,变量被精确替换一次。带有 VAR=${X} 和 X=x 的文本“${VAR}${X}”将返回“${X}x”而不是“xx”。

回答by Gray

I've taken @lexx9999 code and tweaked it a bit to use a StringBuilderand handle a Map as well as environmental variables.

我采用了@lexx9999 代码并对其进行了一些调整以使用 aStringBuilder并处理 Map 以及环境变量。

private final static Pattern ENV_VAR_PATTERN =
    Pattern.compile("\$\{([A-Za-z0-9_.-]+)(?::([^\}]*))?\}");
/**
 * Handle "hello ${var}", "${var:default}" formats for environ variable expansion.
 */
public static String substituteEnvVars(String text) {
    return substituteEnvVars(text, System.getenv());
}
/**
 * Handle "hello ${var}", "${var:default}", find var in replaceMap replace value.
 */
public static String substituteEnvVars(String text, Map<String, ?> replaceMap) {
    StringBuilder sb = new StringBuilder();
    Matcher matcher = ENV_VAR_PATTERN.matcher(text);
    int index = 0;
    while (matcher.find()) {
        sb.append(text, index, matcher.start());
        String var = matcher.group(1);
        Object obj = replaceMap.get(var);
        String value;
        if (obj != null)
            value = String.valueOf(obj);
        else {
            // if no env variable, see if a default value was specified
            value = matcher.group(2);
            if (value == null)
                value = "";
        }
        sb.append(value);
        index = matcher.end();
    }
    sb.append(text, index, text.length());
    return sb.toString();
}

回答by DVK

Unless you're just trying to learn how to build things from scratch, you should not really bother implementing your own template engine - there are multitudes already available.

除非你只是想学习如何从头开始构建东西,否则你真的不应该费心去实现你自己的模板引擎——已经有很多可用的模板引擎

One very good one is FreeMarker(which has Java API). Here is a tutorial.

一个非常好的是FreeMarker(它具有Java API)。这是一个教程