V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Chaox
V2EX  ›  程序员

诚心请教关于 base64 的问题

  •  
  •   Chaox · 2020-11-03 16:44:15 +08:00 · 4875 次点击
    这是一个创建于 1477 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一直很疑惑 base64 的作用。我们传输文件不管是文本文件还是二进制文件最后都是二进制,文本文件只是对二进制的一种处理而显示出文本。 传输文件时,如果我们是用 ASCII 码传输,传输到对方后,对方也有 ASCII 码解释就可以了。其他编码如 UTF-8 之类也是如此。为什么需要 base64 ?

    在网上看到这么一段话:

    我们知道在计算机中任何数据都是按 ascii 码存储的,而 ascii 码的 128 ~ 255 之间的值是不可见字符。而在网络上交换数据时,比如说从 A 地传到 B 地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个 Base64 编码,统统变成可见字符,这样出错的可能性就大降低了。

    这里为什么不同设备处理方式会有不同呢,传输过程中不都是二进制吗,直接把二进制传给对方不就可以了?

    41 条回复    2020-11-06 10:48:43 +08:00
    ipwx
        1
    ipwx  
       2020-11-03 16:46:38 +08:00
    HTML / HTTP 这一套对不可见字符就不太友善。你可以看看相关标准。
    FutherAll
        2
    FutherAll  
       2020-11-03 16:51:38 +08:00
    直接传二进制没办法保证二进制安全?
    P0P
        3
    P0P  
       2020-11-03 16:53:39 +08:00
    很多古老的协议对二进制传输支持不好,或者很多场景下二进制编码受限,比如 FTP 协议就需要指明是 Text mode 还是 Binary mode
    icyalala
        4
    icyalala  
       2020-11-03 17:06:50 +08:00   ❤️ 8
    如果你按二进制传输,那无所谓内容了。
    但是你要按文本文件传输,那二进制内容就放不进来。

    举个例子,服务端和客户端通信都是用 JSON 格式,JSON 按标准是 UTF-8 Only 的,
    如果服务端想通过 JSON 传递一张 PNG 格式的验证码图片,那这个图片是二进制的就有问题了。
    比如二进制里有 '\0' 字符,而这算作 ASCII 字符串的终止符,显然不行。
    这时只能先 base64 编码一下才能放到 JSON 里。
    julyclyde
        5
    julyclyde  
       2020-11-03 18:59:00 +08:00
    传说古代有些设备是 7bit 的
    不过我没有真见过,也没听说过具体型号
    unixeno
        6
    unixeno  
       2020-11-03 20:29:13 +08:00 via Android   ❤️ 3
    base64 的意义就是全部用可见字符来表示一段二进制
    不是所有地方都是按二进制处理的,比如 url,比如网页
    这种地方你要想包含一段二进制,特别是包含不可见字符和非法字符的二进制,就必须先用可见字符编码
    虽然这个用 16 进制来表示也可以,但是 base64 比 16 进制更省空间
    CEBBCAT
        7
    CEBBCAT  
       2020-11-03 20:32:13 +08:00
    base64 是一种映射,这种映射我不能很好地解释,去看编码过程就明白了,相信楼主八成也明白是怎样变幻的。

    在我看来楼主贴的那段引用有不少错误的地方:

    > 我们知道在计算机中任何数据都是按 ascii 码存储的
    存储的是二进制,ASCII 是一种编码

    > 而 ascii 码的 128 ~ 255 之间的值是不可见字符
    ASCII 只定义了 128 个字符

    后面的就不说了,我也一瓶子不满。关于为什么有时候需要使用 base64 对数据进行编码,可以认为这是对协议的一种妥协吧,比如楼上说的 JSON 或 HTTP,而 TCP 协议由于定义了数据长度,所以可以直接传递二进制数据而不需转义。

    HTTP 的结束符号应该是两个\n 还是什么的,可以查看一下维基百科或者相关标准文件,RFC ISO 之类的。

    JSON 的话,假如你想在一个 k-v 的 v 中存入一个带"的字符串,那么为了避免出现读取错误,你只好使用转义或者 base64 这种方式来 escape
    dddd1919
        8
    dddd1919  
       2020-11-04 00:00:47 +08:00
    如果需要文本保存的场景比如数据库,还是很有用的
    Jirajine
        9
    Jirajine  
       2020-11-04 00:28:21 +08:00 via Android
    base64 类似 hex 的作用,不过更省空间。
    Corua
        10
    Corua  
       2020-11-04 02:16:43 +08:00 via Android
    面向文本与面向字节的区别 @Jirajine #9 4 个字符表示原本 3 字节的数据 更占空间了才是
    Phariel
        11
    Phariel  
       2020-11-04 03:21:15 +08:00
    这样做的好处是 信息在传输过程中不会丢失 不然如果遇到传递环节中节点系统不识别不接受不兼容字符 就可能传递不下去或者替换不兼容字符为其他兼容字符造成信息噪点污染数据
    Mutoo
        12
    Mutoo  
       2020-11-04 07:33:34 +08:00
    举个简单的例子:文本转输协议常常以标准的 c_str 为截体,\0 作为截止符。而二进制数据中很可能存在或多或少的 \0,(还有很多不可见的控制字符),直接放在这样的协议上是传输不了的。base64 解决的就是在文本转输协议上发送二进制数据的问题。
    reus
        13
    reus  
       2020-11-04 07:37:25 +08:00 via Android
    传输当然不都是二进制
    lmmortal
        14
    lmmortal  
       2020-11-04 07:51:55 +08:00
    @Jirajine 为什么说 base64 比 hex 更省空间呢?随手试了下:11111111 用 hex 表示是 FF,转换成 base64 就不止两位了
    dcoder
        15
    dcoder  
       2020-11-04 08:02:37 +08:00
    @Chaox
    跟中间路由设备应该无关...
    应该是端到端的 HTTP encoding/decoding prefer 纯文本, 规定这样更 reliable
    opengps
        16
    opengps  
       2020-11-04 08:15:22 +08:00
    其实都是不同层面的封装,类比想想阿波罗 64K 内存能上天,开发难度有多大,再看看你大的 helloworld 的包
    二进制的确是 object,计算机世界的万物之本,1bit,
    然后,8 个 bit 可一表示 1byte,可以表示数字,字母,这时候 ascii
    然后,2-3 个 byte,就可以上升到各个国家语言字符了,这时候比如是 unicode
    再然后,同样道理用来干别的,比如表示 rgb 的三原色 #001122
    然后的然后,就是你现在看到的了

    未来的安装包会更大,当然存储已经允许,正如三体中描述的:来自 21 世纪的古老病毒
    t6attack
        17
    t6attack  
       2020-11-04 08:21:31 +08:00
    以这个 rsa 加解密的简单演示为例: https://v2ex.com/t/542798#r_6999455
    如果加密后的数据不用 base64 编码的话,是无法直接显示在页面上的,显示了也是乱码。乱码复制粘贴到别处就是另一份数据了,文本格式就没问题。包括 rsa 的密钥对,想写进代码里,就必须用文本格式。
    这就是文本格式的可读性、兼容性。

    所谓的 hex,是 16 进制数据文本化。比如,你用二进制编辑器打开一份数据,肉眼可见的,就是 16 进制数据的文本化表达。HEX 只能用 16 个字符表达数据,的其他文本都浪费了。当然比 base64 浪费空间。
    msg7086
        18
    msg7086  
       2020-11-04 08:43:10 +08:00 via Android
    不是所有的用例都支持二进制。

    比如打印在纸上的时候就不能用二进制,显示在屏幕上也不行。
    Bromine0x23
        19
    Bromine0x23  
       2020-11-04 09:42:00 +08:00   ❤️ 1
    @lmmortal 一字节太短了,HEX 是 4bit 一字符,Base64 是 6bit 一字符,加上 Base64 要进行填充,最后长度会是 4 的倍数
    0xFF(8bit) 的时候就是 2 vs 4,但是到 0xFFFF(16bit) 就是 4 vs 4 一样长了,0xFFFFFF (24bit) 是 6 vs 4 就反超了
    xx6412223
        20
    xx6412223  
       2020-11-04 09:52:53 +08:00
    在网络模型中,一般只有最底端的物理层才以二进制传输,而其他的要读取数据包中地址 /端口 /长度等等
    lmmortal
        21
    lmmortal  
       2020-11-04 10:26:37 +08:00 via iPhone
    @Bromine0x23 不太懂,FFFFFF 这个 24 比特转换成 base64 之后长度更长了,有 8 个(MTEx),按这样算起来 base64 像是 3bit
    aneostart173
        22
    aneostart173  
       2020-11-04 10:33:20 +08:00
    二进制对流传输协议不太友好,因为什么编码都可能用,控制字符出现的概率就高了。文本传输就规定了大家只能用这么些字符,协议就容易定,剩下的字符留给下一层当控制字符用。
    FreeEx
        23
    FreeEx  
       2020-11-04 10:38:12 +08:00
    原因就是有些字符使用 ASCII 无法表示,Base64 就是用来解决这个的。
    通常一个数据对象可以序列化为一个 byte 数组,1byte=8bit,ASCII 的表示范围是从 0000 0000 到 0111 1110,局限在于只能显示 26 个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语。Base64 则是用长度为 64 的索引表来对应 6bit 的 00 0000 到 11 1111,不把 byte 看成一个整体,而是拆开 bit 之后按 6 位一个来表示,bit 总长度不能被 6 整除的时候就在后面补 0,补 6 个 0 的时候后面增加一个=,补两个 0 的时候增加两个=,这样就可以用有限的字符来表示各种各样的复杂数据了。
    GM
        24
    GM  
       2020-11-04 10:47:57 +08:00
    @lmmortal

    你搞错了,你说的 FF 那已经是二进制了,不叫 hex 了。

    11111111 的 hex 是 3131 3131 3131 3131,总共 16 个字节,base64 是 MTExMTExMTEK,13 个字节
    wanguorui123
        25
    wanguorui123  
       2020-11-04 10:55:52 +08:00 via iPhone
    1 、部分二进制数据(加密、压缩、音频、图片、视频等算法处理后的二进制数据)方便存储为文本方便复制粘贴、存储、传输
    2 、在部分协议里为了兼容协议的解析规则逼不得已用 Base64 或 HEX 文本进行特殊符号编码
    SuperMild
        26
    SuperMild  
       2020-11-04 11:03:48 +08:00
    @lmmortal hex 其实是 Base16 的别称,hex 不等同于 16 进制。
    nuk
        27
    nuk  
       2020-11-04 11:26:58 +08:00
    咳,因为 internet 出现前,用的 uucp 网络啊,里面一部分 ASCII 是作为控制字符使用的
    不过没有选 uuencode 大概是因为有空格吧?
    shawndev
        28
    shawndev  
       2020-11-04 11:35:47 +08:00
    base64 不是只有>=128 的字符不可显示。比如 0x00 是终止符也不可显示。
    shawndev
        29
    shawndev  
       2020-11-04 11:36:46 +08:00
    有些场景下限定字符有特殊含义,比如 URL 中 /和=有特殊用途,分别代表 path 和 query,那么编码后的 ascii 如果有这些值就会影响 url 的解析。
    Chaox
        30
    Chaox  
    OP
       2020-11-04 11:48:30 +08:00
    @xx6412223 也就是说 比如 应用层的数据如果要给传输层,不过不转换成 base64 编码,有些数据比如换行等没办法被正确识别。是这个意思吗?
    Chaox
        31
    Chaox  
    OP
       2020-11-04 11:53:51 +08:00
    @shawndev 那直接把这些字符变为二进制( ASCII )不就不会影响了吗
    shawndev
        32
    shawndev  
       2020-11-04 11:58:31 +08:00
    @Chaox 上面打错了,ascii 不是只有>=128 的字符不可显示。你如果了解 utf8 编码就不会问为什么不用 utf8 对数据解码形成文本,因为不是所有 bytes 都能转成 utf8 string
    kuro1
        33
    kuro1  
       2020-11-04 12:00:09 +08:00
    btc 还用 base58 呢,一套编码罢了( ipfs 最近还用 base32
    shawndev
        34
    shawndev  
       2020-11-04 12:02:35 +08:00
    lmmortal
        35
    lmmortal  
       2020-11-04 12:15:55 +08:00 via iPhone
    @SuperMild 谢谢
    @GM 确实搞错了,谢谢
    imn1
        36
    imn1  
       2020-11-04 12:32:13 +08:00
    不知道这句话是何时何地看到的
    如果语境不同,路由设备所指的可能也不同,如果中间有个设备是指语义防火墙,是无法保证任意数据 AB 两地一致的,只能降低“出错的可能性”
    ggabc
        37
    ggabc  
       2020-11-04 12:33:17 +08:00
    base64 的重要意义就是把各种字符都统一用 64 个基础字符(实际并不是刚好 64,别纠结这个)表示了,用来简化传输难度,传输完成后还原回去用。
    图片可以 base64,汉字可以 base64,ascii 字符也不冲突照样可以 base64 编码解码一下
    xiangyuecn
        38
    xiangyuecn  
       2020-11-04 12:39:36 +08:00
    base64 之后 还可以 base64,无限套娃,气不气人
    no1xsyzy
        40
    no1xsyzy  
       2020-11-04 13:13:43 +08:00
    所以说
    0b11111111

    "11111111"
    要好好区分啊……
    Chaox
        41
    Chaox  
    OP
       2020-11-06 10:48:43 +08:00
    @t6attack 理解了,谢谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3028 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:35 · PVG 22:35 · LAX 06:35 · JFK 09:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.