Декодировать% в пространство, используя URLDecoder в Java?
У меня есть сценарий использования, в котором я должен декодировать queryParameter URI и делать что-то (вне контекста этого вопроса).
Предположим, у меня есть URI, и я должен его декодировать. Теперь я знаю, что в настоящее время все %20 будут преобразованы в пространство, и при создании пространства URI должно быть представлено %20, но может быть случай, когда я могу получить URI с % в качестве пространства. Поэтому я хочу преобразовать % в пространство, чтобы обеспечить обратную совместимость. В конце есть примечание, которое поможет понять вопрос.
Я старался replaceall() %
с %20
но опять же %20
станет %2020
и много других исключений.
Это необходимо для чтения URI UPI. Согласно официальным документам NPCI:
Примечание. Учитывая, что текущие приложения PSP разработаны для чтения "%" в качестве пробела (""), PSP Банка должен поддерживать как "%", так и "%20", пока экосистема не будет приведена в соответствие с пересмотром. Следовательно, обратная совместимость должна быть обеспечена.
РЕДАКТИРОВАТЬ 1 На основе пшемо комментария -
я пытался
str.replaceAll("%(?![0-9a-fA-F])","%20")
Случай, который не удовлетворяет приведенному выше регулярному выражению: "upi://pay? Pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR"
вывод pn -> Prakash"какой-то другой символ" мар
3 ответа
Возможно, это не тот ответ, который вам нужен, но это может помочь:
public class Test {
public static void main(String... a) {
try {
//
String u = "upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR";
System.out.println(decode(u));
} catch (Exception e) {
e.printStackTrace();
}
}
private static String decode(String in) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
if (c == '%') {
int decoded = Integer.parseInt(in.substring(i + 1, i + 3), 16);
if (decoded >= 32 && decoded <= 126) { //Possible valid char
sb.append((char) decoded);
i += 2;
} else { //not a valid char... maybe a space
sb.append(" ");
}
} else if (c == '+') {
sb.append(" ");
} else {
sb.append(c);
}
}
return sb.toString();
}
}
Возможностей много, поэтому, вероятно, вам понадобится "нестандартное" решение. Приведенный выше код охватывает некоторые случаи.
Интересная проблема. Вы не можете заменить %
в космос надежно, как вы уже видели себя. Вам нужна дополнительная информация о том, что будет транспортироваться через URI, а затем сузить, что нужно заменить, а что нет, например,
%ZTest -> a space for sure
%Abababtest -> is it a space? probably... but we need to be sure that no strange characters or sequences are allowed
%23th%Affleck%20Street -> space? hex? what is what?
Вам нужно больше информации для надежного решения этой проблемы, например:
- какие разрешенные символы? или какие допустимые шестнадцатеричные диапазоны должны быть декодированы?
- какие параметры запроса должны содержать
%
как пробелы? (так что вы можете преобразовать только их) - Вам нужно расшифровать кириллицу, арабский, китайский иероглифы?
- если
%20
находится в URI, можем ли мы предположить, что нет%
будет пространство тогда? или возможно, что оба появятся как пространство в URI?
С этой дополнительной информацией должно быть легче решить проблему.
Тем не менее, вот решение, которое может привести вас в правильном направлении (но, пожалуйста, обратите внимание на предупреждения внизу!):
Pattern HEX_PATTERN = Pattern.compile("(?i)%([A-F0-9]{2})?");
String CHARSET = "utf-8";
String ENCODED_SPACE = "%20";
String ALLOWED_SYMBOLS = "\\p{L}|\\s|@";
String semiDecode(String uri) throws UnsupportedEncodingException {
Matcher m = HEX_PATTERN.matcher(uri);
StringBuffer semiDecoded = new StringBuffer();
while (m.find()) {
String match = m.group();
String hexString = m.group(1);
String replacementString = match;
if (hexString == null) {
replacementString = ENCODED_SPACE;
} else {
// alternatively to the following just check whether the hex value is in an allowed range...
// you may want to lookup https://en.wikipedia.org/wiki/List_of_Unicode_characters for this
String decodedSymbol = URLDecoder.decode(match, CHARSET);
if (!decodedSymbol.matches(ALLOWED_SYMBOLS)) {
replacementString = ENCODED_SPACE + hexString;
}
}
m.appendReplacement(semiDecoded, replacementString);
}
m.appendTail(semiDecoded);
return semiDecoded.toString();
}
Пример использования:
String uri = "upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR";
String semiDecoded = semiDecode(uri);
System.out.println("Input: " + uri);
System.out.println("Semi-decoded: " + semiDecoded);
System.out.println("Completely decoded query: " + new URI(semiDecoded).getQuery());
который напечатает:
Input: upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR
Semi-decoded: upi://pay?pa=praksh%40kmbl&pn=Prakash%20Abmar&cu=INR
Completely decoded query: pa=praksh@kmbl&pn=Prakash Abmar&cu=INR
Предупреждения... некоторые вещи, которые нужно иметь в виду:
- эта конкретная реализация не работает с кириллицей, китайскими или другими буквами, которые принимают более 2 шестнадцатеричных значений (т.е.
%##%##
или же%##%##%##
для отдельных символов больше не будет декодироваться) - вам нужно адаптировать разрешенные символы к вашим потребностям (см. регулярное выражение
ALLOWED_SYMBOLS
; на данный момент он принимает любое письмо, любые пробелы и@
) - charset utf-8 предполагалось
Решение, которое я использовал для этого, состоит в том, чтобы не использовать имя получателя, указанное в QR, и запросить PSP с помощью vpa, чтобы получить правильное имя. Таким образом, вы также убедитесь, что получатель платежа существует.
например:
- данный QR имеет URI как
upi://pay?pa=someone@upi&pn=firstname%lastname&cu=INR
- извлечь па, который является
someone@upi
и использовать его, чтобы получить имя пользователя из PSP - как что-либо кроме имени и примечания не может иметь
%
или же%20
в нем просто используйте любой из обходных путей, представленных в других ответах, или используйте более простые решения для заметок, поскольку заметки обычно менее важны.