Нечувствительный к регистру строковый заменитель

1

Я использую org.apache.commons.lang3.text.StrSubstitutor для синтаксического анализа строки. У меня есть настройка, подобная этому:

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

Это больше не работает, если я использую ключи в разных случаях:

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

Есть ли способ сделать ключи для String Substitator нечувствительными к регистру?

Я не могу использовать toLowerCase() потому что я хочу, чтобы ключи были нечувствительны к регистру.

  • 0
    Вы можете использовать конструктор StrSubstitutor который принимает StrLookup и написать свой собственный метод поиска без StrLookup регистра. Или вы можете создать подкласс HashMap<String, String> который выполняет поиск ключей на карте без учета регистра.
Теги:
string
formatting
key-value

4 ответа

3
Лучший ответ

У StrSubstitutor есть конструктор, который принимает экземпляр StrLookup. Вы можете создать реализацию StrLookup, которая уменьшает доступность ключей, прежде чем их искать.

Вот как это выглядит:

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();
}
}

Используя эту реализацию StrLookup, вам не нужно беспокоиться о том, какую карту вы передаете конструктору.

Следующий тестовый пример успешно завершается, используя приведенную выше реализацию:

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!");
}
}
  • 1
    Использование StrLookup - хорошая идея. Имейте в виду, что вам понадобится карта с строчными ключами, чтобы это работало.
2

Вам не нужно писать пользовательский класс. Предполагая, что вы можете жить с временем доступа к журналу (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"
  • 0
    Это также предполагает, что вы можете создать карту. Если вы получаете карту, предварительно созданную кем-то, вам необходимо использовать одно из решений StrLookup.
1

Я думаю, что эта карта без учета регистра будет работать:

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());
    }
}

Если вы не контролируете создание карты messageValues, вы можете создать из нее CaseMap следующим образом:

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

А затем создайте свой StrSubstitutor следующим образом:

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

Возможно, вы захотите подумать о других методах Map которые также должны быть переопределены, например, containsKey.

0

Если вам нужна гибкость, так как Map и токены нечувствительны к регистру, и вы не контролируете построенную карту, вы можете использовать что-то вроде этого.

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

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();
}}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню