• http通讯及浏览器中的HTML编码、URL编码、base64编码及转义


    http通讯及浏览器中的HTML编码、URL编码、base64编码及转义

    目录

    http通讯及浏览器中的HTML编码、URL编码、base64编码及转义

    1、html编码的特殊字符

    1.1、HTML 实体

    1.2、HTML字符实体,数据的表达

    2、URL编码的特殊字符

    2.1、URL支持的字符        

    2.2、ASCII字符编码表

    3、Base64编码的特殊字符

    3.1、base64在http和html中有许多使用场景:

    3.2、base64字符编码表

    3.3、base64字节规则及编码步骤


    1、html编码的特殊字符

            HTML encoding character

            HTML 和 XHTML 用标准的 7 比特 ASCII 代码在网络上传输数据。

    1.1、HTML 实体

            HTML语言 中的“保留字”,即某些预留字符。
            比如:不能在除“元素”意外的位置,使用小于号(<)和大于号(>),浏览器会误解为它们是标签。
            若有需要让浏览器正确地显示预留字符,我们必须在 HTML 源代码中使用字符实体(character entities)。

    1.2、HTML字符实体,数据的表达

    1、&号 “加” 特殊字符的“约定实体名称”  “加” ;号,比如符号:   < ,表达为:    <

    注意,实体名称:大小写敏感。或者:

    2、&号 “加” # “加” 特殊字符的ASCII码  “加” ;号,比如符号:   < ,表达为:    <

            html保留字的字符实体表:

    显示结果描述实体名称实体编号
    空格  
    <小于号<<
    >大于号>>
    &和号&&
    "引号""
    '撇号 ' (IE不支持)'
    ¢¢
    £££
    ¥日圆¥¥
    欧元
    §小节§§
    ©版权©©
    ®注册商标®®
    商标
    ×乘号××
    ÷除号÷÷

    2、URL编码的特殊字符

            URL encoding character

    2.1、URL支持的字符        

            由于URL只支持英文字母、数字、横杠、下划线、句点、波浪线,若要表示其他字符则需要编码。
            例如:百分号、中文。
            由于百分号也需要编码,因此会出现某些绕过问题。

    ①、URL编码基于ASCII码:

    • URL中无特殊字符: 全是ASCII可显示字符

    https://www.cpuofbs.com/rest/postdata?A=abc&B=123
    https://www.cpuofbs.com/rest/postdata?A=abc&B=123

    • URL中包含特殊字符: 空格 、(顿号等等)

    https://www.cpuofbs.com/rest/postdata?A= abc&B、=123
    https://www.cpuofbs.com/rest/postdata?A=%20abc&B%E3%80%81=123

    • URL中包含特殊字符: 中文

    https://www.cpuofbs.com/rest/postdata?学生姓名=英文名abc&身份证号sfzh=432345678909876543
    https://www.cpuofbs.com/rest/postdata?%E5%AD%A6%E7%94%9F%E5%A7%93%E5%90%8D=%E8%8B%B1%E6%96%87%E5%90%8Dabc&%E8%BA%AB%E4%BB%BD%E8%AF%81%E5%8F%B7sfzh=432345678909876543
     


    ②、特殊字符,先根据当前页面的编码方式转换,比如:

    取其十六进制形式,每两位添加1个%

    2.2、ASCII字符编码表

           HTML 和 XHTML 用标准的 7 比特 ASCII 代码在网络上传输数据。

    2.2.1、“32~126”区段,可显式区段:

    结果描述实体编号
    space
    !exclamation mark!
    "quotation mark"
    #number sign#
    $dollar sign$
    %percent sign%
    &ampersand&
    'apostrophe'
    (left parenthesis(
    )right parenthesis)
    *asterisk*
    +plus sign+
    ,comma,
    -hyphen-
    .period.
    /slash/
    0digit 00
    1digit 11
    2digit 22
    3digit 33
    4digit 44
    5digit 55
    6digit 66
    7digit 77
    8digit 88
    9digit 99
    :colon:
    ;semicolon;
    <less-than<
    =equals-to=
    >greater-than>
    ?question mark?
    @at sign@
    Auppercase AA
    Buppercase BB
    Cuppercase CC
    Duppercase DD
    Euppercase EE
    Fuppercase FF
    Guppercase GG
    Huppercase HH
    Iuppercase II
    Juppercase JJ
    Kuppercase KK
    Luppercase LL
    Muppercase MM
    Nuppercase NN
    Ouppercase OO
    Puppercase PP
    Quppercase QQ
    Ruppercase RR
    Suppercase SS
    Tuppercase TT
    Uuppercase UU
    Vuppercase VV
    Wuppercase WW
    Xuppercase XX
    Yuppercase YY
    Zuppercase ZZ
    [left square bracket[
    \backslash\
    ]right square bracket]
    ^caret^
    _underscore_
    `grave accent`
    alowercase aa
    blowercase bb
    clowercase cc
    dlowercase dd
    elowercase ee
    flowercase ff
    glowercase gg
    hlowercase hh
    ilowercase ii
    jlowercase jj
    klowercase kk
    llowercase ll
    mlowercase mm
    nlowercase nn
    olowercase oo
    plowercase pp
    qlowercase qq
    rlowercase rr
    slowercase ss
    tlowercase tt
    ulowercase uu
    vlowercase vv
    wlowercase ww
    xlowercase xx
    ylowercase yy
    zlowercase zz
    {left curly brace{
    |vertical bar|
    }right curly brace}
    ~tilde~

    2.2.2、“0~30及独立的127”区段,设备控制区段:

           ASCII设备控制代码最初被设计为用来控制诸如打印机和磁带驱动器之类的硬件设备。在HTML文档中这些代码不会起任何作用:

    结果描述实体编号
    NULnull character
    SOHstart of header
    STXstart of text
    ETXend of text
    EOTend of transmission
    ENQenquiry
    ACKacknowledge
    BELbell (ring)
    BSbackspace
    HThorizontal tab
    LFline feed
    VTvertical tab
    FFform feed
    CRcarriage return
    SOshift out
    SIshift in
    DLEdata link escape
    DC1device control 1
    DC2device control 2
    DC3device control 3
    DC4device control 4
    NAKnegative acknowledge
    SYNsynchronize
    ETBend transmission block
    CANcancel
    EMend of medium
    SUBsubstitute
    ESCescape
    FSfile separator
    GSgroup separator
    RSrecord separator
    USunit separator
    DELdelete (rubout)

    3、Base64编码的特殊字符

            Base64 encoding character

    3.1、base64在http和html中有许多使用场景:

    1、比如我们在html中表达1个图片文件进行base64编码的src资源,可以:

    data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==

     2、再比如 对文件进行base64编码,我们上传和下载超大文件,可先对其进行base64二进制编码再压缩后,进行,这样可以极大提升效率并提供了“断点”续传的可能

            案例:delphi压缩后使用http协议base64上传下载6G超大文件_pulledup的博客-CSDN博客delphi压缩后使用http协议base64上传下载6G超大文件注:服务端软件,使用高勇出品GYRestServer系列。欢迎使用,加QQ群咨询:174483085一、知识点:1、Delphi自带的压缩解压单元system.zlib.pas中核心函数的使用2、服务端http协议ContentType(mime-type)相关列表类型的注册3、Base64编码的规则4、为何要分块断点续传,并使用TFileStream文件流替代内存流TMemoryStream5、Buffe..https://blog.csdn.net/pulledup/article/details/121455926

    3.2、base64字符编码表

    base64_百度百科Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。可查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读。Base64由于以上优点被广泛应用于计算机的各个领域,然而由于输出内容中包括两个以上“符号类”字符(+, /, =),不同的应用场景又分别研制了Base64的各种“变种”。为统一和规范化Base64的输出,Base62x被视为无符号化的改进版本。https://baike.baidu.com/item/base64/8545775?fr=aladdin

            大写A~Z ,小写a~z , 数字0~9, +  \  ,以及1个特殊的“字符A”结尾标识符 == 

    6位二进制补齐后的10进制索引
    base64
    字符
    6位二进制补齐后的10进制索引base64字符6位二进制补齐后的10进制索引base64字符6位二进制补齐后的10进制索引base64字符
    0
    A
    16
    Q
    32
    g
    48
    w
    1
    B
    17
    R
    33
    h
    49
    x
    2
    C
    18
    S
    34
    i
    50
    y
    3
    D
    19
    T
    35
    j
    51
    z
    4
    E
    20
    U
    36
    k
    52
    0
    5
    F
    21
    V
    37
    l
    53
    1
    6
    G
    22
    W
    38
    m
    54
    2
    7
    H
    23
    X
    39
    n
    55
    3
    8
    I
    24
    Y
    40
    o
    56
    4
    9
    J
    25
    Z
    41
    p
    57
    5
    10
    K
    26
    a
    42
    q
    58
    6
    11
    L
    27
    b
    43
    r
    59
    7
    12
    M
    28
    c
    44
    s
    60
    8
    13
    N
    29
    d
    45
    t
    61
    9
    14
    O
    30
    e
    46
    u
    62
    +
    15
    P
    31
    f
    47
    v
    63
    /

    base64字符索引、二进制、base64字符 对照表:

    索引2进制Char索引2进制Char索引2进制Char索引2进制Char
    0000000A16010000Q32100000g48110000w
    1000001B17010001R33100001h49110001x
    2000010C18010010S34100010i50110010y
    3000011D19010011T35100011j51110011z
    4000100E20010100U36100100k521101000
    5000101F21010101V37100101l531101011
    6000110G22010110W38100110m541101102
    7000111H23010111X39100111n551101113
    8001000I24011000Y40101000o561110004
    9001001J25011001Z41101001p571110015
    10001010K26011010a42101010q581110106
    11001011L27011011b43101011r591110117
    12001100M28011100c44101100s601111008
    13001101N29011101d45101101t611111019
    14001110O30011110e46101110u62111110+
    15001111P31011111f47101111v63111111/

    Padding

    填充位

    =

     代码表示Base64字符表:

    1. Shotgun.Js.Base64 = {
    2. _table: [
    3. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    4. 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    5. 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    6. 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
    7. ],

    3.3、base64字节规则及编码步骤

    3.3.1、非base64的字符内存编组方式

            通常,计算机的内存中用8位二进制表达1个字符。

            比如字符A :0100 0001 :

    01000001

    3.3.2、base64的字符内存编组方式

            将原本1*8=8,改编为2*(2补齐+6)=16  

            比如字符A :

    00010000 00010000

    查找ASCII表10进制结果:

    16 16

    或(标准格式):

    00010000 00010000 00000000 00000000

    16 16 NUL(null) NUL(null)

            然后,比照base64字符编码表:

    16 16

    结果:

    QQ

    或(标准格式):

    16 16 NUL(null) NUL(null)  ---------00000000 00000000刚好比对出2个“A”则用==表示:

    QQ==

    3.3.3、js中base64的编码和解码:

    1. // 解决方案 #1 – 在编码之前转义字符串---unescape和escape已弃用 :
    2. function utf8_to_b64(str) {
    3. return window.btoa(unescape(encodeURIComponent(str)));
    4. }; //
    5. function b64_to_utf8(str) {
    6. return decodeURIComponent(escape(window.atob(str)));
    7. }; //
    8. const bin8 = '01000001', // 00010000 00010000
    9. bin16 = '0001000000010000', // 00010000 00010000 代表QQ
    10. bin32 = '00010000000100000000000000000000'; // 00010000 00010000 00000000 00000000 代表QQ==
    11. //
    12. //00010000 00010000
    13. let encodedData = window.btoa("A"); // = Base64编码 (缺陷)
    14. console.log(encodedData); //: QQ==
    15. let decodedData = window.atob("QQ=="); // = ASCII编码 (缺陷)
    16. let decodedData_2 = window.atob("QQ=="); // = ASCII编码 (缺陷)
    17. console.log(decodedData); //:
    18. console.log(utf8_to_b64("A")); //: (缺陷)
    19. console.log(b64_to_utf8("QQ==")); //: 必须2**n幂等 //console.log(2 ** 4);//: 2**n幂运算
    20. console.log(b64_to_utf8("QQ")); //: (缺陷)
    21. //
    22. //解决方案 #2 – 重写atob()和btoa()使用TypedArrays 和 UTF-8 :
    23. //注意:以下代码对于从 Base64 字符串获取ArrayBuffer也很有用,反之亦然(见下文):
    24. //
    25. "use strict";
    26. // 字节数组到Base64字符串解码 :
    27. function b64ToUint6(nChr) {
    28. return nChr > 64 && nChr < 91 ?
    29. nChr - 65 :
    30. nChr > 96 && nChr < 123 ?
    31. nChr - 71 :
    32. nChr > 47 && nChr < 58 ?
    33. nChr + 4 :
    34. nChr === 43 ?
    35. 62 :
    36. nChr === 47 ?
    37. 63 :
    38. 0;
    39. }
    40. function base64DecToArr(sBase64, nBlocksSize) {
    41. const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
    42. const nInLen = sB64Enc.length;
    43. const nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2;
    44. const taBytes = new Uint8Array(nOutLen);
    45. let nMod3;
    46. let nMod4;
    47. let nUint24 = 0;
    48. let nOutIdx = 0; //let nOutId = 0;
    49. for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
    50. nMod4 = nInIdx & 3;
    51. nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
    52. if (nMod4 === 3 || nInLen - nInIdx === 1) {
    53. nMod3 = 0;
    54. while (nMod3 < 3 && nOutIdx < nOutLen) {
    55. taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
    56. nMod3++;
    57. nOutIdx++;
    58. }
    59. nUint24 = 0;
    60. }
    61. }
    62. return taBytes;
    63. }
    64. /* Base64字符串到数组编码 : */
    65. function uint6ToB64(nUint6) {
    66. return nUint6 < 26 ?
    67. nUint6 + 65 :
    68. nUint6 < 52 ?
    69. nUint6 + 71 :
    70. nUint6 < 62 ?
    71. nUint6 - 4 :
    72. nUint6 === 62 ?
    73. 43 :
    74. nUint6 === 63 ?
    75. 47 :
    76. 65;
    77. }
    78. function base64EncArr(aBytes) {
    79. let nMod3 = 2;
    80. let sB64Enc = "";
    81. const nLen = aBytes.length;
    82. let nUint24 = 0;
    83. for (let nIdx = 0; nIdx < nLen; nIdx++) {
    84. nMod3 = nIdx % 3;
    85. if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) {
    86. sB64Enc += "\r\n";
    87. }
    88. nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
    89. if (nMod3 === 2 || aBytes.length - nIdx === 1) {
    90. sB64Enc += String.fromCodePoint(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
    91. nUint24 = 0;
    92. }
    93. }
    94. return sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? '' : nMod3 === 1 ? '=' : '==');
    95. }
    96. /* UTF-8数组到JS字符串,反之亦然 : */
    97. function UTF8ArrToStr(aBytes) {
    98. let sView = "";
    99. let nPart;
    100. const nLen = aBytes.length;
    101. for (let nIdx = 0; nIdx < nLen; nIdx++) {
    102. nPart = aBytes[nIdx];
    103. sView += String.fromCodePoint(
    104. nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
    105. /* (nPart - 252 << 30) may be not so safe in ECMAScript! So…: */
    106. (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 :
    107. nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
    108. (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 :
    109. nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
    110. (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 :
    111. nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
    112. (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 :
    113. nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
    114. (nPart - 192 << 6) + aBytes[++nIdx] - 128 :
    115. /* nPart < 127 ? */
    116. /* one byte */
    117. nPart
    118. );
    119. }
    120. return sView;
    121. }
    122. function strToUTF8Arr(sDOMStr) {
    123. let aBytes;
    124. let nChr;
    125. const nStrLen = sDOMStr.length;
    126. let nArrLen = 0;
    127. /* mapping… */
    128. for (let nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++) {
    129. nChr = sDOMStr.codePointAt(nMapIdx);
    130. if (nChr > 65536) {
    131. nMapIdx++;
    132. }
    133. nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6;
    134. }
    135. aBytes = new Uint8Array(nArrLen);
    136. /* 转录 : transcription… */
    137. let nIdx = 0;
    138. let nChrIdx = 0;
    139. while (nIdx < nArrLen) {
    140. nChr = sDOMStr.codePointAt(nChrIdx);
    141. if (nChr < 128) {
    142. /* one byte */
    143. aBytes[nIdx++] = nChr;
    144. } else if (nChr < 0x800) {
    145. /* two bytes */
    146. aBytes[nIdx++] = 192 + (nChr >>> 6);
    147. aBytes[nIdx++] = 128 + (nChr & 63);
    148. } else if (nChr < 0x10000) {
    149. /* three bytes */
    150. aBytes[nIdx++] = 224 + (nChr >>> 12);
    151. aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    152. aBytes[nIdx++] = 128 + (nChr & 63);
    153. } else if (nChr < 0x200000) {
    154. /* four bytes */
    155. aBytes[nIdx++] = 240 + (nChr >>> 18);
    156. aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    157. aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    158. aBytes[nIdx++] = 128 + (nChr & 63);
    159. nChrIdx++;
    160. } else if (nChr < 0x4000000) {
    161. /* five bytes */
    162. aBytes[nIdx++] = 248 + (nChr >>> 24);
    163. aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
    164. aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    165. aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    166. aBytes[nIdx++] = 128 + (nChr & 63);
    167. nChrIdx++;
    168. } else /* if (nChr <= 0x7fffffff) */ {
    169. /* six bytes */
    170. aBytes[nIdx++] = 252 + (nChr >>> 30);
    171. aBytes[nIdx++] = 128 + (nChr >>> 24 & 63);
    172. aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
    173. aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    174. aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    175. aBytes[nIdx++] = 128 + (nChr & 63);
    176. nChrIdx++;
    177. }
    178. nChrIdx++;
    179. }
    180. return aBytes;
    181. }
    182. // 测试 :
    183. const sMyInput = "Base 64 \u2014 ";
    184. const aMyUTF8Input = strToUTF8Arr(sMyInput);
    185. const sMyBase64 = base64EncArr(aMyUTF8Input);
    186. console.log(sMyBase64);
    187. //
    188. const aMyUTF8Output = base64DecToArr(sMyBase64);
    189. const sMyOutput = UTF8ArrToStr(aMyUTF8Output);
    190. console.log(sMyOutput);
    191. //
    192. const sMyInput_ = "\ud869\ude95\u3400\u499b\u2a6c7\udec7";
    193. const aMyUTF8Input_ = strToUTF8Arr(sMyInput);
    194. const sMyBase64_ = base64EncArr(aMyUTF8Input);
    195. console.log(sMyBase64_);
    196. // 👪 \ 转义符: (€) € € < < &(&) (") 表示为" 将单引号 (') 表示为'
    197. //👨‍👩‍👧‍👦为了表达'\u1F468\u200D\u1F469\u200D\u1F467\u200D\u1F466'使用16进制或10进制
    198. //��㐀䦛𪛇�
    199. //𪛇 ; 𣎴 关于UTF-16补充字符与码点 :使用UCS4高低位转换计算后的码点"𣎴"而不是UTF-16的高低位码: ��
    200. //
    201. // 将 Base64 字符串解码为 Uint8Array 或 ArrayBuffer :
    202. //函数base64DecToArr(sBase64[, nBlockSize])返回一个uint8字节数组。
    203. //如果您的目标是构建16位/32位/64位原始数据的缓冲区,请使用nBlockSize参数(幂等参数) : 这是uint8Array.buffer所包含的字节数 :
    204. //uint8Array.buffer.bytesLength属性:
    205. //1、ASCII二进制字符(即字符串中的每个字符都被视为二进制数据8位1个字节的字符)
    206. //2、或UTF-8编码字符串为1个8位,UTF-16字符串为2个8位,UTF-32字符串为4个8位。
    207. // : 字符编码 :
    208. //
    209. // "Base 64 \u2014 Mozilla Developer Network"
    210. const myArray = base64DecToArr("QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw==");
    211. // "Base 64 \u2014 " // : \u2014字节长3 : \u占1个字节、全角字符-占2个字节
    212. const myBuffer = base64DecToArr("QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw==").buffer;
    213. console.log(myBuffer.byteLength); //:
    214. //当您从服务器检索文档时,服务器通常会随文档发送一些附加信息。这称为 HTTP 标头。这是一个关于文档信息的示例,该信息通过 HTTP 标头与文档一起从服务器传输到客户端时传递

  • 相关阅读:
    【Linux内核系列】进程调度
    BeanFactory创建流程
    vue2中使用ueditor百度富文本,并支持插入公式
    源码导入onnx-tensorrt作为onnx解析器
    论文初稿写到什么程度才算合格?
    计算机网络八股文
    数据智慧:C#中编程实现自定义计算的Excel数据透视表
    ts+axios 定义接口返回值的类型
    PS Raw中文增效工具Camera Raw 16
    沃创云新一代CRM
  • 原文地址:https://blog.csdn.net/pulledup/article/details/126297866