从字节数组中高效提取跨字节边界的位范围数据

从字节数组中高效提取跨字节边界的位范围数据

本教程详细介绍了如何在java中利用`bitset`高效地从字节数组中存取跨字节边界的位范围数据。文章通过重构数据编码和解码方法,展示了如何将整数值精确地写入字节数组的指定位范围,并从这些范围中准确提取数据。核心在于利用`bitset`进行位操作,并通过转换为二进制字符串再解析的方式,避免了复杂的位反转和字节序问题,提供了清晰、可维护的解决方案。

引言:理解字节数组中的位操作

在许多低层数据处理场景中,例如网络协议解析、文件格式处理或嵌入式系统通信,我们经常需要从字节数组中存取非字节对齐的数据。这意味着一个数值可能只占据几个位,并且这些位可能跨越一个或多个字节的边界。Java的BitSet类为这种场景提供了强大的支持,它能够以位为单位进行操作,极大地简化了位级别的数据管理。

然而,在使用BitSet将数据写入字节数组或从字节数组中读取数据时,可能会遇到一些挑战,例如BitSet内部的位序表示、toByteArray()和valueOf()方法的行为,以及如何正确处理跨字节边界的数值。本教程将提供一套清晰且经过优化的方法,用于解决这些问题。

数据编码:将数值写入指定位范围

为了确保数据能够被正确地提取,首先需要确保数据被正确地编码并写入到字节数组中。原始的编码方法可能涉及复杂的位移和反转操作,容易出错。这里我们提供一个更直观、更易于理解和调试的编码方案。

核心思想是:将要存储的整数转换为其二进制字符串表示,然后将其各位数字(0或1)精确地放置到BitSet的指定位索引中。

1. createMessageHeader 方法重构

createMessageHeader 方法负责初始化一个包含所有预设值的BitSet,并最终将其转换为byte[]。我们简化了原始代码中的数组重赋值和末尾的位反转逻辑,使之更直接地生成BitSet。

protected byte[] createMessageHeader() {    // 初始化一个足够大的int数组来表示所有位    int[] set = new int[128]; // 128位    // 使用 integrate 方法将数值写入指定位范围    integrate(set, 3, 3);    integrate(set, 0, 5);    integrate(set, 1000, 15);    integrate(set, 200, 23);    integrate(set, 200, 31);    integrate(set, 1294967295, 63); // 这是一个int范围内的最大值    integrate(set, 5, 71);    integrate(set, 3, 79);    integrate(set, 0, 83);    integrate(set, 0, 85);    integrate(set, 1000, 94); // 注意这里是94,不是95    integrate(set, 200, 103);    integrate(set, 200, 111);    integrate(set, 300, 127);    // 将int数组转换为BitSet    BitSet bitSet = binArrayToBitset(set);    // 将BitSet转换为byte数组。BitSet.toByteArray() 会自动处理位到字节的转换    return bitSet.toByteArray();}

2. integrate 方法详解

integrate 方法是编码的核心。它接收一个整数值和其在BitSet中应结束的索引,然后将该值的二进制表示右对齐地插入到int[]数组中。

/** * 将给定值插入到int数组中,其二进制表示在数组中右对齐到给定索引。 * * @param binary          表示所有位的int数组(0或1) * @param value           要插入的整数值 * @param alignEndToIndex 值在数组中应结束的索引(包含) */protected void integrate(int[] binary, int value, int alignEndToIndex) {    // 将整数转换为其二进制字符串表示    String binaryRepresentation = Integer.toBinaryString(value);    // 将二进制字符串转换为int数组(每个元素是0或1)    int[] digits = numberStringToArrayOfDigits(binaryRepresentation);    // 计算起始索引,确保二进制表示右对齐    int startIndex = alignEndToIndex + 1 - digits.length;    // 使用 System.arraycopy 将数字数组复制到目标int数组的正确位置    System.arraycopy(digits, 0, binary, startIndex, digits.length);}/** * 将表示数字的字符串转换为包含其单个数字的整数数组。 * * @param binaryRepresentation 二进制字符串,例如 "10110" * @return 包含单个数字的int数组,例如 {1, 0, 1, 1, 0} */protected int[] numberStringToArrayOfDigits(String binaryRepresentation) {    int[] digits = new int[binaryRepresentation.length()];    for (int i = 0; i < binaryRepresentation.length(); i++) {        // 将字符 '0' 或 '1' 转换为整数 0 或 1        digits[i] = binaryRepresentation.charAt(i) - '0';    }    return digits;}

3. binArrayToBitset 方法

这个辅助方法将一个由0和1组成的int[]数组转换为BitSet。

/** * 将一个由0和1组成的int数组转换为BitSet。 * * @param binArray 包含0和1的int数组 * @return 对应的BitSet */protected BitSet binArrayToBitset(int[] binArray) {    BitSet set = new BitSet(binArray.length); // 初始化BitSet,指定容量    for (int i = 0; i < binArray.length; i++) {        if (binArray[i] != 0) {            set.set(i); // 如果数组元素为1,则设置BitSet中对应的位        }    }    return set;}

数据解码:从指定位范围提取数值

数据编码完成后,下一步是从字节数组中准确地提取出特定位范围内的数值。原始的提取尝试可能因为BitSet.toLongArray()的内部实现和位序问题而导致错误。这里提供一个更健壮的提取方法。

核心思想是:将输入的byte[]转换回BitSet,然后使用BitSet.get(start, end + 1)方法提取所需的子BitSet。接着,将这个子BitSet转换为一个二进制字符串,并使用Integer.parseInt(String, 2)进行解析。

腾讯交互翻译 腾讯交互翻译

腾讯AI Lab发布的一款AI辅助翻译产品

腾讯交互翻译 183 查看详情 腾讯交互翻译

1. extractBits 方法重构

/** * 从字节数组中提取由给定索引形成的整数。 * * @param header       包含位的字节数组 * @param start        要提取的位范围的起始索引(包含) * @param endInclusive 要提取的位范围的结束索引(包含) * @return 提取出的整数值 */private int extractBits(byte[] header, int start, int endInclusive) {    // 将字节数组转换为BitSet    BitSet bitSet = BitSet.valueOf(header);    // 获取指定范围的子BitSet    final BitSet subset = bitSet.get(start, endInclusive + 1);    // 将子BitSet转换为二进制字符串    final int length = endInclusive - start + 1;    StringBuilder b = new StringBuilder(length);    for (int i = 0; i < length; i++) {        b.append(subset.get(i) ? '1' : '0');    }    // 使用基数2解析二进制字符串为整数    return Integer.parseInt(b.toString(), 2);}

处理 long 类型数值的注意事项:

如果提取的位范围表示的数值可能超出int的范围(即超过31位或数值大于Integer.MAX_VALUE),则需要使用Long.parseLong(b.toString(), 2)来解析,并且extractBits方法的返回类型也应改为long。

/** * 从字节数组中提取由给定索引形成的长整数。 * * @param header       包含位的字节数组 * @param start        要提取的位范围的起始索引(包含) * @param endInclusive 要提取的位范围的结束索引(包含) * @return 提取出的长整数值 */private long extractLongBits(byte[] header, int start, int endInclusive) {    BitSet bitSet = BitSet.valueOf(header);    final BitSet subset = bitSet.get(start, endInclusive + 1);    final int length = endInclusive - start + 1;    StringBuilder b = new StringBuilder(length);    for (int i = 0; i < length; i++) {        b.append(subset.get(i) ? '1' : '0');    }    return Long.parseLong(b.toString(), 2);}

辅助与验证:确保数据完整性

为了在开发和调试过程中验证BitSet的内容是否符合预期,一个可视化的打印方法非常有用。

printBitSetByteWise 方法

这个工具方法可以将BitSet以字节为单位的二进制形式打印到控制台,方便与预期的二进制表示进行比对。

/** * 将BitSet以二进制字节形式打印到标准输出,用'|'分隔字节。 * * @param bitSet 要打印的BitSet */private static void printBitSetByteWise(BitSet bitSet) {    // 遍历BitSet的所有位    for (int i = 0; i  0 && i % 8 == 0) {            System.out.print('|');        }        // 打印当前位的值 (1或0)        System.out.print(bitSet.get(i) ? 1 : 0);    }    System.out.println();}

注意: bitSet.length() 返回的是最高设置位的索引加1。如果BitSet中所有位都未设置,则返回0。为了确保打印出整个预期的128位,可以改为 for(int i = 0; i < 128; i++)。

完整示例与实践

现在,我们将所有组件整合在一起,展示一个完整的从数据创建、打印到提取的流程。

public class BitExtractionTutorial {    // ... (此处放置上面定义的所有 protected 和 private 方法) ...    // createMessageHeader(), integrate(), numberStringToArrayOfDigits(),    // binArrayToBitset(), extractBits(), extractLongBits(), printBitSetByteWise()    public static void main(String[] args) {        BitExtractionTutorial tutorial = new BitExtractionTutorial();        // 1. 创建消息头部(字节数组)        final byte[] header = tutorial.createMessageHeader();        // 2. 验证创建的字节数组内容        System.out.println("--- 原始 BitSet 内容 (字节序打印) ---");        // 将 byte[] 转换回 BitSet 进行打印验证        BitSet createdBitSet = BitSet.valueOf(header);        tutorial.printBitSetByteWise(createdBitSet);        // 预期输出示例:0011|0011|1110|1000|1100|1000|1100|1000|0100|1101|0010|1111|1010|0001|1111|1111|0000|0101|0000|0011|0000|0000|0000|0000|0000|0000|0000|0000|0000|0000|0000|0000... (实际会根据BitSet.toByteArray()的填充和长度有所不同,但前128位应与原始数据匹配)        // 示例数据中的BitSet.toByteArray()行为是 little-endian,即低位字节在前。        // 所以实际打印出的可能与原始问题中从左到右的二进制表示是反的。        // 为了和原始问题中的二进制表示对应,需要注意BitSet.valueOf(byte[])的解释方式。        // BitSet.valueOf(byte[]) 将 byte[0] 的最低位映射到 BitSet 的索引 0,byte[0] 的最高位映射到 BitSet 的索引 7,以此类推。        // 实际 BitSet.valueOf(header) 后的 BitSet 打印结果需要根据 BitSet 的实际行为来解释。        // 如果要严格匹配原始问题中的从左到右的二进制串,需要对 BitSet.valueOf(header) 后的 BitSet 进行位序调整,        // 或者在 integrate 时就按照 BitSet.valueOf(byte[]) 的 little-endian 规则来写入。        // 当前的 integrate 方法是按照从左到右的逻辑写入 int[],然后 binArrayToBitset 再将其转换为 BitSet。        // BitSet.toByteArray() 是 little-endian,所以 byte[0] 包含 BitSet 的 0-7 位,byte[1] 包含 8-15 位。        // 打印时,printBitSetByteWise 是按照 BitSet 索引从小到大打印。        // 因此,如果原始问题中的数据是 MSB-first,而 BitSet 内部是 LSB-first,则需要进行转换。        // 这里的解决方案通过将 BitSet 子集转换为字符串再解析,规避了直接处理字节序的复杂性。        // 3. 提取并打印指定范围的数值        System.out.println("\n--- 提取的数值 ---");        System.out.println("位范围 [6, 15] (期望 1000): " + tutorial.extractBits(header, 6, 15));        System.out.println("位范围 [32, 63] (期望 1294967295): " + tutorial.extractBits(header, 32, 63));        System.out.println("位范围 [104, 111] (期望 200): " + tutorial.extractBits(header, 104, 111));        // 示例:提取一个潜在的 long 值 (如果其位数超过 int 范围)        // 假设有一个值存储在 [112, 127] 且预期是 300        System.out.println("位范围 [112, 127] (期望 300): " + tutorial.extractBits(header, 112, 127));    }    // 复制上面定义的 protected 和 private 方法到此处,以便 main 方法可以调用    protected byte[] createMessageHeader() { /* ... */ return null; }    protected void integrate(int[] binary, int value, int alignEndToIndex) { /* ... */ }    protected int[] numberStringToArrayOfDigits(String binaryRepresentation) { /* ... */ return null; }    protected BitSet binArrayToBitset(int[] binArray) { /* ... */ return null; }    private int extractBits(byte[] header, int start, int endInclusive) { /* ... */ return 0; }    private long extractLongBits(byte[] header, int start, int endInclusive) { /* ... */ return 0L; }    private static void printBitSetByteWise(BitSet bitSet) { /* ... */ }}

(请将上述代码中的 /* … */ 替换为实际的方法实现,并确保 BitExtractionTutorial 类包含所有这些方法)

注意事项与最佳实践

位序(Endianness):BitSet.valueOf(byte[]) 和 BitSet.toByteArray() 默认是小端序(Little-Endian)的。这意味着字节数组的第一个字节(byte[0])对应BitSet的最低位(索引0-7),第二个字节(byte[1])对应BitSet的次低位(索引8-15),依此类推。本教程的integrate方法通过将数值转换为二进制字符串并直接放置到BitSet的指定索引,然后extractBits方法通过获取子BitSet并转换为二进制字符串解析,巧妙地规避了直接处理字节内部位序

以上就是从字节数组中高效提取跨字节边界的位范围数据的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/894267.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
windows11如何查看主板型号和BIOS版本_Windows 11主板型号与BIOS信息查看教程
上一篇 2025年11月28日 16:48:30
小米14Pro怎么关闭自动更新?
下一篇 2025年11月28日 16:48:32

相关推荐

  • mysql安装后如何优化配置文件

    答案:优化MySQL配置需先定位配置文件,再根据硬件和业务调整内存、InnoDB、连接等核心参数。具体包括设置innodb_buffer_pool_size为物理内存50%~70%,合理配置日志参数与连接数,启用慢查询日志,并使用工具辅助调优,避免过度配置,确保稳定高效。 MySQL 安装后,优化配…

    2026年9月21日
    000
  • Linux怎么列出系统中已安装的deb包

    使用dpkg -l或apt list –installed可列出已安装的.deb包,前者结合grep ^ii过滤已安装项,后者输出更清晰,两者均支持重定向保存到文件。 在Linux系统中,特别是基于Debian的发行版(如Ubuntu),可以使用命令行工具列出已安装的.deb包。最常用的…

    2026年9月21日
    000
  • mac怎么阻止特定app访问网络_Mac阻止应用访问网络方法

    可通过系统防火墙、hosts文件、第三方工具或pf防火墙阻止应用联网。首先,macOS内置防火墙可阻断入站连接,需在“系统设置-网络-防火墙”中添加应用并启用阻止;其次,编辑/etc/hosts文件,将目标域名指向127.0.0.1可屏蔽其网络访问,需刷新DNS缓存生效;再者,使用Little Sn…

    2026年9月21日
    000
  • VSCode的括号匹配功能如何自定义?

    可通过 settings.json 自定义括号高亮的边框和背景色;2. 用 editor.matchBrackets 控制是否启用高亮;3. 启用 bracketPairColorization 可为嵌套括号着色;4. 使用 Ctrl/Cmd + Shift + 快速跳转配对括号。 VSCode 的…

    2026年9月21日
    000
  • 马斯克xAI的Grok将推AI视频检测工具,能否破解深度伪造难题?

    随着ai视频生成技术飞速渗透网络,深度伪造内容不断扩散,网络信息真实性面临前所未有的挑战。在此背景下,马斯克的xai公司的grok模型即将推出一项关键升级,打造一款“真伪侦探”工具。 近日,马斯克在X平台回应网友担忧时表示,Grok即将获得识别AI生成视频并追踪其网络来源的能力,以此应对深度伪造内容…

    2026年9月21日
    000
  • JSF应用中Markdown文档动态链接处理指南

    本教程旨在解决jsf web应用程序中集成markdown文档时,如何动态处理内部链接以实现页面局部更新的问题。通过结合服务器端markdown渲染和客户端javascript事件监听,我们可以拦截markdown生成的html链接点击事件,利用ajax异步加载并渲染目标markdown文件,从而在…

    2026年9月21日
    500
  • AI推文助手如何生成节日祝福 AI推文助手的情感连接内容创作

    AI推文助手如何生成节日祝福 AI推文助手的情感连接内容创作AI推文助手如何生成节日祝福 AI推文助手的情感连接内容创作AI推文助手如何生成节日祝福 AI推文助手的情感连接内容创作AI推文助手如何生成节日祝福 AI推文助手的情感连接内容创作

    答案:通过AI推文助手的节日模板、情感关键词、用户数据定制和多语言混合策略,可高效生成个性化祝福,增强受众情感连接。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 如果您希望借助AI推文助手在节日期间传递温暖的祝福,同时增强与受众的情感连接…

    2026年9月21日 用户投稿
    000
  • 如何通过命令行参数启动VSCode?

    掌握VSCode命令行用法可提升开发效率,需先安装code命令到PATH,之后可用code .打开目录、code 文件名打开文件、code –diff比较文件、–disable-extensions排查问题,并支持别名与Shell结合使用。 通过命令行启动 VSCode 是一…

    2026年9月21日
    100
  • 如何基于Swoole开发自定义框架?

    基于swoole开发自定义框架可以通过以下步骤实现:1. 创建核心app类,初始化swoole服务器并定义回调函数;2. 实现路由功能,使用router类处理请求分发;3. 添加中间件支持,使用middleware类处理请求;4. 集成异步数据库操作,使用swoole的mysql协程客户端;5. 实…

    2026年9月21日
    000
  • Linux如何使用dnf安装软件包

    dnf是Fedora、CentOS Stream和RHEL 8+的默认包管理工具,用于安装、更新、删除软件包。1. 安装单个包:sudo dnf install package_name,如htop;2. 安装多个包:sudo dnf install vim curl;3. 从本地.rpm文件安装:…

    2026年9月21日
    000
  • 什么是抖音?– 2024 年您需要了解的一切

    抖音究竟是什么? 抖音是一款专注于短视频分享的社交平台,最初以对口型功能起家,在 Musical.ly 时期广为人知。如今,它已发展成为全球最具影响力的社交媒体之一,用户不仅能创作娱乐内容,还能参与教育、时尚、科技等多元领域的表达与传播。尽管起源于移动端,但通过网页端也能轻松浏览海量视频。平台提供了…

    2026年9月21日
    000
  • windows10如何使用资源监视器查看网络和磁盘活动_windows10资源监视器使用方法

    资源监视器可精确定位Windows 10系统中导致网络延迟或磁盘响应缓慢的高占用进程,通过“网络”和“磁盘”选项卡实时监控各进程的流量、连接、读写速度及响应时间,帮助识别异常程序并分析性能瓶颈。 如果您发现Windows 10系统网络延迟或磁盘响应缓慢,可能是某些进程在后台大量占用资源。资源监视器能…

    2026年9月21日
    100
  • iPhone XR如何关闭无用通知提醒

    关闭iPhone XR无用通知需进入设置→通知,选择App关闭允许通知以彻底禁用,或调整显示预览为从不来隐藏锁屏与横幅内容。 想让iPhone XR清净一点,关掉那些没用的通知其实挺简单的。重点是找到正确的开关,既能彻底关闭某个App的打扰,也能调整显示方式减少干扰。 关闭特定App的通知权限 这是…

    2026年9月21日
    100
  • 在Java中如何实现线程优先级控制

    Java中线程优先级通过Thread类实现,取值范围1-10,分别对应MIN_PRIORITY、NORM_PRIORITY和MAX_PRIORITY;新线程继承父线程优先级,可通过setPriority()设置;尽管高优先级线程更可能被调度,但执行顺序不保证,因受操作系统影响;应避免依赖优先级控制关…

    2026年9月21日
    000
  • 如何在Java中使用接口实现多继承效果

    Java不支持多继承,但可通过实现多个接口模拟该效果。类可同时实现Flyable、Swimmable等接口,具备多种行为能力,并能利用默认方法复用逻辑,如Loggable提供日志功能。当多个接口含同名默认方法时,需在类中显式重写以解决冲突。接口用于定义“能做什么”,抽象类描述“是什么”,因类只能单继…

    2026年9月21日
    100
  • 万人同时在线抽奖活动架构

    万人同时在线抽奖活动的系统架构应采用微服务架构、分布式数据库、redis缓存、区块链存储结果,并使用负载均衡和异步处理技术。具体包括:1.采用微服务架构和分布式数据库(如tidb)保证系统稳定性和可扩展性;2.使用redis处理抽奖逻辑,确保高效和随机性;3.将结果存入区块链,保证透明度和可验证性;…

    2026年9月21日
    000
  • 小可AI小程序入口链接_小可AI小程序官方地址

    小可AI小程序官方入口为https://xcx.xiaokeai.com.cn,用户可在社交平台搜索使用;平台支持多轮对话、文本生成、图像理解及语音转文字功能,界面简洁、响应迅速,具备历史记录查看与持续优化的智能算法。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepS…

    2026年9月21日
    000
  • Linux文件和目录管理常见命令

    Linux文件和目录管理依赖于ls、cd、mkdir、rm、cp、mv等核心命令,用于浏览、创建、删除、复制和移动文件与目录;通过find、du、grep等命令可查找文件、定位大文件并清理磁盘空间;使用rename、mmv或脚本可实现批量重命名;为安全起见,应谨慎使用rm命令,推荐结合-i选项或使用…

    2026年9月21日
    000
  • 抖店无货源店铺怎么做?无货源运营核心技巧

    如何打造抖店无货源模式:高效运营实战指南 在当前电商快速发展的趋势下,抖店无货源模式正成为众多创业者的首选。这种模式无需自备库存,极大降低了启动成本和经营风险,但在选品、供应链协同和客户服务方面也提出了更高的要求。本文结合有赞平台的实用功能,深入拆解抖店无货源的搭建流程与关键运营策略,助力商家实现低…

    2026年9月21日
    000
  • 大数据量下的批量导入/导出优化

    在大数据环境下优化批量导入/导出的方法包括:1. 使用批处理技术分批导入/导出数据,减少系统资源压力;2. 采用数据流技术如apache kafka进行实时处理,降低内存占用;3. 利用并行处理技术分配任务到多个处理器或节点,提高处理速度;4. 通过性能监控和调优识别并解决瓶颈点,以提升整体效率。 …

    2026年9月21日
    200

发表回复

登录后才能评论
关注微信