字符编解码与位运算

51 阅读5分钟

判断二进制数据中第n位是否为1

N:待判断的二进制数
B:待判断的位(右往左,索引从1开始)
 
结果:((N>>(B-1)))&1

parseInt('111',2) # 7


十进制转二进制 21..toString(2) 得`10101`

二进制位1的个数

let count = 0;
while (n >0 ) {
n &= (n-1);
count++;
}

按位与运算 |

其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。

例如:
9|5
可写算式如下:
 00001001
 |
 00000101
=00001101

parseInt('00001101',2) => 13

按位异或运算

按位异或运算符“^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。

例如
9^5
可写成算式如下:
 00001001
 ^
 00000101
=00001100
(十进制为12)

parseInt('00001100',2) => 12

求反运算

求反运算符~为单目运算符,具有右结合性。 其功能是对参与运算的数的各二进位按位求反。

例如
求反运算为:
~(1001)
结果为:
0110

<< 左移动多少位

2<<3 表示2的各二进制位左移3位, 2的二进制'00000010' (2..toString(2)), 左移动3位的二进制变为 '00010000',转十进制结果为 16 (parseInt('00010000',2)); 也可以理解该值左移n位就是该值乘以2的n次方:如2乘以23次方(2*2*2*2)

ascii 码编号转字符

'='.charCodeAt(0) => 61 # 字符=号对应的ascii码 61
String.fromCharCode(61) => '=' # 将ascii编码为61转字符 "="

var a = "我";
console.log(a.charCodeAt()); //25105
console.log(a.charCodeAt().toString(2)); //110001000010001

比特 bit 字节 byte ASCII码 Unicode 关系

一个字节是由两位16进制数组成。
十六进制数转换成二进制数:把每一个十六进制数转换成4位的二进制数,就得到一个二进制数
例子:
十六进制 0xA5
方法一: A*161次方+5*160次方得出十进制。10(0xA为十)*16+5*1=165  然后拿十进制再转二进制(165).toString(2) // "10100101"

方法二:16进制中A用二进制表是多少?A代表十进制数10 ,(0xA).toString(2) 就是二进制的 1010, 5的二进制(0x5).toString(2) "101"转换成4位的二进制数,前补齐0"0101", 拼接二进制位得  "1010 0101"
二进制转十进制 parseInt('10100101', 2) // 165 0xA5的十进制刚好也是165

(165).toString(16) // 十进制165转16进制位 "0xa5" 0x表示十六进制

二进制数字与十六进制数字的对应关系如下:0000 -> 00001 -> 10010 -> 20011 -> 30100 -> 40101 -> 50110 -> 60111 -> 71000 -> 81001 -> 91010 -> A、1011 -> B、1100 -> C、1101 -> D、1110 -> E、1111 -> F
因此,116进制数对应4个二进制数位,216进制数位对应8个二进制数位,及1个字节(8个比特位)

python struct 字节流读取

puffer = b'\x00\x00(\x00' # 4个字节
struct.unpack('I', puffer) # I表示整数, 整数占4个字节 解码= 2621440

# 底层算法
(puffer[0] | puffer[1] << 8 | puffer[2] << 16 | puffer[3] << 24);

8,16,24 表示左移动 81,2,3次方
# puffer[0] = 0
# puffer[1] = 0
# puffer[2] = 40
# puffer[3] = 0
带入计算 (0 | 0<<8 | 40 << 16 |0 << 24)= 2621440
40左移动16 表示40*216次方

二进制数组

ArrayBuffer,字符串互转

// ArrayBuffer转为字符串,参数为ArrayBuffer对象
function ab2str(buf) {
   return String.fromCharCode.apply(null, new Uint16Array(buf));
}
// 字符串转为ArrayBuffer对象,参数为字符串
function str2ab(str) {
   var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
   var bufView = new Uint16Array(buf);
   for (var i = 0, strLen = str.length; i < strLen; i++) {
      bufView[i] = str.charCodeAt(i);
   }
   return buf;
}

字节、字符/字符串、Unicode 字符集

