App Inventor 2 进制转换与位运算教程 - 十六进制、二进制、补码、字节操作

« 返回首页

一、为什么需要进制转换

在 App Inventor 2 开发中,进制转换和位运算在以下场景中非常常见:

  • 串口通信:蓝牙、BLE、USB 串口收发的数据通常是十六进制或字节流
  • 物联网协议:Modbus、MQTT 等协议的数据帧涉及多字节拼合和解析
  • 传感器数据:温度、加速度等传感器返回的原始数据需要按字节解析
  • 颜色编码:ARGB 颜色值本质上是十六进制表示的 32 位整数

App Inventor 2 内置了丰富的数学积木块来支持这些操作,本文将逐一详解。

只是想收发设备的数据帧(拼包、把两个字节还原成温度、算 CRC 校验)?内置的二进制组件一块积木就能做,见二进制数据专题。本文讲的是背后的原理和纯积木写法。


二、App Inventor 2 内置的进制相关积木块

2.1 基本数字块(Basic Number Block)

基本数字块 number 不仅仅支持十进制,还支持通过 C 语言风格前缀输入其他进制:

前缀 进制 示例 十进制值
0b 二进制(Base-2) 0b1010 10
0o 八进制(Base-8) 0o14 12
0x 十六进制(Base-16) 0xFF 255

使用方法:直接在数字块中输入带前缀的数字,App Inventor 会自动将其识别为对应进制并转换为十进制值。

数字块: 0xFF    → 值为 255
数字块: 0b1010  → 值为 10
数字块: 0o17    → 值为 15

注意:十六进制字母不区分大小写,0xFF 和 0xff 等价。

2.2 基数数字块(Radix Number Block)

基数数字块提供了一种更直观的进制输入方式。点击下拉菜单可以选择输入进制:

  • decimal(十进制,Base-10):支持正数、负数、小数
  • binary(二进制,Base-2):仅支持非负整数
  • octal(八进制,Base-8):仅支持非负整数
  • hexadecimal(十六进制,Base-16):仅支持非负整数
基数数字块 [hex] FF → 值为 255
基数数字块 [binary] 11111111 → 值为 255
基数数字块 [decimal] 255 → 值为 255

三个积木块返回的值完全相同,只是输入方式不同。

2.3 convert number 积木块(进制转换核心)

这是进制转换最核心的积木块:

global ret1 = convert_number("255", 10, 16)
global ret2 = convert_number("10", 10, 2)
global ret3 = convert_number("FF", 16, 10)

关键特性:

  • 输入和输出都是文本类型(text),不是数字类型(number)
  • 支持的进制:base-2(二进制)、base-8(八进制)、base-10(十进制)、base-16(十六进制)
  • 转换后的十六进制字母为大写(如 FF,不是 ff)

2.4 format as decimal 积木块

将数字格式化为指定小数位数的文本:

global ret4 = format_as_decimal(3.14159, 2)
global ret5 = format_as_decimal(255, 0)

三、常用进制转换方法

3.1 十进制转十六进制

procedure 十进制转十六进制(十进制数) {
  global ret_val = convert_number(十进制数, 10, 16)
}

示例:

输入(十进制) 输出(十六进制)
255 “FF”
16 “10”
10 “A”
0 “0”

3.2 十六进制转十进制

procedure 十六进制转十进制(十六进制文本) {
  global ret_val = convert_number(十六进制文本, 16, 10)
}

注意:convert number 返回的是文本,如果需要参与数学运算,需要用 number 函数转换:

procedure 十六进制文本转数值(十六进制文本) {
  global ret_val = convert_number(十六进制文本, 16, 10)
}

3.3 十进制转二进制

procedure 十进制转二进制(十进制数) {
  global ret_val = convert_number(十进制数, 10, 2)
}

示例:

输入(十进制) 输出(二进制)
10 “1010”
255 “11111111”
128 “10000000”

3.4 二进制转十进制

procedure 二进制转十进制(二进制文本) {
  global ret_val = convert_number(二进制文本, 2, 10)
}

3.5 十六进制与二进制互转

App Inventor 的 convert number 不直接支持 base-16 到 base-2 的转换,但可以中转:

procedure 十六进制转二进制(十六进制文本) {
  global 十进制文本 = convert_number(十六进制文本, 16, 10)
  global ret_val = convert_number(十进制文本, 10, 2)
}

3.6 字符串左侧补零

在很多通信协议中,数据需要固定位宽(如 8 位二进制、2 位十六进制),这就需要左侧补零:

procedure 左侧补零(原始文本, 目标长度) {
  global 补零数 = 目标长度 - lengthOfList(原始文本)
  if (补零数 > 0) {
    global ret_val = join(重复文本("0", 补零数), 原始文本)
  } else {
    global ret_val = 原始文本
  }
}

