您可以使用以下方法编码路径
<?php
$encoded = implode("/", array_map("rawurlencode", explode("/", $path)));
?>
(PHP 4, PHP 5, PHP 7, PHP 8)
rawurlencode — 根据 RFC 3986 进行 URL 编码
string
要编码的URL。
返回一个字符串,其中所有非字母数字字符(除了-_.~
)都已被百分号(%
)后跟两位十六进制数字替换。这是» RFC 3986中描述的编码,用于保护文字字符不被解释为特殊的URL分隔符,并保护URL不被具有字符转换的传输介质(如某些电子邮件系统)破坏。
示例 #1 在 FTP URL 中包含密码
<?php
echo '<a href="ftp://user:', rawurlencode('foo @+%/'),
'@ftp.example.com/x.txt">';
?>
上面的例子将输出
<a href="ftp://user:foo%20%40%2B%25%[email protected]/x.txt">
或者,如果您在URL的PATH_INFO组件中传递信息
示例 #2 rawurlencode() 示例 2
<?php
echo '<a href="http://example.com/department_list_script/',
rawurlencode('sales and marketing/Miami'), '">';
?>
上面的例子将输出
<a href="http://example.com/department_list_script/sales%20and%20marketing%2FMiami">
您可以使用以下方法编码路径
<?php
$encoded = implode("/", array_map("rawurlencode", explode("/", $path)));
?>
我编写了一个简单的函数来将UTF-8字符串转换为URL编码字符串。所有给定的字符都将被转换!
函数
<?php
function mb_rawurlencode($url){
$encoded='';
$length=mb_strlen($url);
for($i=0;$i<$length;$i++){
$encoded.='%'.wordwrap(bin2hex(mb_substr($url,$i,1)),2,'%',true);
}
return $encoded;
}
?>
示例
<?php
echo 'http://example.com/',
mb_rawurlencode('你好');
?>
上面的例子将输出
http://example.com/%e4%bd%a0%e5%a5%bd
rawurlencode() 不能用于未解析的URL。
rawurlencode() 不应用于主机和域名部分(可能包含以“q--”前缀后跟国际域名的特殊编码的国际字符编码的每个域名部分,目前正在测试中)。
rawurlencode() 可以分别用于用户名和密码(这样它就不会编码“:”和“@”分隔符)。
rawurlencode() 不应用于路径(可能包含“/”分隔符):解析的URL的['path']元素必须首先被分解成单独的“目录”名称。包含空格的目录或文件名不应使用urlencode()编码,而应使用rawurlencode()编码,以便它显示为'%20'十六进制序列(而不是'+')。
rawurlencode() 不应用于编码解析的URL的['query']元素。相反,您必须使用urlencode()函数。
典型的查询通常在每个参数之间使用“&”分隔符。然而,这个“&”分隔符只是一个约定,用于使用默认GET方法的HTML表单的www-url-编码格式。但是,当在HTML页面中引用包含静态查询参数的URL时,这些“&”分隔符应该在HTML代码中编码为“&”,以符合HTML规范。这不是URL规范的一部分,而是HTML封装的一部分!有些浏览器忘记了这一点,并使用它们的HTTP GET查询发送“&”。您可能希望在解析和验证URL时将“&”替换为“&”。这应该在对查询部分调用urlencode()之前完成。
解析的URL的['fragment']部分(在任何URL中找到的第一个“#”分隔符之后)不应使用此rawurlencode()函数编码,而应使用urlencode()编码。
验证在HTTP请求中发送的URL比您想象的要复杂得多。这只能在解析的URL(其中URL的基本元素已被拆分)上完成,然后您必须分解路径组件,并检查查询或片段部分中是否存在“&”序列。
接下来要做的是检查您想要支持的URL方案(例如,只有'http'、'https'或'ftp')。
您可能希望检查['port']部分以查看它是否确实是1到65535之间的十进制整数。
您可能希望删除您想要支持的URL方案使用的默认端口号(例如'http'的端口'80'、'ftp'的端口'21'、'https'的端口'443'),并严格限制所有低于1024的端口号,或一些低于140的关键端口(这包括DNS和NetBios端口)。
然后,您可能希望严格控制['host']部分(实际上是完整的域名或IP地址),禁止那些不包含至少一个点的主机名,禁止那些以点开头的主机名,禁止那些包含两个连续点的主机名,禁止那些以'-'破折号开头或结尾的主机名,禁止那些包含'.-'或'-.'(在所有域名中无效)的主机名,禁止那些在域名部分的第二和第三个字符以外的位置包含两个破折号且后面没有至少一个其他字符的主机名,禁止只有单个非数字字符或超过6个字符的顶级域名(".museum"目前是最长的可接受顶级域名),检查纯整数的伪顶级域名是否有效地在0到255之间,在这种情况下,通过将其与long2ip(ip2long($host))进行比较来检查它是否为有效的IPv4地址……
完成此操作后,您必须根据规范对所有分解路径元素之前的部分使用urlencode()函数,对查询和片段部分使用rawurlencode()函数,以重新创建一个完整且有效的URL。
phpversion()>=5.3符合RFC 3986,而phpversion()<=5.2.7RC1不符合RFC 3986。
相关RFC的历史
RFC 1738第2.2节
仅限字母数字字符、特殊字符 "$-_.+!*'(),", 以及
为其保留用途而使用的保留字符可在URL中
不进行编码地使用。
RFC 2396第2.3节
unreserved = alphanum | mark
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
RFC 2732第3节
(3) 将 "[" 和 "]" 添加到“保留”字符集中
RFC 3986第2.3节
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
RFC 3987第2.2节
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
用于清理完整URL的示例函数
<?php
private function sanitizeUrl($url){
$parts = parse_url($url);
// 可选,但我们只清理定义了 scheme 和 host 的 URL
if($parts === false || empty($parts["scheme"]) || empty($parts["host"])){
return $url;
}
$sanitizedPath = null;
if(!empty($parts["path"])){
$pathParts = explode("/", $parts["path"]);
foreach($pathParts as $pathPart){
if(empty($pathPart)) continue;
// 路径部分可能已经进行了URL编码
$sanitizedPath .= "/" . rawurlencode(rawurldecode($pathPart));
}
}
// 构建URL
$targetUrl = $parts["scheme"] . "://" .
((!empty($parts["user"]) && !empty($parts["pass"])) ? $parts["user"] . ":" . $parts["pass"] . "@" : "") .
$parts["host"] .
(!empty($parts["port"]) ? ":" . $parts["port"] : "") .
(!empty($sanitizedPath) ? $sanitizedPath : "") .
(!empty($parts["query"]) ? "?" . $parts["query"] : "") .
(!empty($parts["fragment"]) ? "#" . $parts["fragment"] : "");
return $targetUrl;
}
?>
--- 1) 关于URL中的“保留”字符
请注意,RFC 1738规定字符 "{", "}", "|", "\", "^", "~", "[", "]", 和 "`"都被认为是不安全的,应在*所有*URL中使用 "%xx" 三元组进行URL编码。
但是,一些HTTP URL似乎使用"~"字符作为用户帐户的前缀,例如
http://www.any.host.domain/~user/subpath/page.html?query#fragment
这种用法是可以接受的,但是RFC规定在路径组件中应使用"%7E"代替"~"。HTTP服务器应该接受"~"等同于"%7E",并且根据RFC,"%7E"形式应该是规范的形式。
但是,一些HTTP服务器并不完全符合此RFC,并且将"%7E"与"~"区别对待(即,它们认为它是路径组件名称的一部分,并搜索包含"~"字符的目录名称,而不是将"~user"路径组件映射到用户的目录。在这种情况下,这些不兼容的HTTP服务器将找不到与该URL关联的资源,并可能返回404错误或其他错误,例如访问被拒绝。
在对这样的HTTP URL使用rawurlencode()时,最好考虑这种遗留用法,通过在结果上使用str_replace()将"/%7E"转换回"/~",以便URL能够正确地映射到这些服务器对"~"字符的遗留用法。在兼容的HTTP服务器上,它们将对"~"不安全字符与推荐的"%7E"形式进行等效处理,因此它们将自动将"~"字符规范化为"%7E"。
--- 2) URL中主机名的编码
最后,请注意,URL中的主机域名部分*绝不能*使用rawurlencode()进行编码,因为 "[" 和 "]" 是有效的定界符,*必须*用于引用IPv6地址或其他不符合主机名允许的受限字符集的主机名(如果主机名包含诸如":"之类的字符,通常用于指定备用的非默认端口号,则*必须*使用 "[" 和 "]" 字符)。
主机名的编码使用另一种编码,需要对国际域名进行编码,使用Unicode字符的base-64编码和"bq--"前缀。此编码只能用于单个子域名部分(以"."字符分隔)。此编码不使用任何"%xx"三元组。
因此,*绝不要*在未解析的URL上使用urlencode()或rawurlencode(),除非此完整URL是查询参数字符串的一部分!
--- 3) URL中用户名/密码的编码
没有标准指定URL中的密码。实际上,有一种使用":"字符分隔用户名和密码的遗留用法,但这强烈不建议这样做。RFC没有尝试为URL的身份验证部分(在"@"字符和主机名部分之前)指定语义。
如果需要编码密码,请始终分别对用户名和密码使用rawurlencode(),然后插入":"字符以分隔这两个组件。不要使用urlencode()(它可以使用"+"来编码空格,并且不会工作,因为用户名和密码认为"+"和空格是不同的!)
请注意,RFC 1738已得到修订
"[" 和 "]" 不再被认为是不安全的,而是现在被认为是“保留的”,这意味着它们*可以在*URL中使用!
目前,这种用法仅在主机名部分允许,但有一些建议允许在某些URL方案中使用这种用法。现在发现类似的扩展使用 "{}" 字符作为具有特殊语义的“保留”字符,而不是必须进行URL编码的“不安全”字符……
另请注意,某些字符目前是“保留的”,但应该被认为是“不安全的”:这包括括号"()",当URL用于MIME标头时,它们显然是不安全的。
因此,如果有效的URL包含"()"字符,则应使用更高级别的编码将URL用上层协议中定义的一对“不安全”字符括起来(例如,MIME标头中的一对"<>",因为这些字符不可能是有效URL的一部分)……
关于'[email protected]'程序的说明
在HTML页面中使用charset=utf-8不会解决整个问题吗?
我正在HTML表单和PHP程序之间传递一些数据——我的“特殊”字符与波兰语字母有关——看起来JavaScript编码实际上……有效。
当然,我可能只测试了有限的案例。
只是一个想法。
关于rickyale和djmaze的评论……
您尝试实现的不是utf8和url编码的组合吗?例如:
<?
$str = "bl?f Charl?ne";
$enc = urlencode(utf8_encode($str));
$str2 = utf8_decode(urldecode($enc));
echo "$str -> $enc -> $str2";
?>
将输出
bl?f Charl?ne -> bl%C3%B8f+Charl%C3%A8ne -> bl?f Charl?ne
至少对我有效,Jeroen Hofstee
Microsoft URLEncode方法忽略了RFC1738中的文档,该文档指出
"....特殊字符 "$-_.+!*'(),", 和为其保留用途而使用的保留字符可在URL中不进行编码地使用"
例如,[email protected]变为myaddress%40mydomain%2Ecom,而php和其他语言将其编码为myaddress%40mydomain.com
在从asp移植时或在不同平台上进行URL编码的字符串的字符串比较时,这可能是一个问题。
注意:php将正确地将myaddress%40mydomain%2Ecom解码为[email protected],只有编码不同
请注意,如果您像这样以HTTP方式实现自己的服务器请求引擎
GET $request_uri
您应该首先拆分$request_uri路径的所有部分并对每个部分进行rawurlencode(),然后将这些部分再次连接起来。此函数将正确转换URI
function translate_uri($uri) {
$parts = explode('/', $uri);
for ($i = 0; $i < count($parts); $i++) {
$parts[$i] = rawurlencode($parts[$i]);
}
return implode('/', $parts);
}
因为如果您对整个URI使用rawurlencode(),路径分隔符'/'也会被编码,请求将不会正确。'/'字符不应该被编码,只有它们之间的部分。
希望这对像我这样的人有所帮助……
URL/URI编码是一个非常复杂的问题。
例如
'http://example.org:port/path1/path2/data?key1=value1&argument#fragment' (1),或者
'scheme://user:[email protected]:port/path1/path2/data?key1=value1&key2=value2#fragment' (2)
例如,(2)应该编码为
'scheme://'.rawurlencode('user').':'.rawurlencode('password').'@example.com:port/'
.rawurlencode('path1').'/'.rawurlencode('path2').'/'.rawurlencode('data')
.'?'.htmlentities(urlencode('key1').'='.urlencode('value1').'&'.urlencode('key2').'='.urlencode('value2'))
.'#'.urlencode('fragment') 等。
为了方便编码,我编写了'toURI'函数,参见 https://gist.github.com/msegu/bf7160257037ec3e301e7e9c8b05b00a
URI 结构如下:[scheme:][//authority][path][?query][#fragment]
意思是 [scheme:][//[user[:password]@]host[:port]][/path][?query][#fragment]
或者:scheme:[user@host][?query] (mailto: 等)
toURI() 函数简要说明
fragment => urlencode (例如,空格转换为 '+')
query,例如 'key1=value1&key2' => 每个键和值:urlencode(如果 $type<0 则使用 rawurlencode)
然后整个 query => htmlentities
path,例如 dir/dir/file => 每个目录和文件:rawurlencode (例如,空格转换为 %20)
user:password => user 和 password 分别:rawurlencode
(参见 2002-09-13 的匿名注释!)
toURI() 使用示例
<?php
// 简单用法,查询参数/值中没有特殊字符
echo toURI('key1=value1&key2=value 2&argument1 argument2#fragment');
//'key1=value1&key2=value+2&argument1+argument2#fragment' - 正确
echo toURI('?key1=value 1&argu+ments#frag');
//'?key1=value+1&argu%2Bments#frag' - 正确
echo toURI('../path 1/path 2/file name');
//'../path%201/path%202/file%20name' - 正确
echo toURI('example.com/path1/path2/data?key1=value1&key2=value2#fragment', 1);
//'example.com/path1/path2/data?key1=value1&key2=value2#fragment' - 正确;1 比自动检测更好
echo toURI('http://user:_pass [email protected]:123/path 1/data?key1=value 1&key2=value2#fragment'); // 包含用户名、密码或未知查询参数时,使用 $spec_replace - 见下文
echo toURI('path 1/path 2/da ta?key1=value 1&argu+ments#frag', 5);
//'path 1/path%202/da%20ta?key1=value+1&argu%2Bments#frag' - 错误,应该是 4:
echo toURI('path 1/path 2/da ta?key1=value 1&argu+ments#frag', 4);
//'path%201/path%202/da%20ta?key1=value+1&argu%2Bments#frag' - 正确
echo toURI('example.com:port/path1/path2/data?key=value&path=dir 1/dir 2/file#fragment', 5);
//'example.com:port/path1/path2/data?key=value&path=dir+1/dir+2/file#fragment' - 正确
echo toURI('path1/path2/data?key1=valueWith~!@?/#$%^&*()inside&arg#frag', 2);
//'path1/path2/data?key1=valueWith%7E%21@?/%23%24%25%5E&%2A%28%29inside&arg#frag' - 错误(第一个 &),使用 $spec_replace - 查看我的 github 上更完整的示例 https://gist.github.com/msegu/bf7160257037ec3e301e7e9c8b05b00a
?>
这里需要注意。rawurlencode 将 ä 转换为 %C3%83%C2%A4,但 Firefox 会将其内部转换为 %c3%83%c2%a4。这可能会导致重写循环出现错误。
谢谢。
对于那些希望根据 RFC 3986 从 URL 中去除所有非保留字符的人,代码如下所示:
<?php
$stripped = preg_replace('/[^[:alnum:]-._~]/', '', $source);
?>
为了使此字符串能够在 URL 中正确使用,您可能仍然需要使用 rawurlencode,因为 [:alpha:] posix 方括号表达式会捕获带重音字符 - 如果您只想包含 ASCII 字符,请改用 [A-Za-z][0-9]。
因此,一个基本的“slug”生成例程可能如下所示:
<?php
function strtoslug($string) {
// 去除 RFC:3986 中除保留字符外的所有字符
$stripped = preg_replace('/[^[:alnum:][:blank:]-._~]/', '', $string);
// 将压缩的空格转换为连字符
$slug = preg_replace('/[:blank:]+/', '-', $stripped);
return $slug;
}
?>
关于 URL 中的“;”保留字符
rawurlencode() 将其编码为 "%2A" 三元组。当用于 URL 的路径部分时,这将破坏 URL RFC 中定义的用法,该用法允许为路径的*每个*元素(用“/”分隔)指定附加参数。
因此,如果路径元素包含“;”字符(某些文件系统允许它,但不推荐),则必须对其进行编码,以避免将其与参数扩展混淆。
此映射允许用于使用分层方案(HTTP、HTTPS、FTP、FILE 等)的 URL,以便每个以“/”为前缀的路径元素都可以具有附加的导航参数,例如授权字符串或语义参数。
路径元素的通用格式可能包括以下路径元素:
"/." 或 "/.." 或 "/.specialname" 或 "/regularname"
每个部分后面都可以跟一个“;”和其他用“;”分隔的参数。这些参数可以是有序的或无序的。无序参数具有一个符号名称,该名称与其值用等号分隔。
不要将路径元素参数与查询字符串混合:这些参数直接附加到各个路径元素,当此路径元素不是 URL 的最后一个元素时,这会有所不同。这些参数是资源名称的一部分(与查询字符串不同),并且 "." 和 ".." 的语义适用于包含其参数的完整路径元素,因此
"/subdir1/subdir2/page.html;charset=UTF-8/../index.html"
将解析为 "/subdir1/index.html"。
请注意
"/subdir1/subdir2/page.html;charset=UTF-8"
指定与
"/subdir1/subdir2/page.html"
不同的资源名称。它不一定包含查询,因此可以默认缓存(与包含查询字符串的 URL 不同)。
使用路径元素参数时,必须先分别对它们的可选名称和必需值进行 rawurlencode() 处理,然后再插入“;”和“=”参数,并创建将在完整路径中合并的路径元素。
结果是,您*必须*先不 urlencode() 或 rawurlencode 个别路径元素,然后再解析它们:
- 首先将路径分解为用“/”分隔的路径元素。
- 然后将每个路径元素分解为用“;”字符分隔的名称和参数。
- 然后将包含“=”号的路径元素参数拆分为名称/值对。
- 确保在每个路径元素中,无序路径参数(已根据“=”拆分为一对)位于有序参数(包括主路径元素名称)之后,并且没有两个无序参数具有相同的名称(此限制不适用于仅提供值的无序、未命名参数)。
- 最后,您可以解释构成每个路径元素的 rawurlencoded 名称和值。
另请注意,某些不兼容的 HTTP 服务器认为命名参数是有序的,并且不会向用于分解路径元素参数列表的“;”和“=”添加语义。在客户端代理上,验证 URL 时,最好不要尝试解释此列表,而应该只通过隔离引入此列表的第一个“;”来拆分路径元素的主部分和参数列表。但是,编码的参数列表不能包含任何“/”参数。
警告:请注意,路径元素参数(由“;”引入)可用于分层 URL 的上层,甚至在最终文档名称及其查询参数之前。构建 URL 列表时,不应使用“;”分隔符盲目分隔 URL,因为每个 URL 的路径部分都可能包含“;”字符(“;”字符不能安全地出现在查询字符串中)。在这种情况下,请使用例如“<>”或引号之类的包围对将每个 URL 括起来。
如果您像我一样,有时不幸地被迫使用 PHP4,这里有一个 PHP 实现的 http_build_query(),它产生的输出与该函数大致相同,并接受相同的参数。
这里唯一的区别是 RFC 选择器参数的行为并不完全正确。此实现通过 urlencode() 传递 RFC1738,通过 rawurlencode() 传递 RFC3986,这并不完全正确,有关更多信息,请参见这些函数的手册页。
<?php
if (!function_exists('http_build_query')) {
if (!defined('PHP_QUERY_RFC1738')) define('PHP_QUERY_RFC1738', 1);
if (!defined('PHP_QUERY_RFC3986')) define('PHP_QUERY_RFC3986', 2);
function http_build_query ($query_data, $numeric_prefix = NULL, $arg_separator = NULL, $enc_type = PHP_QUERY_RFC1738, $base = NULL) {
$result = array();
$arg_separator = ($arg_separator != '') ? (string) $arg_separator : ini_get('arg_separator.output');
$enc_func = ($enc_type == PHP_QUERY_RFC3986) ? 'rawurlencode' : 'urlencode';
foreach ($query_data as $key => $item) $result[] = (is_array($item) || is_object($item)) ? http_build_query($item, NULL, $arg_separator, $enc_type, ($base !== NULL) ? "$base%5B".$enc_func($key).'%5D' : $enc_func($key)) : (($base !== NULL) ? "$base%5B".$enc_func($key).'%5D='.$enc_func($item) : ((is_int($key) && $numeric_prefix !== NULL) ? (string) $numeric_prefix : '').$enc_func($key).'='.$enc_func($item));
return implode($arg_separator, $result);
}
}
PHP 的函数 `rawurlencode()` 和 `urlencode()` 都会对整个参数字符串进行编码,导致结果无法作为有效的链接使用。
此处列出的函数可以将链接字符串(例如 http://www.domain.com/long_path/to\file.php?query=param#fragm)编码为有效的 <a href=""> 参数字符串,同时保留原始 URI 结构和给定的路径。
function linkencode ($p_url) {
$ta = parse_url($p_url);
if (!empty($ta[scheme])) { $ta[scheme].='://'; }
if (!empty($ta[pass]) and !empty($ta[user])) {
$ta[user].=':';
$ta[pass]=rawurlencode($ta[pass]).'@';
} elseif (!empty($ta[user])) {
$ta[user].='@';
}
if (!empty($ta[port]) and !empty($ta[host])) {
$ta[host]=''.$ta[host].':';
} elseif (!empty($ta[host])) {
$ta[host]=$ta[host];
}
if (!empty($ta[path])) {
$tu='';
$tok=strtok($ta[path], "\\/");
while (strlen($tok)) {
$tu.=rawurlencode($tok).'/';
$tok=strtok("\\/");
}
$ta[path]='/'.trim($tu, '/');
}
if (!empty($ta[query])) { $ta[query]='?'.$ta[query]; }
if (!empty($ta[fragment])) { $ta[fragment]='#'.$ta[fragment]; }
return implode('', array($ta[scheme], $ta[user], $ta[pass], $ta[host], $ta[port], $ta[path], $ta[query], $ta[fragment]));
}
我已经尝试将之前的评论以及一些错误修复合并到 dphantom 的 linkencode 函数中。对于这些测试用例,我没有发现任何错误。
http://example.com/path1;var1=val1/p2;v2
http://example.com/p1;v1/p2;v2
http://[ip:v6:440]:8080
http://example.com:8080
http://example.com/~joe
http://example.com/foobar/~joe
http://username:password@hostname/path 1//path 2/?arg 1=value 1&arg 2=value 2#fragment identifier
hostname/path 1//path 2/?arg 1=value 1&arg 2=value 2#fragment identifier
http://invalid_host..name/
function linkencode($p_url){
$uparts = @parse_url($p_url);
$scheme = array_key_exists('scheme',$uparts) ? $uparts['scheme'] : "";
$pass = array_key_exists('pass',$uparts) ? $uparts['pass'] : "";
$user = array_key_exists('user',$uparts) ? $uparts['user'] : "";
$port = array_key_exists('port',$uparts) ? $uparts['port'] : "";
$host = array_key_exists('host',$uparts) ? $uparts['host'] : "";
$path = array_key_exists('path',$uparts) ? $uparts['path'] : "";
$query = array_key_exists('query',$uparts) ? $uparts['query'] : "";
$fragment = array_key_exists('fragment',$uparts) ? $uparts['fragment'] : "";
if(!empty($scheme))
$scheme .= '://';
if(!empty($pass) && !empty($user)) {
$user = rawurlencode($user).':';
$pass = rawurlencode($pass).'@';
} elseif(!empty($user))
$user .= '@';
if(!empty($port) && !empty($host))
$host = ''.$host.':';
elseif(!empty($host))
$host=$host;
if(!empty($path)){
$arr = preg_split("/([\/;=])/", $path, -1, PREG_SPLIT_DELIM_CAPTURE); // 需要 PHP > 4.0.5。
$path = "";
foreach($arr as $var){
switch($var){
case "/"
case ";"
case "="
$path .= $var;
break;
default
$path .= rawurlencode($var);
}
}
// 针对需要字面量 /~username 的服务器的遗留补丁
$path = str_replace("/%7E","/~",$path);
}
if(!empty($query)){
$arr = preg_split("/([&=])/", $query, -1, PREG_SPLIT_DELIM_CAPTURE); // 需要 PHP > 4.0.5。
$query = "?";
foreach($arr as $var){
if( "&" == $var || "=" == $var )
$query .= $var;
else
$query .= urlencode($var);
}
}
if(!empty($fragment))
$fragment = '#'.urlencode($fragment);
return implode('', array($scheme, $user, $pass, $host, $port, $path, $query, $fragment));
}
在我的 Apache 2 / Windows NT 机器上,包含 umlauts 的本地 Windows 路径让我遇到了严重的问题。如果我只使用 rawurlencode,Apache 就找不到任何这些文件。这里没有提到,但是你可以通过简单地先将你的路径转换为 utf8 来解决这个问题。
rawurlencode(utf8_encode($str));
针对 rickyale at ig dot com dot br 的示例,一个更简单的版本
<?php
function encode($text)
{
$REQUEST_URI = str_replace('"', '%22', $text);
// 0 - 128
return preg_replace('#([\x3C\x3E])#e', '"%".bin2hex(\'\\1\')', $text);
}
?>
只需用你需要编码的所有字符填充正则表达式即可。
注意:在他数组中的 142 及以上是特定语言的 ASCII 字符,因此将其转换为等效的 Unicode ('%C5%BD') 可能有效也可能无效。
这需要一个更强大和更完善的系统来处理非美国字符表。
除了我之前的帖子,我还想补充一点,这个函数是用于“目录/somefile.ext”路径的。
为了构造有效的 ftp url(包含密码),
请执行以下操作
$valid_path = "ftp://" . rawurlencode($user) . ":" . rawurlencode($pass) . ftp_url_encode($your_server_path_to_file)
最后一个函数将对路径 URL 进行编码,以便语言字符保持不变,并且在下载对话框出现后,您可以获得相同的下载文件名。
<?php
/*
:: 使用 rawurldecode() 处理拉丁字符问题 ::
------------------------------------------
如果你需要使用 rawurldecode() 将 %C3%B1 转换为 'ñ' 会发生什么?它并不像我们期望的那样工作。我们会得到 "ñ"。为了解决这个问题,我编写了以下函数:
*/
function urlRawDecode($raw_url_encoded)
{
# 十六进制转换表
$hex_table = array(
0 => 0x00,
1 => 0x01,
2 => 0x02,
3 => 0x03,
4 => 0x04,
5 => 0x05,
6 => 0x06,
7 => 0x07,
8 => 0x08,
9 => 0x09,
"A"=> 0x0a,
"B"=> 0x0b,
"C"=> 0x0c,
"D"=> 0x0d,
"E"=> 0x0e,
"F"=> 0x0f
);
# 查找类似 %C3%[A-Z0-9]{2} 这种模式的拉丁字符,例如:-> %C3%B1 = 'ñ'
if(preg_match_all("/\%C3\%([A-Z0-9]{2})/i",$raw_url_encoded,$res))
{
$res = array_unique($res = $res[1]);
$arr_unicoded = array();
foreach($res as $key => $value){
$arr_unicoded[] = chr(
(0xc0 | ($hex_table[substr($value,0,1)]<<4)) | (0x03 & $hex_table[substr($value,1,1)])
);
$res[$key] = "%C3%" . $value;
}
$raw_url_encoded = str_replace($res,$arr_unicoded,$raw_url_encoded);
}
# 返回原始 URL 解码结果
return rawurldecode($raw_url_encoded);
}
# 测试
print "解码后的字符 -> " . urlRawDecode("%C3%B1");
// 输出:
// 解码后的字符 -> ñ
/*
:: 关于此函数功能的简要说明 ::
-----------------------------------------------------
此函数对 C3 和 B1 进行两次二进制运算。为了获得这种 raw url 编码字符的 ASCII 表示,我们必须在 0xC3 的高位字节 (0xC) 和 0xB1 的高位字节 (0xB) 之间进行逻辑或运算 -> (0xC0 | 0xB0),然后,对两个低位字节进行逻辑与运算 (0x03 & 0x01),最后,我们必须在这两个结果之间进行逻辑或运算 -> [十六进制] ((0xC0 | 0xB0) | (0x03 & 0x01)) = [二进制] ((1100 0000 | 1011 0000) | (0000 0011 & 0000 0001)) = [十六进制] 0xF1 = [二进制] 1111 0001 = "ñ" 字符。
希望对您有所帮助,如果您遇到类似问题,请尝试使用此函数。
再见,
Javi =)
*/
?>
这似乎是您可以为用户提供的正确 FTP URL 编码方式
function ftp_url_encode($string) {
$hex="";
$retstr = "";
for ($i=0; $i < strlen($string) ;$i++) {
$char = $string[$i];
if(($char >= '0' && $char <= '9') || ($char >= 'A' && $char <= 'Z') || ($char >= 'a' && $char <= 'z') || $char == '.' || $char == '-' || $char == '/' || (ord($char) >=128) ) $retstr .= $char;
else
$retstr .= "%".strtoupper(dechex(ord($string[$i])));
}
return $retstr;
}
浏览器会损坏某些语言字符
我必须提到Javier的帖子中的一些内容:您遇到的问题只发生在您使用ISO-8859-1(又名ISO-LATIN-1)编码时,它是在ASCII的基础上扩展的,使用128-255的值表示拉丁语系字符(这些字符不是ASCII的一部分)。说0xF1是“ñ”在ASCII中的正确值是错误的:任何等于或大于0x80的值在ASCII中都是无效的;并且在ASCII中没有“ñ”的“正确”值,因为ASCII字符集不包含该字符。
这些编码/解码函数旨在用于UTF-8,它是一种与ASCII兼容的Unicode编码,因此能够表示整个Unicode字符范围。
要点是:“ñ”是您得到的0xC3 0xB1序列,解释为两个单字节ISO-8859-1字符;但是,如果您将它们解释为UTF-8,它们确实代表“ñ”。如果您使用拉丁字符集和编码,那么您的方法就足够了(它本质上是一个utf-8 => iso-latin-1转换器)。
对于任何使用UTF-8编码的人,请在使用像Javier这样的方法之前检查是否存在任何问题:这些多字节值实际上是表示UTF-8中任何非ASCII字符的正确方法。
有关UTF-8和ISO-8859-1编码的更详细信息,请查看维基百科
http://en.wikipedia.org/wiki/UTF-8
http://en.wikipedia.org/wiki/ISO-8859-1
正如peter@nospam所说,微软在发送数据时使用不同的字符串编码表…
通过一些测试,我创建了一个表格,其中包含像? ? ? ? ?这样的特殊字符的编码。
这是给那些需要知道这个表格是什么的人。
数组的索引是字符的ord()值。
使用chr(index)来了解字符,并用值替换。
var $ENCODE_TABLE = ARRAY(33=>'%21', 35=>'%23', 36=>'%24', 37=>'%25', 38=>'%26', 40=>'%28', 41=>'%29', 43=>'%2B', 44=>'%2C', 47=>'%2F', 58=>'%3A', 59=>'%3B', 60=>'%3C', 61=>'%3D', 62=>'%3E', 63=>'%3F', 91=>'%5B', 92=>'%5C', 93=>'%5D', 123=>'%7B', 124=>'%7C', 125=>'%7D', 142=>'%C5%BD', 192=>'%C3%80', 193=>'%C3%81', 194=>'%C3%82', 195=>'%C3%83', 196=>'%C3%84', 197=>'%C3%85', 199=>'%C3%87', 200=>'%C3%88', 201=>'%C3%89', 202=>'%C3%8A', 203=>'%C3%8B', 204=>'%C3%8C', 205=>'%C3%8D', 206=>'%C3%8E', 207=>'%C3%8F', 210=>'%C3%92', 211=>'%C3%93', 212=>'%C3%94', 213=>'%C3%95', 214=>'%C3%96', 217=>'%C3%99', 218=>'%C3%9A', 219=>'%C3%9B', 220=>'%C3%9C', 221=>'%C3%9D', 224=>'%C3%A0', 225=>'%C3%A1', 226=>'%C3%A2', 227=>'%C3%A3', 228=>'%C3%A4', 229=>'%C3%A5', 231=>'%C3%A7', 232=>'%C3%A8', 233=>'%C3%A9', 234=>'%C3%AA', 235=>'%C3%AB', 236=>'%C3%AC', 237=>'%C3%AD', 238=>'%C3%AE', 239=>'%C3%AF', 242=>'%C3%B2', 243=>'%C3%B3', 244=>'%C3%B4', 245=>'%C3%B5', 246=>'%C3%B6', 249=>'%C3%B9', 250=>'%C3%BA', 251=>'%C3%BB', 252=>'%C3%BC', 253=>'%C3%BD', 255=>'%C3%BF');
示例:
function encode($text) {
while(list($ord, $enc) = each($ENCODE_TABLE)) {
$text = str_replace(chr($ord), $enc, $text);
}
return $text;
}
希望这有帮助…