Java - 将字符串转换为有效的URI对象

74
我正在尝试从一个字符串中获取java.net.URI对象。该字符串包含一些字符,需要替换为它们的百分比转义序列。但是当我使用URLEncoder以UTF-8编码对字符串进行编码时,甚至/也会被替换为它们的转义序列。
我如何从String对象中获得有效的编码URL? http://www.google.com?q=a b给出的结果是http%3A%2F%2www.google.com...,而我想要的输出结果是http://www.google.com?q=a%20b 请问有人能告诉我如何实现这一点。
我正在尝试在Android应用程序中完成此操作。因此,我只有访问有限数量的库。
11个回答

58
你可以尝试使用Apache commons-httpclient项目中的org.apache.commons.httpclient.util.URIUtil.encodeQuery。就像这样(请参阅URIUtil):
URIUtil.encodeQuery("http://www.google.com?q=a b")

将变成:

http://www.google.com?q=a%20b

你当然可以自己做,但URI解析可能会变得非常混乱...


谢谢Hans。我正在尝试在Android应用程序中完成这个任务。因此,我只能使用有限数量的库。你还有其他建议吗?再次感谢。 - lostInTransit
2
也许您可以查看URIUtil类的源代码(毕竟它是开源的)。我认为从那个类中提取必要的代码是可能的。 - Hans Doggen
7
指定项目(Apache commons-httpclient)“已经终止生命周期”。它已经在某种程度上被HttpComponents-httpclient所取代,但我无法在新API中找到一个等效的方法。 - dgiugg
2
我同意dgiugg的观点。这个答案已经过时了。 - Sarp Kaya
1
似乎对于较新版本的apache提交-httpclient,它并不存在。 - Daniel
https://dev59.com/zHE85IYBdhLWcg3wzWzM - Daniel

45

4
非常感谢!有时候找一个简单的Java函数会花费很长时间,真是荒谬! - Abdo
1
不幸的是,当尝试编码斜杠(“/”)时,encode()方法是垃圾。我只使用了一个普通的String.replace()来完成任务。那太糟糕了… searchQuery.replace("/", "%2f"); - Bogdan Zurac

34

我在这里补充一条针对Android用户的建议。你可以使用以下方法,避免需要获取任何外部库。此外,在某些答案中提出的所有搜索/替换字符解决方案都是危险的,应该避免。

试一下这个方法:

String urlStr = "http://abc.dev.domain.com/0007AC/ads/800x480 15sec h.264.mp4";
URL url = new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
url = uri.toURL();

在这个特定的URL中,您可以看到我需要对这些空格进行编码,以便我可以用它来发起请求。

这种方法利用了Android类中可用的几个功能。首先,URL类可以将url分解为其正确的组件,因此您不需要进行任何字符串搜索/替换工作。其次,此方法利用了URI类功能,该功能在通过组件构造URI而不是从单个字符串构造URI时,可以正确地转义组件。

这种方法的美妙之处在于,您可以取任何有效的url字符串并使其正常工作,而不需要自己具备任何特殊的知识。


14

即使这是一个有着已被接受的答案的旧帖子,我也会发布我的替代性答案,因为它对当前问题非常有效,并且似乎没有人提及这种方法。

使用java.net.URI库:

URI uri = URI.create(URLString);

如果你想要与此对应的URL格式化字符串:

String validURLString = uri.toASCIIString();

与许多其他方法(例如 java.net.URLEncoder)不同,此方法仅替换不安全的ASCII字符(如 çé等)。


在上面的示例中,如果URLString是以下String

"http://www.domain.com/façon+word"

生成的validURLString将会是:

"http://www.domain.com/fa%C3%A7on+word"

这是一个格式良好的URL。


1
你的答案正是我在寻找的,由于各种原因我无法提取参数,而这是唯一真正有效的方法。 - Ramin
每个人在处理异常时也应该查看文档 http://developer.android.com/reference/java/net/URI.html#create(java.lang.String) - Junior Mayhé
这似乎无法转换引号?例如 ' " - behelit
1
@behelit 确实如此,我刚刚检查了。然而,'是一个安全字符。但是"会引发异常!在java.net.URL中也是一样的。 - dgiugg

