使用java中的URLDecoder将%解码到空间?

时间:2017-11-29 19:14:56

标签: java utf-8 urlencode rfc upi

我有一个用例,我必须解码URI的queryParameter并执行操作(超出此问题的范围)。

假设我有一个URI,我必须解码它。现在我知道目前所有 %20 都会转换为 空间 ,同时创建URI space 应由 %20 表示,但可能会出现我可能会使用 空间 。因此,我想将 转换为 空间 ,以保持向后兼容性。最后有一个注释有助于理解这个问题。

我使用replaceall() %尝试了%20,但%20再次成为%2020,还有许多其他例外。

  

这是阅读UPI URI所必需的,根据NPCI的官方文件:

     

注意:考虑到当前的PSP应用程序开发为“%”   作为空格(“”),银行PSP应支持“%”和“%20”,直到   生态系统与修订一致的时间。因此,落后   应确保兼容性。

编辑1 基于pshemo评论 -

我试过了

str.replaceAll("%(?![0-9a-fA-F])","%20")

不满足上述正则表达式的情况是 “upi:// pay?pa = praksh%40kmbl& pn = Prakash%Abmar& cu = INR”

输出是pn - >普拉卡什“其他人物”mar

3 个答案:

答案 0 :(得分:1)

可能不是您想要的答案,但这可能会有所帮助:

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

有很多可能性,因此可能您需要一个“定制”解决方案。上面的代码涵盖了某些情况。

答案 1 :(得分:1)

有趣的问题。如您所见,您不能将%可靠地替换为一个空格。您需要有关将通过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?

您需要更多信息来可靠地解决该问题,例如:

  1. 哪些是允许的符号?还是允许解码的十六进制范围是哪些?
  2. 哪些查询参数包含%作为空格? (因此您可以仅对其进行转换)
  3. 您还需要解码西里尔文,阿拉伯文,汉字吗?
  4. 如果URI中有%20,那么我们可以假设没有%会是空格吗?还是两者都可能在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的正则表达式;现在它可以接受任何字母,任何空格和@
  • 假定字符集为utf-8

答案 2 :(得分:0)

我为此使用的解决方案是不使用QR中提供的收款人名称,并使用vpa查询PSP以获取正确的名称。这样,您还可以确保收款人存在。

例如:

  1. 给定QR的URI为upi://pay?pa=someone@upi&pn=firstname%lastname&cu=INR
  2. 提取someone@upi的pa并使用它从PSP获取用户名
  3. 由于名称和注释以外的任何内容都不能包含%%20, 只需使用其他答案中提供的任何解决方法或使用 笔记的更简单解决方案,因为笔记通常不那么重要。