示例:

原始文本 目标长度 输出
“FF” 4 “00FF”
“A” 2 “0A”
“1010” 8 “00001010”

完整过程——十进制转 2 位十六进制:

procedure 十进制转两位十六进制(十进制数) {
  global 十六进制文本 = convert_number(十进制数, 10, 16)
  global ret_val = 左侧补零(十六进制文本, 2)
}

四、位运算积木块

App Inventor 2 内置了三个位运算积木块,它们在处理二进制数据时非常有用。

4.1 Bitwise And(按位与)

global res1 = bitwise_and(数值A, 数值B)

逐位比较,两个对应位都为 1 时结果位为 1,否则为 0。

  6 = 0110
  3 = 0011
  ----------
AND = 0010 = 2

典型用途:提取特定位、掩码操作。

// 提取低 4 位
global res2 = bitwise_and(数值, 15) // 0xF is 15
// 提取高 4 位
global res3 = bitwise_and(数值, 240) // 0xF0 is 240

4.2 Bitwise Or (Inclusive)(按位或)

global res4 = bitwise_ior(数值A, 数值B)

逐位比较,任一对应位为 1 时结果位为 1。

  6 = 0110
  3 = 0011
  ----------
 OR = 0111 = 7

典型用途:合并标志位。

// 设置第 0 位为 1
global res5 = bitwise_ior(数值, 1)

4.3 Bitwise Or (Exclusive / XOR)(按位异或)

global res6 = bitwise_xor(数值A, 数值B)

逐位比较,两个对应位不同时结果位为 1,相同时为 0。

  6 = 0110
  3 = 0011
  ----------
XOR = 0101 = 5

典型用途:数据校验、简单加密、翻转特定位。

4.4 位运算的组合应用:提取字节

将一个 16 位整数拆分为高字节和低字节:

procedure 提取高字节(十六位整数) {
  global ret_val = bitwise_and(十六位整数, 65280) // 0xFF00
}

注意:App Inventor 2 没有内置的位移(shift)积木块,但可以通过除法和乘法模拟:

  • 右移 n 位 = 除以 2^n 后取 floor
  • 左移 n 位 = 乘以 2^n
procedure 右移8位(数值) {
  global ret_val = math.floor(数值 / 256)
}

procedure 提取高字节(十六位整数) {
  global ret_val = math.floor(十六位整数 / 256)
}

procedure 提取低字节(十六位整数) {
  global ret_val = bitwise_and(十六位整数, 255)
}

五、补码(Two’s Complement)计算

5.1 什么是补码

在计算机中,有符号整数使用补码(Two’s Complement)来表示负数。对于一个 N 位有符号整数:

  • 最高位(MSB)为 0:表示正数,值就是其二进制表示
  • 最高位(MSB)为 1:表示负数,值的绝对值 = 按位取反再加 1

以 8 位有符号整数为例:

二进制 无符号值 有符号值(补码)
00000000 0 0
01111111 127 127
10000000 128 -128
11111111 255 -1
11111110 254 -2

5.2 在 App Inventor 2 中计算补码

场景1:将无符号字节转换为有符号值

从串口收到的数据是 0~255 的无符号字节,但某些传感器返回的是有符号温度值(-128~127)。需要将无符号值转为有符号值:

procedure 无符号字节转有符号(无符号字节) {
  if (无符号字节 > 127) {
    global ret_val = 无符号字节 - 256
  } else {
    global ret_val = 无符号字节
  }
}

示例:

无符号值 有符号值
255 -1
254 -2
128 -128
127 127
0 0

场景2:将有符号值转换为无符号字节(用于发送)

procedure 有符号转无符号字节(有符号数值) {
  if (有符号数值 < 0) {
    global ret_val = 有符号数值 + 256
  } else {
    global ret_val = 有符号数值
  }
}

示例:

有符号值 无符号字节
-1 255
-128 128
0 0
127 127

场景3:16 位有符号整数(Int16)的补码

procedure 无符号16位转有符号(无符号数值) {
  if (无符号数值 > 32767) {
    global ret_val = 无符号数值 - 65536
  } else {
    global ret_val = 无符号数值
  }
}

场景4:求一个数的补码二进制表示

将一个有符号整数转为 N 位二进制补码字符串:

procedure 求补码二进制(负整数) {
  global 无符号值 = 256 + 负整数 // 对于8位
  global ret_val = 十进制转二进制(无符号值)
}

示例:

有符号值 位数 补码二进制
5 8 “00000101”
-5 8 “11111011”
-1 8 “11111111”
-128 8 “10000000”
127 8 “01111111”

原理说明:

以 -5 的 8 位补码为例:

  1. -5 + 256 = 251
  2. 251 的二进制 = 11111011
  3. 这就是 -5 的 8 位补码表示

