介绍在计算机科学里, Hash函数, 也就是大家说的散列函数, 这种函数能把放进来的输入数据, 变成固定长度的、叫做散列值或者是哈希值的东西。现在有了很多强大的、也很灵活的Hash函数, 大家用它们能在好多不同的应用场合里, 把数据存储搞定, 把数据校验做好, 还能做加密这类工作, 用处挺大的。这篇文章会从最基础的地方开始讲起, 一直讲到比较深度的使用方面, 目的就是让大家能全面地了解中文字符串相关的这类型函数的使用方法。目录里提到了什么是哈希函数、内置哈希函数、常见的hash算法以及hash在数据结构中的应用, 另外还涉及了使用hash进行数据校验、安全性和冲突的处理问题, 还有哈希表的实现过程、哈希集合和哈希映射的使用方法, 也讲了如何使用hash进行加密以及如何通过hash来判断用户上传的文本文件是否重复, 接下来我们会讨论一点, 那就是第一点, 也就是什么是hash函数这一方面的问题。Hash函数是一种算法, 这种算法把输入的东西给变成固定的大小的输出结果, 输入的可以是任意长的东西, 输出的大小通常是小的, 那个出来以后固定的长度就叫作哈希值, 这个Hash函数具备下面的这些特性, 就是需要使用hash()函数来去计算哈希值。 hash(42)计算字符串Hello!的哈希值。等于哈希值是左括号一, 逗号空格二, 逗号空格三右括号。print(fHash value of 42: {})现在要开始打印的, 是那个名为Hello的内容所对应的值的哈希数据的值了, 然后在这里加一个大括号作为占位符。用print函数把元组的一二三的哈希值打印出去, 看看具体的数字结果是多少。## 3. 常见的Hash算法Python中常见的Hash算法包括MD5Message Digest Algorithm 5、SHA-1Secure Hash Algorithm 1和SHA-256等。这些算法被广泛用于数据校验、数据完整性验证和密码学中。首先我们需要导入Python的hashlib模块pythonimport hashlib3.第一步, 我们要去使用那个名为MD5的算法机制, 然后通过它来计算出对应的Hash值。MD5这个算法, 它会把那种长度的任意输入啊进行一番操作, 最终转换成128位的哈希值。可是, 由于MD5这安全性不太行, 现在大家都不再推荐在那些对安全要求很高的敏感场景里去使用它了。data Hello, MD5!# 创建MD5对象md5_obj hashlib.md5()# 更新哈希对象以使用字符串md5_obj.update(data.encode())# 获取MD5哈希值md5_hash md5_obj.hexdigest()print(fMD5 Hash of {data}: {md5_hash})3.第二步是执行计算Hash值这一系列操作,具体的做法是采用SHA-256这个算法来进行处理。现在的人直接能看懂的话这么说, 那个叫SHA-256的算法, 它会把不管什么长度的输入内容, 都给转换成那种固定是二百五十六位的一个哈希值出来, 因为这就提供了比别的更厉害的安全性存在, 所以这算是在那些个数据校验操作还有加密使用场景里面, 更适合去被大家选去用掉的一种情况。data Hello, SHA-256!# 创建SHA-256对象sha256_obj hashlib.sha256()# 更新哈希对象以使用字符串sha256_obj.update(data.encode())# 获取SHA-256哈希值sha256_hash sha256_obj.hexdigest()print(fSHA-256 Hash of {data}: {sha256_hash})4. 在当下这些个数据结构里头, 这个Hash, 它是被广泛地应用到了非常多的场合里面去的。在数据结构这一学问里面, Hash函数经常被用到, 目的就是把Hash表也就是我们常说的散列表给实现出来。那么Hash表究竟是个什么东西? 它是一种专门用来存放键值对的数据结构。这种结构有什么好处? 它能够让你在插入数据、查找数据或者是删除数据的时候, 把时间复杂度维持在常量级别, 也就是说这些操作都能在常数时间内完成。在其中, 能够选用字典这种数据结构来完成哈希表的构建。所谓字典, 指的是一种由键值对搭配组成的集合, 并且这个集合不具有次序性。作为该集合中的键, 需要具备可以被哈希计算的特性, 也就是说它必须属于一种可哈希的数据类型范畴。# 创建一个字典person {-- --name: John Doe,age: 30,email: johnexample.com}# 添加新的键值对person[city] New York# 获取键对应的值print(Name:, person[name])print(Age:, person[age])print(Email:, person[email])print(City:, person.get(city, City not found))# 删除键值对del person[email]# 检查键是否存在if email in person:print(Email found.)else:print(Email not found.)5. 使用Hash进行数据校验在数据完整性验证这个应用领域里面, Hash函数是被经常使用到的一个工具, 它的一个核心作用就是用于确认数据在整个传输的过程当中以及整个存储的过程当中, 是否出现了被篡改的情况, 针对这种具体的应用场景, 我们首先需要去执行的操作是计算出原始数据对应的哈希值是什么, 然后我们将刚刚计算出来的这个哈希值, 拿去和接收端那边接收到的数据的哈希值进行直接比较以判断一致性。import hashlibdef calculate_hash(data):# 创建SHA-256对象sha256_obj hashlib.sha256()# 更新哈希对象以使用数据sha256_obj.update(data)# 获取SHA-256哈希值return sha256_obj.digest()# 原始数据original_data bHello, Hash!# 计算原始数据的哈希值original_hash calculate_hash(original_data)# 模拟数据传输或存储过程中数据被篡改tampered_data bHello, Tampered Hash!# 计算篡改后数据的哈希值tampered_hash calculate_hash(tampered_data)# 对比哈希值if original_hash tampered_hash:print(Data integrity verified: Data is unchanged.)else:print(Data integrity compromised: Data has been tampered with.)6. 这涉及到关于安全性能以及发生矛盾时候的具体状况。Hash函数的安全性, 就是指说, 当给定了那个哈希值之后, 人是很困难去找到那个对应着的原始输入数据的。再一个方面来说, 如果不一样的输入数据, 产生了相同的那个哈希值这种情况的话, 这个在术语上就被称为是哈希冲突了。在一般的日常情况之下, 哈希算法基本上都会被设计成能够去抵抗预像攻击以及第二像攻击这两个方面。那么这里的预像攻击主要就是说要去找到那个原先的原始数据, 而第二像攻击呢则是说要去找另外的某一个别的数据, 但是这个别的数据和那个给定的哈希值是一模一样的。然而, 完全避免哈希冲突是不可能的。好的Hash算法应该使冲突发生的概率尽可能小。7. Hash表的实现在之前的那些案例里面, 咱们一直都用着字典这个东西去给大家展示Hash表到底是怎么个回事的。接下来嘛, 我们就稍微花点时间来搞清楚一下这个Hash表的内部运作原理究竟是怎么回事。Hash表的核心逻辑是采用这样的方式, 也就是借助于Hash函数把键转化为一个索引数字, 紧接着就把包含有键和值的这一组数据安置在由该索引所定位的那个存储位置上面。当用户需要去获取某个特定键所关联的数值的时候, 只需要调用一下Hash函数来锁定正确的索引指向, 这样就可以实现迅速的取值操作。这个字典把哈希表该有的那些功能全都实现出来了, 它还采用了开放定址那个法子去解决哈希冲突的问题, 而且它还会盯着实际需求去看情况动态去把表的大小给调一下, 目的就是为了能够一直保持着好的性能状态。8. 哈希集合和哈希映射在中, 除了字典这是哈希映射的一种实现方式之外 , 还有集合它被视为一种哈希集合的数据类型。所谓集合, 指的其实就是由一组没有顺序之分且彼此互不重复的元素所组成的整体。# 创建一个哈希集合fruits {apple, banana, orange, apple, grape}print(Fruits:, fruits) # 输出{orange, banana, grape, apple}哈希集合的底层实现与哈希映射是类似的, 然而呢, 哈希集合这个玩意仅仅是单纯地去存储那些所谓的键而已, 至于对应的值方面, 那是一丁点都没有去进行保存操作的。9. 使用Hash进行加密Hash函数除了数据校验之外, 它还广泛地被应用到了密码学里边的密码哈希当中, 在存储用户密码的那些场景下面, 我们通常是不会直接把原始的密码存起来的, 我们会把它计算成哈希值之后, 再去进行存储, 这样子的话, 就算数据库发生了泄漏的状况, 那些攻击者也还是没法子很容易就拿到了用户的真实密码。import hashlibdef hash_password(password):# 创建SHA-256对象sha256_obj hashlib.sha256()# 更新哈希对象以使用密码sha256_obj.update(password.encode())# 获取SHA-256哈希值return sha256_obj.hexdigest()# 用户注册时设置密码user_password my_secret_passwordhashed_password hash_password(user_password)# 模拟登录验证input_password input(Enter your password: )input_hashed_password hash_password(input_password)if input_hashed_password hashed_password:print(Login successful.)else:print(Invalid password. Please try again.)10.你可以通过计算用户上传的文本文件的哈希值, 然后利用这个哈希值来对比, 从而判断该文件是否存在重复。为了判断用户上传的文本文件是不是存在重复的情况, 我们可以依靠Hash值的唯一性这个特性来达成目标。当用户上传一个文本文件的时候, 第一步需要做的事情就是计算出该文件的Hash值。然后, 将这个新计算出来的Hash值和之前上传过的那些文件的Hash值进行对比操作。一旦发现这两个文件所对应的Hash值是相同的状况出现, 这就意味着这两份文件很有可能是一模一样的内容, 也就是被用户进行了重复上传这一行为。下面提供了一个非常简易的示范代码, 以此来达成上述目的。import hashlibdef calculate_file_hash(file_path):# 创建SHA-256对象sha256_obj hashlib.sha256()# 以二进制方式读取文件内容避免文本编码问题with open(file_path, rb) as file:while chunk : file.read(8192): # 每次读取8KB数据sha256_obj.update(chunk)# 获取文件的SHA-256哈希值return sha256_obj.hexdigest()def is_file_duplicate(file_path, known_hashes):file_hash calculate_file_hash(file_path)return file_hash in known_hashes# 已知的Hash值集合用于存储之前上传文件的Hash值known_hashes set()# 假设用户上传了两个文本文件file1_path path/to/uploaded_file1.txtfile2_path path/to/uploaded_file2.txt# 检查文件1是否重复if is_file_duplicate(file1_path, known_hashes):print(File 1 is a duplicate.)else:print(File 1 is unique.)known_hashes.add(calculate_file_hash(file1_path))# 检查文件2是否重复if is_file_duplicate(file2_path, known_hashes):print(File 2 is a duplicate.)else:print(File 2 is unique.)known_hashes.add(calculate_file_hash(file2_path))在这段代码内部, 那个特定的函数是用来算出文件对应的SHA-256哈希值的, 而另外一个专门的函数则是用来判别该文件存不存在重复现象的, 因为它会执行比对操作, 也就是把当前这个文件的Hash值和之前已经存在的那个Hash值集合拿来比较。用户要注意, 要是用户上传的文件数量很多的话, 已知的Hash值集合可能就会出现体积特别大的情况。所以在实际的工作场景里面, 很可能需要把已知的Hash值放到数据库里面去存储, 这样做的目的是为了能够实现效率更高的查找操作和处理更快速的比较流程。结论Hash函数这种工具, 它的用处特别广, 大到结构数据的搭建, 小到数据对错的查验, 再到密码领域的工作, 它都能发挥很关键的作用。所以呢, 每个搞开发的人, 必须得把使用Hash函数的本事练到位, 这是一项不能少的基本功。不管你是处在搭建数据结构这个环节, 还是在进行数据校对这项工作, 亦或是在搞定密码和加密任务的时候, Hash函数都可以拿来帮你完成那些既高效又安全的解决办法。