【问题标题】:How to compress/decompress a long query string in PHP?如何在 PHP 中压缩/解压缩长查询字符串?
【发布时间】:2012-10-10 02:39:30
【问题描述】:

我怀疑这是否是加密,但我找不到更好的短语。我需要像这样传递一个长查询字符串:

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

查询字符串不包含敏感信息,所以在这种情况下我并不真正关心安全性。只是……嗯,太长太丑了。是否有一个库函数可以让我将查询字符串编码/加密/压缩成类似于 md5() 的结果(类似于,始终为 32 个字符的字符串),但可以解码/解密/解压缩?

【问题讨论】:

  • 您已经将其命名为:“压缩”可能是更合适的标题;)或者为什么不通过 POST 发送数据?
  • 或者存储在SESSION中...有很多方法,但他想存储在uri中。这不是一个坏主意:P
  • 请注意,由于服务器和浏览器的限制,GET 字符串的大小不得超过 1-2 KB。
  • 是同一台服务器还是其他服务器?
  • @jodeci 如果你给每个查询字符串一个唯一的、随机的标识符,你应该不会有多个实例的问题。

标签: php


【解决方案1】:

您可以尝试结合使用 gzdeflate(原始 deflate 格式)来压缩您的数据,而 base64_encode 则仅使用那些在没有百分比编码的情况下允许使用的字符(另外交换字符 @ 987654325@ 和 / -_):

$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%。但此压缩程序的实际效率取决于您使用的数据。

【讨论】:

  • 100% 完全逐字剪切和粘贴正是我想要的 :)
  • 这太棒了,如上所述,我能够将其复制并粘贴到我的应用程序中。但是,撇号返回为 \'
【解决方案2】:

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

因此,您可以将 ASCII 字符转换为其他内容,然后再将其转换为 ASCII 字符。

但实际上,大多数情况下,您开始使用的 ASCII 字符已经是最佳长度。这里是一个快速测试:

$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

如您所见,原始字符串最短。在编码压缩中,base64 是最短的,因为它使用最大的字母表来表示二进制数据。不过还是比原来的要长。

对于一些非常具体的字符组合和一些非常具体的压缩算法,可以压缩成 ASCII 可表示的数据,它可能会实现一些压缩,但这是相当理论上的。 更新: 实际上,这听起来太消极了。问题是您需要弄清楚压缩是否对您的用例有意义。不同的数据压缩方式不同,不同的编码算法工作方式也不同。此外,较长的字符串可以实现更好的压缩比。可能在某个地方可以实现一些压缩的最佳位置。您需要弄清楚您是否大部分时间都处于最佳状态。

像 md5 这样的东西是不合适的,因为 md5 是一个 散列,这意味着它是不可逆的。您无法从中取回原始值。

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

【讨论】:

  • 实际上,您不能肯定地说任何编码都会导致与原始字符串一样长的字符串。这实际上取决于原始字符串。如果压缩足够好,那么即使是编码版本也可以更短。我用 base64 尝试了我的示例,它比原始字符串短。但在某种程度上你是对的,因为可能大多数时候,编码版本会更长。
  • @Felix 是的,我刚刚在我的答案中添加了同样的想法。这并非不可能,但非常不切实际。
  • @gumbo 是的,但这真的不会做太多。
  • @deceze:它将字符串扩展 20 个字符,使其比 Base 64 编码的字符串长。
  • @gumbo 如果空格只替换为+,则不会。 :)
【解决方案3】:

这对我很有用:

$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

所以字符串越长,压缩效果越好。压缩参数,好像没有任何作用。

【讨论】:

  • 我用数组imploded 测试了urlencode(base64_encode(gzcompress($in))); 到一个字符串。我开始看到对长度超过 80 个字符的字符串进行压缩。例如从 110 到 94。您的 $in/$out 变量在您的答案中没有得到很好的解释。我认为您是在说这些是字符串的输入/输出大小(压缩之前/之后)。这是我的回答,以您的回答为起点:stackoverflow.com/a/20915918/631764
  • 如果您通过重复相同的文本块来创建更长的输入字符串,您将获得非常好的压缩,因为输入中有重复模式(低熵)。如果您以较少的重复(较高的熵)压缩字符串,您将获得较少的压缩。长话短说:您需要确保使用真实(标准)数据进行基准测试。
【解决方案4】:

更新:
gzcompress() 帮不了你。例如,如果您接受 Pekka 的回答:

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

所以这种方式(通过 URL 传递数据)可能不是最好的方式...

如果您不超过字符串的 URL 限制,您为什么要关心 字符串的外观?我认为无论如何它都会自动创建,发送和处理,不是吗?