六、字节与整数互转

6.1 多字节拼合为整数(解析收到的数据帧)

在串口通信中,经常需要将多个字节拼合成一个 16 位或 32 位整数。

大端序(Big-Endian,高位在前)

procedure 两字节大端拼合(高字节, 低字节) {
  global ret_val = 高字节 * 256 + 低字节
}

示例:收到字节 0x1A, 0x2B → 结果 = 0x1A * 256 + 0x2B = 6699

小端序(Little-Endian,低位在前)

procedure 两字节小端拼合(高字节, 低字节) {
  global ret_val = 低字节 * 256 + 高字节
}

示例:收到字节 0x2B, 0x1A → 低字节=0x2B, 高字节=0x1A → 结果 = 0x1A * 256 + 0x2B = 6699

四字节拼合为 32 位整数

procedure 四字节大端拼合(字节0, 字节1, 字节2, 字节3) {
  global ret_val = 字节0 * 16777216 + 字节1 * 65536 + 字节2 * 256 + 字节3
}

其中 16777216 = 2^24,65536 = 2^16

6.2 整数拆分为字节(构建发送数据帧)

procedure 整数拆两字节大端(数值) {
  global 高字节 = math.floor(数值 / 256)
  global 低字节 = modulo(数值, 256)
  global ret_val = [高字节, 低字节]
}
procedure 整数拆四字节大端(数值) {
  global 字节0 = math.floor(数值 / 16777216)
  global 字节1 = math.floor(modulo(数值, 16777216) / 65536)
  global 字节2 = math.floor(modulo(数值, 65536) / 256)
  global 字节3 = modulo(数值, 256)
  global ret_val = [字节0, 字节1, 字节2, 字节3]
}

七、浮点数与字节互转

7.1 问题描述

很多传感器(如温度传感器、IMU)返回的数据是 IEEE 754 浮点数,需要将 4 个字节还原为浮点数,或将浮点数转为 4 字节用于发送。

App Inventor 2 没有内置浮点数与字节的互转积木块,但可以通过 Web浏览器1 执行 JavaScript 来实现。

7.2 使用 Web浏览器1 进行浮点数转换

步骤1:在 Screen 中添加一个 Web浏览器1 组件(可以设为不可见)。

步骤2:编写 HTML 文件,包含浮点数转换的 JavaScript 函数:

<!-- float_converter.html -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body>
<script>
// 浮点数转4字节(小端序)
function floatToBytes(value) {
  var buf = new ArrayBuffer(4);
  new Float32Array(buf)[0] = value;
  var bytes = new Uint8Array(buf);
  return JSON.stringify([bytes[0], bytes[1], bytes[2], bytes[3]]);
}

// 4字节转浮点数(小端序)
function bytesToFloat(b0, b1, b2, b3) {
  var buf = new ArrayBuffer(4);
  var bytes = new Uint8Array(buf);
  bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3;
  return new Float32Array(buf)[0];
}
</script>
</body>
</html>

步骤3:在 App Inventor 中调用:

when 蓝牙客户端1.BytesReceived(numberOfBytes) {
  global 字节列表 = 蓝牙客户端1.ReceiveSignedBytes(numberOfBytes)
  // 字节列表形如:[-1, 85, 10, ...]
}

7.3 利用 Web 组件进行转换

另一种方式是通过 Web 组件调用本地 JavaScript:

when 蓝牙客户端1.BytesReceived(numberOfBytes) {
  global 无符号字节列表 = 蓝牙客户端1.ReceiveUnsignedBytes(numberOfBytes)
  // 无符号字节列表形如:[255, 85, 10, ...]
}

八、ASCII 码与文本互转

8.1 文本转 ASCII 码

App Inventor 2 没有直接的 ord() 函数,但可以使用文本积木块组合实现:

procedure 文本转ASCII码列表(文本) {
  global 字节列表 = []
  global i = 1
  while (i <= length(文本)) {
    global 字符 = segment(文本, i, 1)
    // 这里需要扩展或自定义方法转换字符为ASCII,示意
    addItemsTo(字节列表, CharToAscii(字符))
    global i = i + 1
  }
  global ret_val = 字节列表
}

注意:App Inventor 的文本块中没有直接的 charAt 或 ord 积木块,但可以通过以下方式实现:

  • segment(文本, 起始位置, 长度) — 截取子串
  • 对于 ASCII 码转换,可以使用 JavaScript 通过 Web浏览器1 调用 charCodeAt()

8.2 ASCII 码转文本

使用 characterFromCode 积木块(在文本相关操作中)将 Unicode 码点转为字符。


九、UTF-8 编码与文本转换

9.1 问题场景

通过蓝牙或串口收到 UTF-8 编码的字节流,需要还原为中文文本。