ASCII码中,一个英文字母(不分大小写)占一个字节byte的空间,一个中文汉字占两个字节的空间
UTF-8编码中,一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
Unicode编码中,一个英文等于两个字节,一个中文(含繁体)等于两个字节。
符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小。
UTF-16编码中,一个英文字母字符或一个汉字字符存储都需要2个字节(Unicode扩展区的一些汉字存储需要4个字节)。
UTF-32编码中,世界上任何字符的存储都需要4个字节。

字符串转16进制

ASCII码映射表

var b = 'a'.charCodeAt(0) // 字符串a的结果为十进制 97 的ASCII码
var c =  b.toString(16) // 十进制97转为16进制结果 61 十六进制表示为 0x61
var d = c.toString(2) // 16进制转为二进制结果 1100001 
var e = parseInt("1100001",2) // 二进制转为十进制ASCII码 为97
var f = String.fromCharCode(e) // 十进制 97 的ASCII码转字符串为 a

'a'(a字符占一个字节) -> 97(十进制 ASCII码) -> 0x61(十六进制) -> '01100001'(二进制,8比特位一个字节)

Unicode 编解码

// 解码
// "你好" 对应Unicode 编码为 "\\u4f60\\u597d" 其中4f60 为16进制(Unicode一个中文占两个字节), 0x4f60 转十进制 
var a = parseInt("0x4f60",16) // 等于十进制 20320 
String.fromCharCode(a) // 20320 十进制unicode字符为'你'

// 编码
var a = "好".charCodeAt(0) // 转为十进制unicode码'22909' 
var b = a.toString(16) // 转为十六进制unicode 597d
var u = `\\u${b}` // '\\597d'

字符编解码

字符'b', 'i', 't', 's' 对应ASCII码 分别为 98,105,116,115, 每个字符占一个字节(8比特位)共四个字节,编码成一个整数 98<<24|105<<16|116<<8|115<<0, 24,16,8,0分别为左右3字节(3 x 8),2字节(2 x 8),1字节(1 x 8),0字节(0 x 8)比特位, 等于1651078259。

示例

'你'.charCodeAt(0).toString(16) 转unicode 16进制表示
unicode 20320 => 4f60 占两个字节,216进制字符为一个字节
0x4f <<8|0x60 <<0 = 20320

解码

字符'b', 'i', 't', 's' 字节表示 []byte{98, 105, 116, 115} 一共4字节, 编码 98<<24|105<<16|116<<8|115<<0 分别左移3(3*8 =24比特位),2,1,0字节后成 1651078259整数, 解码时只需要将整数 按位右移>>, 0xff 为ASCII码范围,ASCII码共有256个字符 ASCII码占用一个字节,可以有0~255共256个取值

1651078259>>24 // 98
1651078259>>16 & 0xff //105
1651078259>>8 & 0xff // 116
1651078259>>0 & 0xff // 115

中文转成16进制 转成十进制 '菲'.charCodeAt() => 33778, (33778).toString(16) => 83f2.

或者 33778>>8 & 0xff =131, 33778>>0 & 0xff =242中文unicode占两个字节,所以右移2次 . 131 和 242的16进制表示为0x83, 0xf2, 拼接后得到0x83f2, 16进制每两位为一个字节,

Unicode 和 UTF-8 之间的区别

Unicode 是字符集。 UTF-8 是编码。

Unicode 是有唯一的十进制数字(代码点)的字符列表。 A = 65,B = 66,C = 67,...。

这个十进制数字列表表示字符串 "hello":104 101 108 108 111

编码是将这些数字转换为二进制数字以存储在计算机中的方式:

UTF-8 编码将像这样(二进制)存储 "hello":01101000 01100101 01101100 01101100 01101111,01101000 => parseInt('01101000', 2) 将二进制字符变为unicode码 104 => String.fromCharCode(104) 将unicode码转为字符 h

编码将数字转换为二进制。字符集将字符转换为数字。

参考: mp.weixin.qq.com/s?__biz=MzU… 二进制读写:www.jb51.net/article/147…