但是,如果您想将其用作例如电子邮件中的某种确认链接,无论如何您都必须考虑一些简短且易于为用户键入的内容。你可以,例如将所有需要的数据存储在数据库中并创建某种令牌。


也许gzcompress() 可以帮助你。但这会导致出现不允许的字符,因此您也必须使用urlencode()(这会使字符串再次变长且丑陋;))。

【讨论】:

  • 是的。结果需要为urlencode()d,这对于非常小的字符串可能不切实际,但可能适用于较大的字符串。
  • @Pekka:是的,刚刚注意到了。它似乎对短字符串没有真正的影响。
  • Felix 我知道你在 Facebook,关于在 Facebook 中压缩字符串。您知道 Cassandra (db) 中有 UUID,它是一个长文本! Twitter 和 Instagram 也在使用 Cassandra(Cassandra 最初是在 Facebook 开发的),当我查找 Facebook、Twitter 和 Instagram 帖子或用户的 id 时,那不是 UUID,我不确定他们是否使用 UUID(或 timeuuid)在他们的 URL 中,或者他们有一种功能和算法来减少 UUID 的长度。 如果使用 UUID,你知道并且你能说出 Facebook 正在做什么使用短 URL 吗?
【解决方案5】:

基本上,就像他们说的:压缩文本,然后以有用的方式编码发送。 但是

1)常见的压缩方法比文本重因为字典。如果数据始终是已确定数据块的未确定顺序(例如文本中是单词或音节 [3],以及数字和一些符号),您可以始终使用相同的静态字典,并且不要发送它(不要t 将其粘贴到 URL 上)。然后您可以节省字典的空间

1.a)如果您已经发送了语言(或者如果它始终相同),您可以为每个语言生成一个字典。

1.b)利用格式限制。如果您知道它是一个数字,则可以直接对其进行编码(参见 3)。如果您知道这是一个日期,您可以编码为 Unix-time[1](自 1970 年 1 月 1 日以来的秒数),因此“21/05/2013 23:45:18”变为“519C070E”(十六进制);如果是一年中的某个日期,您可以编码为自新年以来的天数,包括 29/02(25/08 为 237)。

1.3) 您知道电子邮件必须遵循某些规则,并且通常来自相同的几台服务器(gmail、yahoo 等)。您可以利用这一点,用您自己的简单方式对其进行压缩方法:

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) 忽略相似文本,只压缩可变文本。喜欢:

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

2.b) 按类型对数据进行编码或压缩(使用 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] 是一个很好的方法,但您可以以牺牲速度为代价挤压更多(因为您使用用户函数而不是编译函数)。

至少使用 Javascript 的函数 encodeURI(),您可以在参数值处使用这 80 个字符中的任何一个而无需修改:

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

所以,我们可以构建一个“Base 80”(d)编码函数。

【讨论】:

    【解决方案6】:

    对于长/非常长的字符串值,您希望使用 POST 方法而不是 GET !

    为了获得良好的编码,您可能想尝试 urlencode()/urldecode()

    或htmlentities()/html_entity_decode()

    还要注意'%2F' 被翻译成浏览器作为'/' 字符(目录分隔符)。如果您只使用 urlencode,您可能想对其进行替换。

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

    【讨论】:

      【解决方案7】:

      这些函数将压缩和解压缩字符串或数组。

      有时您可能想要获取一个数组。

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

      【讨论】:

      • 切记切勿盲目地反序列化数据。这是一个非常常见的安全漏洞。
      【解决方案8】:

      不是真正的答案,而是这里建议的各种方法的比较。

      @Gumbo 和 @deceze 使用答案来比较我在 GET 中使用的相当长的字符串的长度。

      <?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)
      

      base64_encode 与 gzcompress 和 base64_encode 与 gzdeflate(以及一些字符串转换)的结果相当。 gzdeflate 似乎效率略高

      【讨论】:

        【解决方案9】:

        base64_encode 使字符串不可读(当然很容易解码),但将音量提高了 33%。

        urlencode() 将任何不适合 URL 的字符转换为它们的 URL 编码对应字符。如果您的目标是使字符串在 URL 中起作用,那么这可能是适合您的方法。

        如果您正在运行会话,您还可以考虑将查询字符串放入带有随机(小)数的会话变量中,并将该随机数放入 GET 字符串中。当然,此方法的生存时间不会超过当前会话。

        请注意,由于服务器和浏览器的限制,GET 字符串的大小不得超过 1-2 KB。

        【讨论】:

          猜你喜欢
          • 2015-01-05
          • 2013-10-30
          • 1970-01-01
          • 2015-02-06
          • 2013-05-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多