V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
JustLookBy
V2EX  ›  程序员

字符串怎么承载 int32 信息?

  •  
  •   JustLookBy · 2021-09-16 16:19:12 +08:00 · 3099 次点击
    这是一个创建于 1195 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求:js 传输的 json 字符串里面有坐标数组,坐标数值都小于 4,096 。 尽可能压缩传输体积。

    目前想法是 把 x,y 坐标直接 (x<<16)|y 聚集成一个 int32 值,刚好 4 个字节对应 utf8 4 个字节,然而有很多字符都没对应的,所以无法一一转化.

    用的是 websocket 传输,虽然支持 arrayBuffer 类型,但是我的数据都是字符串格式的,还有其他信息要一起。

    大佬们有什么方案吗?把坐标压缩到字符串里面节省空间

    第 1 条附言  ·  2021-09-16 20:24:37 +08:00
    先采用了 #5 #18 俩位的建议,用 base64 进行传输。

    一开始觉得不行是因为我对`utf8` 的匮乏了解,原来 utf8 并不是固定字节,我以前以为是固定 4 字节。。

    所以用 base64 以后实际上比理想体积增加了不到一倍而已,目前可以接受
    26 条回复    2021-09-17 10:12:23 +08:00
    lasuar
        1
    lasuar  
       2021-09-16 16:23:58 +08:00
    我有个问题,你这个 (x<<16)|y 操作可逆吗? 另外,你要节省就直接传 binary,别整 json 。
    JustLookBy
        2
    JustLookBy  
    OP
       2021-09-16 16:32:40 +08:00
    @lasuar 可逆啊,因为坐标值都小于 4,096

    n = (x<<16)|y ; x=n>>16;y=n & (0xff-1)

    传 binary 是最后方案了
    zjsxwc
        3
    zjsxwc  
       2021-09-16 16:34:08 +08:00
    压缩 json 用这个库不行吗?
    https://github.com/rgcl/jsonpack
    chenluo0429
        4
    chenluo0429  
       2021-09-16 16:35:23 +08:00   ❤️ 1
    比如对于坐标 2423,2342,使用坐标字符串是{"point":"2423,2342"},转换成 int32 是{"point":158796070},数字转成 36 进制存储是{"point":"2mjjty"}。基本上省了个寂寞。
    想要压缩就用 protobuf, flatbuffer, kiwi 这些,不要用 json
    chendy
        5
    chendy  
       2021-09-16 16:36:07 +08:00   ❤️ 3
    必须字符串的话
    用 base64 的码表做 64 进制表示,然后全部拼进一个字符串里,两个字符是一个数字,四个字符是一个坐标,接收方自己解析成坐标数组就行
    chenluo0429
        6
    chenluo0429  
       2021-09-16 16:37:53 +08:00
    一定要用 json,你还不如把字段名精简一下,可能比你折腾数字优化得还多些
    jifengg
        7
    jifengg  
       2021-09-16 16:44:23 +08:00
    不知道你的“字符串里面有坐标数组”具体是什么格式,如果数字多的话,自定义一个二进制格式,传输之前 zip 压缩一下。
    json 是格式统一方便应用解析,要尽可能节省的话用二进制+压缩。压缩会耗 CPU 可以酌情考虑加不加。
    zjsxwc
        8
    zjsxwc  
       2021-09-16 16:46:26 +08:00
    而且 js 里整数存储比较奇葩,与主流语言使用 64 位存整数不同,js 是用 54 位来存储整数的。
    在 js 中当然不存在 int32 了。

    当然和楼主的需求不一样,因为用了 json 文本格式传输,楼主应该是想用字符表示上短一点数字,
    我觉得可以先按楼主的算法两个数字变成一个数字,然后这个数字再用 36 进制表示,然后对 json 文本进行 jsonpack 压缩。


    smartbot
        9
    smartbot  
       2021-09-16 16:46:37 +08:00   ❤️ 1
    protobuf, 应该是一种可选数据格式。
    JustLookBy
        10
    JustLookBy  
    OP
       2021-09-16 16:50:46 +08:00
    @zjsxwc 好,我试试 谢谢

    @chenluo0429 因为坐标数组上千个,所以优化还是比较有用的

    @chendy 四个字符一个坐标的话也就是 16 字节一坐标,空间多了 3 倍,不是很高效
    littlewing
        11
    littlewing  
       2021-09-16 16:52:38 +08:00
    ISO-8859-1
    masterclock
        12
    masterclock  
       2021-09-16 16:54:01 +08:00
    1. 选择 CBOR 格式传输
    2. 如果时大量数据,加压缩
    chendy
        13
    chendy  
       2021-09-16 16:54:13 +08:00
    @JustLookBy 但是相比于直接用数字已经砍掉一半了,还去掉了一堆逗号
    zjsxwc
        14
    zjsxwc  
       2021-09-16 16:57:50 +08:00
    protobuf 应该是最省带宽
    lasuar
        15
    lasuar  
       2021-09-16 17:12:35 +08:00
    @JustLookBy 行,位运算玩的挺溜
    Vegetable
        16
    Vegetable  
       2021-09-16 17:18:29 +08:00
    认为这个没什么意义,哪怕是传 binary,也省不下来多少,更何况你还有别的内容,还不如上一层 gzip 。
    learningman
        17
    learningman  
       2021-09-16 17:18:29 +08:00 via Android
    感觉拿 gzip 压一下就差不多了吧。。。
    2i2Re2PLMaDnghL
        18
    2i2Re2PLMaDnghL  
       2021-09-16 18:48:37 +08:00   ❤️ 1
    我都怀疑你是特地凑好的
    64*64=4096

    如果限定在 ASCII 内,则只有 94 个字符可供表示(从 0x20 到 0x7F 再去掉引号和反斜杠),用 base94 表示的话
    log_94 4096 = 1.83
    仍然是四个字符表示一个坐标,和 base64 没区别

    如果拓宽到 unicode,鉴于 UCS-2 会导致其他部分全部翻倍,得不偿失,所以还是 UTF-8
    这就有个问题,UTF-8 编码效率非常不稳定,甚至可能三字节表示一个码位,看你的分布了

    压缩有压缩的工具。
    2i2Re2PLMaDnghL
        19
    2i2Re2PLMaDnghL  
       2021-09-16 18:59:05 +08:00
    压缩可能比 protobuf 更少。
    比如如果你一千个坐标里面有五百个是同一个坐标,压缩能减很多。
    joesonw
        20
    joesonw  
       2021-09-16 19:18:46 +08:00
    const ab = new ArrayBuffer(4);
    const view = new Uint16Array(ab);
    view[0] = x;
    view[1] = y;
    const socket = new WebSocket("ws://localhost:8080");
    socket.binaryType = "arraybuffer";
    socket.write(ab);
    xiangyuecn
        21
    xiangyuecn  
       2021-09-16 20:05:54 +08:00
    存储前后坐标的差值,大概率能变成 0-9 的一位数,只需一个字符就能存储一个数字,2 个字符存储一个坐标,压缩比目测可以达到 30%

    之前写过一套经纬度坐标的压缩+解压代码,支持 6 位小数精度,150 来行代码,欢迎围观
    https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov/blob/master/assets/geo-echarts.js#L528-L707
    also24
        22
    also24  
       2021-09-16 20:07:16 +08:00
    @learningman #17
    我也觉得,反正都是重复字符,压缩效果应该很不错才对
    elfive
        23
    elfive  
       2021-09-16 20:16:31 +08:00 via iPhone
    数据压缩呀,lzma 压缩,我之前做的嵌入式程序,也是 json,也是传点坐标(两个 float,一般是上万个点),经过 lzma 压缩,能节约大概 80%的数据量。
    JustLookBy
        24
    JustLookBy  
    OP
       2021-09-16 20:21:04 +08:00
    @xiangyuecn
    @elfive 谢谢 我看下
    目前先用 base64,效果还算不错。
    ysc3839
        25
    ysc3839  
       2021-09-16 21:02:05 +08:00 via Android
    允许换掉 JSON 的话改用 Msgpack 吧,和 JSON 兼容。
    SmiteChow
        26
    SmiteChow  
       2021-09-17 10:12:23 +08:00
    msgpack
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3724 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 05:07 · PVG 13:07 · LAX 21:07 · JFK 00:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.