9
如果您不喜欢使用库,那么这个怎么样?
请注意,您不应该在整个 URL 上使用此函数,而是应该在组件上使用此函数……例如只有 "a b" 组件,因为您构建 URL 时计算机无法知道哪些字符应具有特殊意义,哪些字符应具有字面意义。
/** Converts a string into something you can safely insert into a URL. */
public static String encodeURIcomponent(String s)
{
    StringBuilder o = new StringBuilder();
    for (char ch : s.toCharArray()) {
        if (isUnsafe(ch)) {
            o.append('%');
            o.append(toHex(ch / 16));
            o.append(toHex(ch % 16));
        }
        else o.append(ch);
    }
    return o.toString();
}

private static char toHex(int ch)
{
    return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10);
}

private static boolean isUnsafe(char ch)
{
    if (ch > 128 || ch < 0)
        return true;
    return " %$&+,/:;=?@<>#%".indexOf(ch) >= 0;
}

这并不总是有效(至少在某些情况下)。例如,字符'Š'被编码为'%M1',但应该被编码为'%C5%A0'。 - mindas
这对于制表符等字符也不起作用。我建议如果它不匹配[A-Za-z0-9_-.~],则将其更改为不安全。请参见http://en.wikipedia.org/wiki/Percent-encoding。 - Gray

4

你可以使用URI类的多参数构造函数。根据URI javadoc:

多参数构造函数引用所需组件中的非法字符。这些构造函数始终引用百分号(“%”)。保留任何其他字符。

因此,如果您使用

URI uri = new URI("http", "www.google.com?q=a b");

然后你会得到 http:www.google.com?q=a%20b 这个结果不是完全正确的,但它更接近了。

如果你知道你的字符串不包含URL片段(例如 http://example.com/page#anchor),那么你可以使用以下代码来获得你想要的结果:

String s = "http://www.google.com?q=a b";
String[] parts = s.split(":",2);
URI uri = new URI(parts[0], parts[1], null);

为了安全起见,您应该扫描字符串中的#字符,但这将帮助您入门。


4

我曾经在一个项目中遇到过类似的问题,需要从字符串中创建一个URI对象。我也没有找到任何干净的解决方案。以下是我的解决方法:

public static URI encodeURL(String url) throws MalformedURLException, URISyntaxException  
{
    URI uriFormatted = null; 

    URL urlLink = new URL(url);
    uriFormatted = new URI("http", urlLink.getHost(), urlLink.getPath(), urlLink.getQuery(), urlLink.getRef());

    return uriFormatted;
}

你可以使用以下URI构造函数来指定端口(如果需要):
URI uri = new URI(scheme, userInfo, host, port, path, query, fragment);

不处理问号的转换(我尝试使用URL:http://www.google.com/Do you like Spam?,它处理了空格,但没有处理末尾的问号) - kentcdodds
@kentcdodds 这是因为在这种情况下问号是合法的。我相信如果你再加一个问号,它会被转换。 - Sebas

3

嗯,我尝试使用

String converted = URLDecoder.decode("toconvert","UTF-8");

我希望这正是你真正需要的内容?

这就是我一直在寻找的答案,而且不需要依赖外部库。 - Michael Plautz
1
不,这是错误的答案。URLDecoder.decode("to convert","UTF-8") 返回 "to convert",URLDecoder.decode("to%20convert","UTF-8") 返回 "to convert"。因此,这与问题所要求的相反。 - Sarp Kaya

1

或者你可以使用这个类:

http://developer.android.com/reference/java/net/URLEncoder.html

自 API 级别 1 起在 Android 中存在。

然而,令人烦恼的是,它会特殊处理空格(将其替换为 + 而不是 %20)。为了解决这个问题,我们可以使用以下代码片段:

URLEncoder.encode(value, "UTF-8").replace("+", "%20");


1
这将会给出 http://www.google.com?q=a+b 而不是期望的 http://www.google.com?q=a%20b。 - cutts
啊,是的,几周后我自己发现了这个。将修改答案以反映我们最终使用的内容。 - MrCranky
1
该方法现在已被弃用,用户应指定一种编码方式,请参见:http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URLEncoder.html - Aidanc
真的,我错过了那个。回答已经修正。 - MrCranky

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接