如何在PHP中压缩/解压缩长查询string?

我怀疑这是不是encryption,但我找不到一个更好的词组。 我需要传递一个像这样的长查询string:

http://test.com/test.php?key=[some_very_loooooooooooooooooooooooong_query_string] 

查询string中不包含敏感信息,所以在这种情况下我并不担心安全问题。 只是…太好了,太长了,太难看了。 是否有一个库函数,可以让我编码/encryption/压缩查询string类似于md5()(类似于,总是一个32字符的string)的结果类似的东西,但解码/解密/解压缩?

基本的前提是非常困难的。 在URL中传输任何值意味着您被限制为ASCII字符的一个子集。 使用像gzcompress这样的任何types的压缩都会减小string的大小,但会导致二进制的blob。 该二进制blob不能在URL中传输,因为它会产生无效的字符。 要使用ASCII子集传输该二进制blob,您需要以某种方式对其进行编码并将其转换为ASCII字符。

所以,你可以把ASCII字符转换成其他的东西,然后转换成ASCII字符。

但实际上,大多数情况下,从头开始的ASCII字符已经是最佳长度了。 这里快速testing一下:

 $str = 'Hello I am a very very very very long search string'; echo $str . "\n"; echo base64_encode(gzcompress($str, 9)) . "\n"; echo bin2hex(gzcompress($str, 9)) . "\n"; echo urlencode(gzcompress($str, 9)) . "\n"; Hello I am a very very very very long search string eNrzSM3JyVfwVEjMVUhUKEstqkQncvLz0hWKUxOLkjMUikuKMvPSAc+AEoI= 78daf348cdc9c957f05448cc554854284b2daa442772f2f3d2158a53138b9233148a4b8a32f3d201cf801282 x%DA%F3H%CD%C9%C9W%F0TH%CCUHT%28K-%AAD%27r%F2%F3%D2%15%8AS%13%8B%923%14%8AK%8A2%F3%D2%01%CF%80%12%82 

正如你所看到的,原来的string是最短的。 在编码压缩中,base64是最短的,因为它使用最大的字母表来表示二进制数据。 尽pipe如此,它仍然比原来的要长。

对于一些非常具体的字符组合,可以用一些非常具体的压缩algorithm来压缩成ASCII可表示的数据,这样做可能会实现一些压缩,但这是相当理论化的。 更新:其实,这听起来太消极了。 事情是你需要弄清楚压缩对你的用例是否合理。 不同的数据压缩不同,不同的编码algorithm工作方式不同。 而且,更长的琴弦可以达到更好的压缩比。 可能会有一个甜蜜的地方,可以实现一些压缩。 你需要弄清楚大多数时间你是否在这个甜蜜的地方。

像md5是不适合的,因为md5是一个散列 ,这意味着它是不可逆的。 你不能从它得到原来的价值。

恐怕只能通过POST发送参数,如果它在URL中不起作用的话。

您可以尝试使用gzdeflate (raw deflate格式)的组合来压缩数据,而base64_encode仅使用那些不使用百分比编码的字符(另外交换字符+/ by -_ ):

 $output = rtrim(strtr(base64_encode(gzdeflate($input, 9)), '+/', '-_'), '='); 

而相反的:

 $output = gzinflate(base64_decode(strtr($input, '-_', '+/'))); 

这里是一个例子:

 $input = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; // percent-encoding on plain text var_dump(urlencode($input)); // deflated input $output = rtrim(strtr(base64_encode(gzdeflate($input, 9)), '+/', '-_'), '='); var_dump($output); 

这种情况下的节省大约是23%。 但是这个压缩程序的实际效率取决于你使用的数据。

这对我很好:

 $out = urlencode(base64_encode(gzcompress($in))); 

节省很多。

 $in = 'Hello I am a very very very very long search string' // (51) $out = 64 $in = 500 $out = 328 $in = 1000 $out = 342 $in = 1500 $out = 352 

所以string越长压缩越好。 压缩参数似乎没有任何作用。

更新:
gzcompress()不会帮助你。 例如,如果你拿Pekka的答案:

string长度:640
压缩的string长度:375
url编码的string长度:925
(使用base64_encode,它只有500个字符;))

所以这样(通过URL传递数据)可能不是最好的方法…

如果你没有超过string的URL限制,为什么你关心string是怎么样的? 我假设它被创build,发送和自动处理,不是吗?

但是,如果您想将其用作电子邮件中的某种确认链接,则必须考虑为用户input简短的内容。 您可以将所有需要的数据存储在数据库中,并创build一些令牌。


也许gzcompress()可以帮助你。 但是这会导致不允许的字符,所以你也必须使用urlencode() (这会使得string变长和变丑)。

基本上,就像他们说的那样:压缩文本,并以有用的方式发送它。 但是

1) 由于字典的原因,常见的压缩方法比文本更重。 如果数据总是一个确定的数据块(如文本中的单词或大纲[3],数字和一些符号)的不确定的顺序,你可以使用总是相同的静态字典,不要发送它(唐'把它粘贴到URL上)。 那么你可以保存字典的空间

1.a)如果你已经发送了语言(或者如果它总是一样的话),你可以为每个语言生成一个字典。

1.b)利用格式限制。 如果你知道它是一个数字,你可以直接编码(见3)。 如果你知道它是一个date,你可以编码为Unix时间[1](自01/01/1970秒以来的秒),所以“21/05/2013 23:45:18”变成“519C070E”(hex); 如果是一年中的某个date,那么你可以编码为新年以后的天数,包括29/02(25/08将是237)。

1.3)你知道的电子邮件必须遵循一定的规则,通常是从相同的less数服务器(Gmail,雅虎等)您可以利用它的优点,用自己的简单方法压缩:

 samplemail1@gmail.com,samplemail2@yahoo.com.ar,samplemail3@idontknowyou.com => samplemail1:1,samplemail2:5,samplemail3@idontknowyou:1 

2)如果数据遵循模式 ,则可以使用它来帮助压缩。 例如,如果总是遵循这个模式:

 name=[TEXT 1]&phone=[PHONE]&mail=[MAIL]&desc=[TEXT 2]&create=[DATE 1]&modified=[DATE 2]&first=[NUMBER 1]&last=[NUMBER 2] 

你可以: 2.a)忽略相似的文本,只压缩variables文本。 喜欢:

 [TEXT1]|[PHONE]|[MAIL]|[TEXT 2]|[DATE 1]|[DATE 2]|[NUMBER 1][NUMBER 2] 

2.b)按types编码或压缩数据(使用base64 [2]或类似编码编号)。 像在1)。 这甚至可以抑制分隔符。 喜欢:

 [DATE 1][DATE 2][NUMBER 1][NUMBER 2][PHONE][MAIL]|[TEXT 1]|[TEXT 2] 

3)编码:

3.a)虽然如果我们用HTTP不支持的字符压缩编码,它们会变成更重的字符(比如'año'=>'a%C3%B1o'), 这仍然是有用的 。 也许你想压缩它存储在一个Unicode或二进制数据库,或粘贴在网站(Facebook,Twitter等)。

3.b)虽然Base64 [2]这是一个很好的方法,但是你可以在牺牲速度的情况下挤压更多的东西(因为你使用的是用户函数而不是编译的函数)。

至less使用Javascript的函数encodeURI(),您可以在参数值中使用80个字符中的任何一个,而不会受到任何修改:

 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.:,;+*-_/()$=!@?~' 

因此,我们可以build立一个“基地80”(四)编码function。

不是一个真正的答案,而是比较这里提出的各种方法。

