简体   繁体   English

不区分大小写的字符串替换器

[英]Case-insensitive String Substitutor

I am using org.apache.commons.lang3.text.StrSubstitutor to parse a String.我正在使用org.apache.commons.lang3.text.StrSubstitutor来解析字符串。 I have it setup similar to this:我有类似这样的设置:

StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(killer) killed &(target)!");

This no longer works if I write the keys in different cases:如果我在不同情况下编写密钥,这将不再有效:

"Information: &(KILLER) killed &(TARGET)!"

Is there a way of making the keys for the String Substitutor case-insensitive ?有没有办法让 String Substitutor 的键不区分大小写

I cannot use toLowerCase() because I only want the keys to be case-insensitive.我不能使用toLowerCase()因为我只希望键不区分大小写。

StrSubstitutor has a constructor that takes an instance of StrLookup. StrSubstitutor 有一个构造函数,它接受一个 StrLookup 的实例。 You can create an implementation of StrLookup that lowercases the keys its looking for before actually looking for them.您可以创建 StrLookup 的实现,在实际查找之前将其查找的键小写。

Here's how it looks like:这是它的样子:

public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {

private final Map<String, V> map;

CaseInsensitiveStrLookup(final Map<String, V> map) {
    this.map = map;
}

@Override
public String lookup(final String key) {
    String lowercaseKey = key.toLowerCase(); //lowercase the key you're looking for
    if (map == null) {
        return null;
    }
    final Object obj = map.get(lowercaseKey);
    if (obj == null) {
        return null;
    }
    return obj.toString();
}
}

Using this StrLookup implementation you don't need to worry about what kind of Map you're passing to the constructor.使用此 StrLookup 实现,您无需担心传递给构造函数的 Map 类型。

The following test case returns succesfully, using the above implementation:以下测试用例成功返回,使用上述实现:

import org.apache.commons.lang3.text.StrSubstitutor;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.HashMap;
import java.util.Map;

@Test
public class TestClass {

@Test
public void test() {

    Map<String, String> messageValues = new HashMap<String, String>();
    messageValues.put("killer", "Johnson");
    messageValues.put("target", "Quagmire");
    StrSubstitutor sub = new StrSubstitutor(new CaseInsensitiveStrLookup<String>(messageValues), "&(", ")", '\\');

    String format2 = sub.replace("Information: &(killer) killed &(target)!");
    String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
    Assert.assertEquals(format, "Information: Johnson killed Quagmire!");
    Assert.assertEquals(format2, "Information: Johnson killed Quagmire!");
}
}

You don't need to write a custom class.您不需要编写自定义类。 Assuming you can live with the log( n ) access times, just use a case-insensitive TreeMap.假设您可以忍受 log( n ) 访问时间,只需使用不区分大小写的 TreeMap。

public static void main(String[] args) {
    Map<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    m.put("foo", "bar");
    StrSubstitutor sub = new StrSubstitutor(m);
    String s = sub.replace("${FOO}");
    System.out.println(s);
} // prints "bar"

I think this case-insensitive map would work:我认为这个不区分大小写的地图会起作用:

import java.util.HashMap;
import java.util.Map;

public class CaseMap<V> extends HashMap<String, V> {
    public CaseMap() {
    }

    public CaseMap(int capacity) {
        super(capacity);
    }

    public CaseMap(int capacity, float loadFactor) {
        super(capacity, loadFactor);
    }

    public CaseMap(Map<String, ? extends V> map) {
        putAll(map);
    }

    public V put(String key, V value) {
        return super.put(key.toUpperCase(), value);
    }

    public V get(Object key) {
        if (!(key instanceof String)) return null;
        return super.get(((String)key).toUpperCase());
    }
}

If you don't control the creation of the messageValues map, you could build a CaseMap from it like this:如果您不控制 messageValues 映射的创建,您可以像这样从它构建一个 CaseMap:

CaseMap<String> caseFreeMessageValues = new CaseMap<String>(messageValues);

And then build your StrSubstitutor like this:然后像这样构建你的 StrSubstitutor :

StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");

You might want to think about other methods of Map that should be overridden as well, such as containsKey .您可能想考虑也应该覆盖的其他Map方法,例如containsKey

In case you need flexibility with both the Map and the Tokens being case insensitive AND you are not in control of the map being built you can use something like this.如果您需要地图和令牌不区分大小写的灵活性,并且您无法控制正在构建的地图,您可以使用这样的东西。

String replaceTokens(String str, Map<String, String> messageValues) {
    if(tokenToValue == null || tokenToValue.size() < 1) return str;
    StrSubstitutor caseInsensitiveTokenReplacer = new StrSubstitutor(new CaseInsensitiveStrLookup<>(messageValues),
                                                                     "&(", ")", '\\');
    return caseInsensitiveTokenReplacer.replace(str);
}

StrLookup Implementation StrLookup 实现

public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {

private final Map<String, V> map = new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER);

public CaseInsensitiveStrLookup(final Map<String, V> map) throws NullValueKeyNotSupported {
    if(map.containsKey(null)) throw new Exception(); // Dont want to support null 
    this.map.putAll(map);
}

@Override
public String lookup(final String key) {
    V value = map.get(key);
    if(value == null) return null;
    return value.toString();
}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM