热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍

OTA差分算法1.bsdiffBsdiff采用差分文件信息包含三个部分:一是ADD和INSERT的控制信息;一部分是包含概率匹配中不同字节差异文件(difference);最后一部
OTA差分算法

1.bsdiff

Bsdiff采用差分文件信息包含三个部分: 一是ADD和INSERT的控制信息;一部分是包含概率匹配中不同字节差异文件(difference);最后一部分是不属于概率匹配内容的额外信息(extra文件)。Bsdiff算法使用的的前提条件,一是文件直接修改引起的变化相当稀疏,二是数据和代码倾向于成块进行移动,导致大部分不同地址调整了相同的大小。ADD指令操作对象包含源文件中信息的偏移、长度以及需要添加的值;INSERT包含需要添加的长度以及需要添加的信息。区别于Vcdiff,Bsdiff只是差分文件生成的作用,而生成的文件并不会比源文件小,但其具有高度可压缩性,使得压缩后的差分文件比较小,参考文献中使用bzip2的压缩方法。生成差分包的时候,首先对旧文件进行后缀排序(使用faster string matching 算法),然后使用二分查找的方法将新文件中的字符串和旧文件字符串进行比较生成相应的diff文件。而差分包与旧有的文件生成新文件时会简单些,直接利用差分信息进行处理,无需排序等操作。
如下为排序过程,首先按照字符值直接排序,第一次排序完成后,13、6、5的字符顺序即可确定,由于这些字符唯一,而其他出现重叠的字符需要根据其后续字符再次排序,如index3的e和index12的e,需要使用其后续的第一个字符o和$比较再次排序,依次类推,直至将所有的元素排序完成。

《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》

字符串查找方式如下,采用二分法,示例需要找到”obeo”字符串,先找到I index为( 0 + 13 ) / 2的位置6,对应原来字符串的index 10进行字符串比较,obeo > obe$因此再到( 6 + 13 ) / 2的位置即index 9 对应元字符串的index 7,以此类推。通过字符串的查找和比较,从新文件头字符串开始,依次到旧文件排序信息中查找字符串位置进行对比,得到旧文件的位置和匹配长度信息等。

《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》

Bsdiff生成的差分包由几个部分组成,Header文件头、控制信息、diff部分的信息,其中头文件包含目标文件大小、控制信息长度等,以及extra部分信息。通过两种操作ADD、INSERT进行合并。控制信息用三元组表示,由add长度,insert长度以及从旧文件忽略长度三部分表示。执行方式如下

《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》
 

如何安装还有使用命令的教程给列出来。

① 依赖的tar bsdiff-4.3.tar.gz    bzip2-1.0.6.tar.gz

     解压:    tar –zxvf bsdiff-4.3.tar.gz  

      解压:    tar –zxvf bzip2-1.0.6.tar.gz

      重命名:  mv bzip2-1.0.6.tar.gz  bzip2

      将bzip2文件夹移动到 /usr/local/include :   sudo  cp –r bzip2 /usr/local/include

② 依赖的源

       yum install bzip2-devel.x86_64 进行下载

③ 修改 bsdiff-4.3 文件夹里面的 bsdiff.c 和bspatch.c

在include 头里添加

#include “bzip2/bzlib.c”

#include “bzip2/crctable.c”

#include “bzip2/compress.c”

#include “bzip2/decompress.c”

#include “bzip2/randtable.c”

#include “bzip2/blocksort.c”

#include “bzip2/huffman.c”

修改Makefile

 在.ifndef和.endif 前面加一个 tab 空出距离,否则报错

如图:《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》

④准备工作完成以后 进行编译 : make

⑤运行命令

两个绿色的就是编译出的命令行

 差分:  ./bsdiff  [oldfile] [newfile] [patchName]

 合成:  ./bspatch [oldfile] [newFileName] [patchName]

对比文件是否有差异: sha1sum  [fileName] 检验文件完整性和hash值。

 

2.hdiffpatch

① 需要用到的zip包:  HDiffPatch-master.zip , lz4-dev.zip, zstd-dev.zip, lzma-master.zip

②解压

   unzip HDiffPatch-master.zip

   unzip lz4-dev.zip

unzip zstd-dev.zip

unzip lzma-master.zip

③重命名

mv HDiffPatch-master  hdiffpatch

mv lz4-dev  lz4

mv zstd-dev  zstd

mv lzma-master  lzma

在/usr/local/include/bzip2 目录下 将 bzlib.h 复制到 /usr/local/include  

sudo   cp bzlib.h  ../

安装 zlib:  yum install -y zlib zlib-devel (如果是ubuntu的话 需要  apt-get install zlib1g zlib1g-dev)

④ 编译: make

《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》

成功结果:

《OTA差分算法(bsdiff,hdiffpatch,xdelta3)和OTA升级update.zip介绍》

2.1使用命令

执行命令的语法:

   拆分: ./hdiffz [oldFile] [newFile] [patchName]

   合成: ./hpatchz [oldFile] [patchName][oldFileName]

命令详解:

hdiffz [-m[-matchScore]|-s[-matchBlockSize]]  [-c-compressType[-compressLevel]] [-o] oldFile newFile outDiffFile

hpatchz  [-m|-s[-s-cacheSize]]  [-o] oldFile diffFile outNewFile

建议命令参数:

      hdiffz run by: -s-128 -c-bzip2-9 [oldFile] [newFile] [outDiffFile]
    hpatchz run by: -s-4m [oldFile] [diffFile] [outNewFile]
对比文件是否有差异: sha1sum  [fileName] 检验文件完整性和hash值。

2.2hdiffz 参数简介

-m matchScore使用matchScore将所有文件加载到内存中,默认是difffileSize 这种方式不推荐.

-s-matchBlockSize所有文件加载为流式处理,参数由matchBlockSize决定,参数设置例如 128 128K 128M

默认128,建议32 – 16K 64K 1M等。

特殊选项:

C-压缩-压缩能级,设置差分文件的压缩类型和级别,默认不压缩;

支持压缩类型和级别:

(参考:HTTPS://Github. COM/SISON/LZTAMP/BROB/MARST/LZTAT17171SORTED.D)

       -zlib[-{1..9}]              DEFAULT level 9

        -bzip2[-{1..9}]             DEFAULT level 9
        -lzma[-{0..9}[-dictSize]]   DEFAULT level 7
            dictSize(==decompress stream size) can like 4096 or 4k or 4m or 128m etc…, DEFAULT 4m
        -lz4                         no level
        -lz4hc[-{3..12}]            DEFAULT level 11
        -zstd[-{0..22}]             DEFAULT level 20

2.3hpatchz 参数简介

内存参数:

  -m  旧文件全部加载入内存

  -s-cacheSize  旧文件以流的方式加载。

  cacheSize can like 262144 or 256k or 512m or 2g etc…, DEFAULT 128m

 

3.Xdelta3

依赖的包 xdelta-gpl-release3_1.zip
解压,unzip xdelta-gpl-release3_1.zip
cd   xdelta-gpl-release3_1/xdelta3

在当前文件夹执行命令:   ./run_release.sh

                                        autoscan .

                                        aclocal

                                        autoheader

                                        automake –add-missing

                                       ./configure

                                       make

3.1命令

执行命令的方法

拆分 :  ./xdelta3 -v -e -s [oldFile] [newFile] [patchName]

合成 :  ./xdelta3 -v -d -s [oldFile] [patchName] [newFileName]

对比文件是否有差异: sha1sum  [fileName]  检验文件完整性和hash值。

命令详细参数

用法: xdelta3 [命令/选项] [input [output]]

特殊命令名:

    config      输出 xdelta3 配置信息

    decode      解压缩 input

    encode      压缩 input

    test        运行内置的测试

为 VCDIFF 输入所用的特殊命令:

    printdelta  输出整个变化的信息

    printhdr    输出第一个窗口的信息

    printhdrs   输出所有窗口的信息

标准选项:

   -0 .. -9     压缩等级

   -c           使用 stdout

   -d           解压缩

   -e           压缩

   -f           强制覆盖

   -h           显示帮助

   -q           静默模式

   -v           使用详细信息(最大2)

   -V           显示版本

内存选项:

   -B bytes     源窗口大小

   -W bytes     输入窗口大小

压缩选项:

   -s source    如果存在,选择来源文件从哪儿复制

   -S [djw|fgk] 启用/弃用二级压缩

   -N           弃用小字符串匹配压缩

   -D           弃用外部解压缩 (压缩/解压缩)

   -R           弃用外部重压缩 (压缩)

   -n           弃用校验 (压缩/解压缩)

   -C           软配置 (压缩, 无文档的)

   -A [apphead] 弃用/提供程序头部 (压缩)

 

4.系统环境 系统:centos7

 

OTA升级中关于update.zip包的一些总结

update.zip包整理
一、 update.zip包的目录结构
          |—-boot.img
          |—-system/
          |—-recovery/
                `|—-recovery-from-boot.p
                `|—-etc/
                        `|—-install-recovery.sh
          |—META-INF/
              `|CERT.RSA
              `|CERT.SF
              `|MANIFEST.MF
              `|—-com/
                     `|—-google/
                             `|—-android/
                                    `|—-update-binary
                                    `|—-updater-script
                             `|—-android/
                                    `|—-metadata
二、文件描述

boot.img 包含kernel和ramdisk,用来更新boot分区所需要的文件
system 内容升级后放在系统的system分区,主要更新一些apk和so库
recovery/目录中的recovery-from-boot.p是boot.img和recovery.img的补丁(patch),主要用来更新recovery分区,其中etc/目录下的install-recovery.sh是更新脚本
update-binary是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中的描述的操作,该文件在具体的更新包中名字由源码中bootable/recovery/install.c中的
ASSUMED_UPDATE_BINARY_NAME的值而定
updater-script:此文件是一个脚本文件,具体描述更新过程,在之前可以根据具体情况编写该脚本适应我们的具体需求,该文件的命名由源码中的bootable/recovery/ 
updater/updater.c文件中的SCRIPT_NAME的值而定
metadata文件是描述设备信息及环境变量的元数据,主要包括一些编译选项,签名公钥,时间戳以及设别型号等
uesrdata目录,用来更新系统中的用户数据部分,这部分内容在更新后会存放在系统的data目录下,但是在Android M后data分区默认加密,在OTA的时候不能挂载data分
区,所以不能进行更新
在update.zip包生成后需要对其进行签名,否则在升级时会出现认证失败的错误提示。而且签名要使用和目标版本一致的加密公钥。加密公钥以及加密所需的三个文件在Android源码编译后生成的具体路径为:
out/host/linux-x86/framework/signapk.jar
build/target/product/security/testkey.x509.pem
build/target/product/security/testkey.pk8
具体的加密方法:$ java -jar out/host/linux-x86/framework/signapk.jar -w build/traget/product/security/tesetkey.x509.pem build/target/product/security/testkey.pk8 update.zip update_signed.zip
MANIFEST.MF :这个manifest文件定义了与包的组成结构相关的数据,类似Android应用的manifest.xml文件
CERT.RSA:与签名文件相关联的签名程序块文件,他存储了用于签名jar文件的公共签名
CERT.SF:这是jar文件的签名文件,其中前缀CERT代表签名者
在具体升级的时候,对update.zip包检查时大致会分成三步:1.检验SF文件与RSA文件是否匹配。2、检验MANIFEST.MF与签名文件中的digest是否一致。3.检验包中的文件与MANIFEST中所描述的是否一致。

三、Android升级包update.zip的生成过程分析(全部系统包)
使用make otapackage命令生成update.zip的过程分析。
在源码根目录下执行make otapackage命令生成update.zip包主要分为两步,

第一步是根据Makefile执行编译生成一个update原包(zip格式)。

第二步是运行一个python脚本,并以上一步准备的zip包作为输入,最终生成我们需要的升级包。

下面进一步分析这两个过程。(简单来说就是,首先生成cota包,在利用cota包生成差分包和整包)
            第一步:编译Makefile。对应的Makefile文件所在位置:build/core/Makefile。从该文件会生成一个zip包,这个包最后会用来制作OTA package 或者filesystem image。
根据Makefile可以分析这个包的生成过程:
            首先创建一个root_zip根目录,并依次在此目录下创建所需要的如下其他目录
            ①创建RECOVERY目录,并填充该目录的内容,包括kernel的镜像和recovery根文件系统的镜像。此目录最终用于生成recovery.img。
            ②创建并填充BOOT目录。包含kernel和cmdline以及pagesize大小等,该目录最终用来生成boot.img。
            ③向SYSTEM目录填充system image。
            ④向DATA填充data image。
            ⑤用于生成OTA package包所需要的额外的内容。主要包括一些bin命令。
            ⑥创建META目录并向该目录下添加一些文本文件,如apkcerts.txt(描述apk文件用到的认证证书),misc_info.txt(描述Flash内存的块大小以及boot、recovery、system、userdata等分区的大小信息)。
            ⑦使用保留连接选项压缩我们在上面获得的root_zip目录。
            ⑧使用fs_config(build/tools/fs_config)配置上面的zip包内所有的系统文件(system/下各目录、文件)的权限属主等信息。fs_config包含了一个头文件#include“private/android_filesystem_config.h”。在这个头文件中以硬编码的方式设定了system目录下各文件的权限、属主。执行完配置后会将配置后的信息以文本方式输出 到META/filesystem_config.txt中。并再一次zip压缩成我们最终需要的原始包。

           第二步:上面的zip包只是一个编译过程中生成的原始包。这个原始zip包在实际的编译过程中有两个作用,一是用来生成OTA update升级包,二是用来生成系统镜像。在编译过程中若生成OTA update升级包时会调用(具体位置在Makefile的1037行到1058行)一个名为ota_from_target_files的Python脚本,位置在/build/tools/releasetools/ota_from_target_files。这个脚本的作用是以第一步生成的zip原始包作为输入,最终生成可用的OTA升级zip包。

./ota_from_target_files  -p . -s “../../../../device/qcom/common”, -t $1 -v -d MTD -v -i ./v1/targetfiles.zip  ./v2/targetfiles.zip update.zip
用法—Usage: ota_from_target_files [flags] input_target_files output_ota_package
                        -b 过时的。
                        -k签名所使用的密钥
                        -i生成增量OTA包时使用此选项。后面我们会用到这个选项来生成OTA增量包。
                        -w是否清除userdata分区
                        -n在升级时是否不检查时间戳,缺省要检查,即缺省情况下只能基于旧版本升级。
                        -e是否有额外运行的脚本
                        -m执行过程中生成脚本(updater-script)所需要的格式,目前有两种即amend和edify。对应上两种版本升级时会采用不同的解释器。缺省会同时生成两种格式的脚 本。
                        -p定义脚本用到的一些可执行文件的路径。
                        -s定义额外运行脚本的路径。
                        -x定义额外运行的脚本可能用的键值对。
                        -v执行过程中打印出执行的命令。
                        -h命令帮助

下面我们分析ota_from_target_files这个python脚本是怎样生成最终zip包的。
                      主函数main是python的入口函数,我们从main函数开始看,大概看一下main函数(脚本最后)里的流程就能知道脚本的执行过程了。
                       ① 在main函数的开头,首先将用户设定的option选项存入OPTIONS变量中,它是一个python中的类。紧接着判断有没有额外的脚本,如果有就读入到OPTIONS变量中。
                       ② 解压缩输入的zip包,即我们在上文生成的原始zip包。然后判断是否用到device-specific extensions(设备扩展)如果用到,随即读入到OPTIONS变量中。
                       ③ 判断是否签名,然后判断是否有新内容的增量源,有的话就解压该增量源包放入一个临时变量中(source_zip)。自此,所有的准备工作已完毕,随即会调用该 脚本中最主要的函数WriteFullOTAPackage(input_zip,output_zip)
                       ④ WriteFullOTAPackage函数的处理过程是先获得脚本的生成器。默认格式是edify。然后获得metadata元数据,此数据来至于Android的一些环境变量。然后获得设备配置参数比如api函数的版本。然后判断是否忽略时间戳。
                       ⑤ WriteFullOTAPackage函数做完准备工作后就开始生成升级用的脚本文件(updater-script)了。生成脚本文件后将上一步获得的metadata元数据写入到输出包out_zip。
                       ⑥至此一个完整的update.zip升级包就生成了。将升级包拷贝到SD卡中就可以用来升级了。

四、 Android OTA增量包update.zip的生成
在上面的过程中生成的update.zip升级包是全部系统的升级包,而在实际升级中,我们只希望能够升级我们改变的那部分内容。这就需要使用增量包来升级。生成增量包的过程也需要上文中提到的ota_from_target_files.py的参与。
         下面是制作update.zip增量包的过程。
          ① 在源码根目录下依次执行下列命令
           $ . build/envsetup.sh
           $ lunch XXX
           $ make
           $ make otapackage
           执行上面的命令后会在out/target/product/tcc8800/下生成我们第一个系统升级包。我们先将其命名为A.zip
          ② 在源码中修改我们需要改变的部分,比如修改内核配置,增加新的驱动等等。修改后再一次执行上面的命令。就会生成第二个我们修改后生成的update.zip升级包。将其命名为B.zip
          ③ 在上文中我们看了ota_from_target_files.py脚本的使用帮助,其中选项-i就是用来生成差分增量包的。使用方法是以上面的A.zip 和B.zip包作为输入,以update.zip包作为输出。生成的update.zip就是我们最后需要的增量包
              具体使用方式是:将上述两个包拷贝到源码根目录下,然后执行下面的命令。
              $ ./build/tools/releasetools/ota_from_target_files -i A.zip B.zip update.zip。(还可以加入其他参数,例如-x,-k,-p等等)
              在执行上述命令时会出现未找到recovery_api_version的错误。原因是在执行上面的脚本时如果使用选项i则会调用WriteIncrementalOTAPackage会从A包和B包中的META目录下搜索misc_info.txt来读取recovery_api_version的值。但是在执行make  otapackage命令时生成的update.zip包中没有这个目录更没有这个文档。
              此时我们就需要使用执行make otapackage生成的原始的zip包。这个包的位置在out/target/product/XX/obj/PACKAGING/target_files_intermediates/目录下(cota包),它是在用命令make otapackage之后的中间生产物,是最原始的升级包。我们将两次编译的生成的包分别重命名为A.zip和B.zip,并拷贝到SD卡根目录下重复执行上面的命令:  $ ./build/tools/releasetools/ota_form_target_files -i A.zip B.zip update.zip。
           
另:生成差分包调用的是文件./build/tools/releasetools/ota_from_target_files中的WriteIncrementalOTA方法,调用时需要将两个版本的差分资源包作为参数传进来,形如:
./build/tools/releasetools/ota_from_target_files –n –i ota_v1.zip ota_v2.zip update.zip
其中,参数n表示忽略时间戳;i表示生成增量包(即差分包);ota_v1.zip与ota_v2.zip分别代表前后两个版本的差分资源包;而update.zip则表示最终生成的差分包。
WriteIncrementalOTA函数会计算输入的两个差分资源包中版本的差异,并将其写入到差分包中;同时,将updater及生成脚本文件udpate-script添加到升级包中。


推荐阅读
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在Linux系统中,通过使用`read`和`write`函数可以实现文件的高效复制操作。`open`函数用于打开或创建文件,其返回值为文件描述符,成功时返回一个有效的文件描述符,失败时返回-1。`path`参数指定了要操作的文件路径,而`oflag`参数则定义了文件的打开模式和属性。此外,为了确保数据的完整性和一致性,还需要合理处理文件读取和写入过程中的错误和异常情况。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 深入解析C#中app.config文件的配置与修改方法
    在C#开发过程中,经常需要对系统的配置文件进行读写操作,如系统初始化参数的修改或运行时参数的更新。本文将详细介绍如何在C#中正确配置和修改app.config文件,包括其结构、常见用法以及最佳实践。此外,还将探讨exe.config文件的生成机制及其在不同环境下的应用,帮助开发者更好地管理和维护应用程序的配置信息。 ... [详细]
  • 如何在Linux系统中实现Windows风格的桌面环境:将Ubuntu 18.04定制为Windows主题界面
    如果您是从Windows转到Linux系统的用户,可能会觉得默认的Ubuntu主题和桌面环境缺乏吸引力和可定制性。尤其是对于习惯了Windows风格的任务栏和主题的用户,Ubuntu 18.04的橙色主题可能显得过于简洁。为了提升用户体验,可以通过安装特定的桌面环境和主题来实现类似Windows的界面效果。本文将详细介绍如何在Ubuntu 18.04中配置和定制桌面环境,使其具备Windows风格的外观和功能。 ... [详细]
  • 在洛谷 P1344 的坏牛奶追踪问题中,第一问要求计算最小割,而第二问则需要找到割边数量最少的最小割。通过为每条边附加一个单位权值,可以在求解最小割时优先选择边数较少的方案,从而同时解决两个问题。这种策略不仅简化了问题的求解过程,还确保了结果的最优性。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 使用 `git stash` 可以将当前未提交的修改保存到一个临时存储区,以便在后续恢复工作目录时使用。例如,在处理中间状态时,可以通过 `git stash` 命令将当前的所有未提交更改推送到一个新的储藏中,从而保持工作目录的整洁。此外,本文还将详细介绍如何解决 `git stash pop` 时可能出现的冲突问题,帮助用户高效地管理代码变更。 ... [详细]
  • 寒假作业解析:第三周 2月12日 第7题
    尽快完成之前的练习任务!每日一练2.1 Problem A Laurenty and Shop 的题目要求是选择两条不同的路线以最小化总的等待时间。简要分析:通过对比不同路线的等待时间,可以找到最优解。此问题可以通过动态规划或贪心算法来解决,具体取决于路线的复杂性和约束条件。 ... [详细]
  • 2018年9月21日,Destoon官方发布了安全更新,修复了一个由用户“索马里的海贼”报告的前端GETShell漏洞。该漏洞存在于20180827版本的某CMS中,攻击者可以通过构造特定的HTTP请求,利用该漏洞在服务器上执行任意代码,从而获得对系统的控制权。此次更新建议所有用户尽快升级至最新版本,以确保系统的安全性。 ... [详细]
  • 本文深入解析了C++中`while`循环的使用方法及其应用场景,包括计数控制和时间控制两种主要类型。通过具体的代码示例,详细介绍了如何利用`while`循环实现精确的计数控制和灵活的时间控制,帮助读者更好地理解和掌握这一重要的编程结构。此外,文章还探讨了`while`循环在实际开发中的常见用法和优化技巧,为初学者提供了宝贵的实践经验。 ... [详细]
  • 在Ubuntu系统中配置Python环境变量是确保项目顺利运行的关键步骤。本文介绍了如何将Windows上的Django项目迁移到Ubuntu,并解决因虚拟环境导致的模块缺失问题。通过详细的操作指南,帮助读者正确配置虚拟环境,确保所有第三方库都能被正确识别和使用。此外,还提供了一些实用的技巧,如如何检查环境变量配置是否正确,以及如何在多个虚拟环境之间切换。 ... [详细]
  • 哈希表(Hash Table)是一种高效的查找算法,与传统的链表和树结构相比,其在查找过程中无需进行逐个元素的比较。本文将深入探讨哈希表的基本原理、应用场景以及优化策略,帮助读者全面理解其在实际开发中的优势和局限性。通过实例分析和代码示例,我们将展示如何有效利用哈希表提高数据处理效率,并解决常见的冲突问题。 ... [详细]
author-avatar
Yyao
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有