9.2 解决方案

App Inventor 2 内部的文本编码为 UTF-8,所以:

  • 如果使用蓝牙 ReceiveText 方法,App Inventor 会自动处理 UTF-8 解码
  • 如果使用 ReceiveBytes 或 Signed/Unsigned Byte 接收,需要手动处理

对于手动字节流到 UTF-8 文本的转换,推荐使用 Web浏览器1 + JavaScript:

function bytesToUTF8(byteArray) {
  var uint8 = new Uint8Array(byteArray);
  var decoder = new TextDecoder('utf-8');
  return decoder.decode(uint8);
}

十、实战案例:串口通信数据解析

10.1 场景描述

通过蓝牙串口接收传感器数据,协议格式如下:

帧头: 0xAA 0x55
长度: 1字节(数据区字节数)
数据区: 温度(2字节, 有符号, 大端序) + 湿度(2字节, 无符号, 大端序)
校验: 1字节(数据区所有字节异或值)

10.2 数据帧示例

假设收到以下十六进制字节:

AA 55 04 00 C8 01 90 58

解析:

  • AA 55 — 帧头
  • 04 — 数据长度(4字节)
  • 00 C8 — 温度:0x00C8 = 200 → 有符号 = +200(0.1°C单位,即 20.0°C)
  • 01 90 — 湿度:0x0190 = 400(0.1%单位,即 40.0%)
  • 58 — 校验:0x00 ⊕ 0xC8 ⊕ 0x01 ⊕ 0x90 = 0x59… 实际需要验证

10.3 App Inventor 2 解析代码

global 字节列表 = [255, 1, 10]
蓝牙客户端1.SendBytes(字节列表)

10.4 提取温度和湿度值

procedure 处理有效数据(字节列表) {
  if (lengthOfList(字节列表) >= 5) {
    global 帧头 = selectListItem(字节列表, 1)
    global 地址 = selectListItem(字节列表, 2)
    global 命令 = selectListItem(字节列表, 3)
    global 数据 = selectListItem(字节列表, 4)
    global 校验和 = selectListItem(字节列表, 5)
  }
}

10.5 发送十六进制命令

procedure 发送十六进制命令(hexString) {
  global 字节列表 = 十六进制文本转字节列表(hexString)
  蓝牙客户端1.SendBytes(字节列表)
}

十一、常用工具过程汇总

以下是将本文所有工具过程整理在一起,方便直接使用:

左侧补零

procedure 左侧补零(原始文本, 目标长度) {
  global 补零数 = 目标长度 - length(原始文本)
  if (补零数 > 0) {
    global ret_val = join(重复文本("0", 补零数), 原始文本)
  } else {
    global ret_val = 原始文本
  }
}

十进制转固定位宽十六进制

procedure 十进制转固定位十六进制(十进制数, 长度) {
  global 十六进制文本 = convert_number(十进制数, 10, 16)
  global ret_val = 左侧补零(十六进制文本, 长度)
}

无符号转有符号

procedure 无符号转有符号(无符号字节) {
  if (无符号字节 > 127) {
    global ret_val = 无符号字节 - 256
  } else {
    global ret_val = 无符号字节
  }
}

异或校验

procedure 异或校验(字节列表) {
  global 校验值 = 0
  for item in 字节列表 {
    global 校验值 = bitwise_xor(校验值, item)
  }
  global ret_val = 校验值
}

十二、积木块速查表

需求 使用哪个积木块 所在分类
输入十六进制数 基本数字块 0xFF 或基数数字块 Math
输入二进制数 基本数字块 0b1010 或基数数字块 Math
十进制 ↔ 十六进制 convert number Math
十进制 ↔ 二进制 convert number Math
十六进制 ↔ 二进制 先转十进制再转目标进制(中转法) Math
按位与(掩码) Bitwise And Math
按位或(置位) Bitwise Or Math
按位异或(校验) Bitwise Or (Exclusive) Math
右移 n 位 floor(数值 / 2^n) Math
左移 n 位 数值 * 2^n(用 Bitwise And 截断) Math
补码转有符号 if 值 > 2^(n-1)-1 then 值 - 2^n Math + Control
浮点数 ↔ 字节 Web浏览器1 + JavaScript(IEEE 754) Web浏览器1
ASCII 码 ↔ 字符 segment + Unicode 操作 Text

参考资料


*文档版本:2026.05 作者:App Inventor 2 中文网 www.fun123.cn*

参考资料与版权声明

原文来源

版权声明

本文档基于 MIT App Inventor 官方文档整理编写,参考 Apache 2.0 授权内容。 本文档由 ai2claw 🐝 编写整理,App Inventor 2 中文网(www.fun123.cn)原创发布,仅供学习参考。

文档反馈