使用@Gumbo和@deceze的答案来获取我在GET中使用的相当长的string的长度比较。

 <?php $test_str="33036,33037,33038,38780,38772,37671,36531,38360,39173,38676,37888,36828,39176,39196,37321,36840,38519,37946,36543,39287,38989,38976,36804,38880,38922,38292,38507,38893,38993,39035,37880,38897,38378,36880,38492,38910,36868,38196,38750,37938,39268,38209,36856,36767,37936,36805,39248,36777,39027,39056,38987,38779,38919,38771,36851,38675,37887,38246,38791,38783,38661,37899,36846,36834,39263,37928,36822,37947,38992,38516,39177,38904,38896,37320,39217,37879,38293,38511,38774,37670,38185,37927,37939,38286,38298,38977,37891,38881,38197,38457,36962,39171,36760,36748,39249,39231,39191,36951,36963,36755,38769,38891,38654,38792,36863,36875,36956,36968,38978,38299,36743,36753,37896,38926,39270,38372,37948,39250,38763,38190,38678,36761,37925,36776,36844,37323,38781,38744,38321,38202,38793,38510,38288,36816,38384,37906,38184,38192,38745,39218,38673,39178,39198,39036,38504,36754,39180,37919,38768,38195,36850,38203,38672,38882,38071,39189,36795,36783,38870,38764,39028,36762,36750,38980,36958,37924,38884,37920,38877,36858,38493,36742,37895,36835,37907,36823,38762,38361,37937,38373,37949,36950,39202,38495,38291,36533,39037,36716,38925,37620,38906,37878,37322,38754,36818,39029,39264,38297,38517,36969,38905,36957,36789,36741,37908,38302,38775,39216,36812,38767,36845,36849,39181,39168,38671,39188,38490,36961,39201,36717,38382,38070,37868,38984,36770,38981,38494,36807,38885,36759,36857,38924,39038,38888,38876,36879,37897,36534,36764,37931,38254,39030,38990,37909,38982,38290,36848,37857,37923,38249,38658,38383,36813,36765,36817,37263,36769,37869,38183,36861,38206,39031,36800,36788,36972,38508,38303,39051,38491,38983,38759,36740,37958,36967,37930,39174,39182,36806,36867,36855,39222,37862,36752,38242,37965,38894,38182,37922,37918,36814,36872,38886,36860,36527,38194,38975,36718,39224,37436,39032"; echo(strlen($test_str)); echo("<br>"); echo(strlen(base64_encode(gzcompress($test_str,9)))); echo("<br>"); echo(strlen(bin2hex(gzcompress($test_str, 9)))); echo("<br>"); echo(strlen(urlencode(gzcompress($test_str, 9)))); echo("<br>"); echo(strlen(rtrim(strtr(base64_encode(gzdeflate($test_str, 9)), '+/', '-_'), '='))); ?> 

结果如下:

 1799 (original length string) 928 (51.58% compression) 1388 1712 918 (51.028% compression) 

使用gzcompress的base64_encode和使用gzdeflate的base64_encode(以及一些string转换)的结果是可比的。 gzdeflate似乎给稍微更好的效率

对于长/很长的string值,您想使用POST方法而不是GET!

对于一个好的编码,你可能想尝试urlencode()/ urldecode()

或者htmlentities()/ html_entity_decode()

还要小心'%2F'被翻译成浏览器的'/'字符(目录分隔符)。 如果你只使用urlencode你可能想要做一个replace它。

我不build议在GET参数上使用gzcompress。

这些函数将压缩和解压缩一个string或一个数组。

有时你可能想要一个数组。

 function _encode_string_array ($stringArray) { $s = strtr(base64_encode(addslashes(gzcompress(serialize($stringArray),9))), '+/=', '-_,'); return $s; } function _decode_string_array ($stringArray) { $s = unserialize(gzuncompress(stripslashes(base64_decode(strtr($stringArray, '-_,', '+/='))))); return $s; } 

base64_encode使string不可读(虽然当然很容易解码),但音量却增加了33%。

urlencode()将任何不适合URL的字符都转换为URL编码的对应字符。 如果您的目标是使string在URL中工作,这可能是您的正确方法。

如果你有一个会话正在运行,你也可以考虑把查询string放入一个带有随机(小)数字的会话variables中,并将该随机数字放入GETstring中。 当然,这个方法不会比当前的会话更长久。

请注意,由于服务器和浏览器的限制,GETstring的大小不能超过1-2千字节。