0

0

揭秘 CPF 和 CNPJ 校验位算法:清晰简洁的方法

PHPz

PHPz

发布时间:2024-09-04 08:46:03

|

739人浏览过

|

来源于dev.to

转载

揭秘 cpf 和 cnpj 校验位算法:清晰简洁的方法

我清楚地记得我在本科学习期间第一次接触cpf(巴西id)验证算法。在申请米纳斯吉拉斯州联邦大学 ufmg 精确科学研究所实习时,我们被要求手写一段 java 代码,在简单解释算法后验证 cpf 校验位。

从那时起,我在不同的专业环境中多次遇到这个问题,经常求助于从互联网复制解决方案并添加一些单元测试。然而,每次,我都会对这些解决方案中反复出现的问题感到震惊。它们往往更植根于命令式范例,而不是预期的 java 代码面向对象方法。但是,更让我困扰的是,这些实现带来的高认知负荷使得阅读和理解代码的意图变得不切实际。

尚未需要实现此代码的感兴趣的开发人员可以轻松找到任何编程语言的解决方案。然而,它们都倾向于以相同的方式呈现:对 cpf 校验位如何实现的解释的简单复制。似乎很少有人花时间去理解这种方法背后的原因。

碰撞问题

在软件开发中,哈希码算法中经常会遇到碰撞避免的概念,特别是在使用素数模的情况下。 cpf(巴西id)和cnpj(巴西公司id)中的校验位功能类似,重点是避免冲突。这确保了简单的数字求和不会错误地验证不正确的条目,因为多种组合可以产生相同的总和。

为了缓解这种情况,常见的做法是应用加权和,将每个数字乘以一个特定的因子。您可以将其视为将数字沿一条线展开;乘法使得多个数字不太可能出现在同一位置。那么,数字在数字中的位置决定了它的权重,这是有道理的。

为了进一步增强可靠性并最大限度地降低碰撞风险,总和以 11 为模,然后从相同的素数中减去该结果。为了确保校验位仍然是个位数,10 和 11 的结果将转换为 0。

认知负荷

用于计算 cpf 和 cnpj 校验位的算法可能很难理解。虽然算法背后的总体动机可能很清楚,但掌握每个部分的具体作用通常具有挑战性。出现这种复杂性的部分原因是计算涉及一系列数学计算,这些计算通常集中在一个单一的大型方法中。此外,通常以莫名其妙的数组形式呈现的权重可能显得不合逻辑。

绘蛙
绘蛙

电商场景的AI创作平台,无需高薪聘请商拍和文案团队,使用绘蛙即可低成本、批量创作优质的商拍图、种草文案

下载

为了解决这个问题,我专注于减少缺乏自我解释的代码量。通过坚持单一职责原则(solid 中的“s”),我努力创建更简单、更易于理解的方法。我还努力通过有意义的变量名称来定义关键概念,旨在在代码库中建立一种普遍存在的语言。通过这种方法,我试图找出用于 cpf 校验位的方法与用于 cnpj 的方法的区别,因为需要一种方法的软件通常需要另一种方法。代码的核心功能如下所示,另外,要进一步查看,包括完整的代码和相关的单元测试,请访问我的 github 存储库。

  private string getcheckdigits(string document, int maxweight) {
    final int lengthwithoutcheckdigits = getbasedigitslength(document);

    int firstweightedsum = 0;
    int secondweightedsum = 0;
    for (int i = 0; i < lengthwithoutcheckdigits; i++) {
      final int digit = character.getnumericvalue(document.charat(i));
      final int maxindex = lengthwithoutcheckdigits - 1;
      final int reverseindex = maxindex - i;
      firstweightedsum += digit * calculateweight(reverseindex, maxweight);
      // index is incremented, starting from 3, skipping first check digit.
      // the first part will be added later as the calculated first check digit times its corresponding weight.
      secondweightedsum += digit * calculateweight(reverseindex + 1, maxweight);
    }

    final int firstdigit = getcheckdigit(firstweightedsum);
    // add the first part as the first check digit times the first weight.
    secondweightedsum += min_weight * firstdigit;
    final int seconddigit = getcheckdigit(secondweightedsum);

    return string.valueof(firstdigit) + seconddigit;
  }

  private int calculateweight(int complementaryindex, int maxweight) {
    return complementaryindex % (maxweight - 1) + min_weight;
  }

  private int getcheckdigit(int weightedsum) {
    final var checkdigit = enhancecollisionavoidance(weightedsum);
    return checkdigit > 9 ? 0 : checkdigit;
  }

  private int enhancecollisionavoidance(int weightedsum) {
    final var weightsumlimit = 11;
    return weightsumlimit - weightedsum % weightsumlimit;
  }

将cnpj和cpf的校验位计算结果与网上找到的典型解决方案进行比较:

public class ValidaCNPJ {

  public static boolean isCNPJ(String CNPJ) {
// considera-se erro CNPJ's formados por uma sequencia de numeros iguais
    if (CNPJ.equals("00000000000000") || CNPJ.equals("11111111111111") ||
        CNPJ.equals("22222222222222") || CNPJ.equals("33333333333333") ||
        CNPJ.equals("44444444444444") || CNPJ.equals("55555555555555") ||
        CNPJ.equals("66666666666666") || CNPJ.equals("77777777777777") ||
        CNPJ.equals("88888888888888") || CNPJ.equals("99999999999999") ||
       (CNPJ.length() != 14))
       return(false);

    char dig13, dig14;
    int sm, i, r, num, peso;

// "try" - protege o código para eventuais erros de conversao de tipo (int)
    try {
// Calculo do 1o. Digito Verificador
      sm = 0;
      peso = 2;
      for (i=11; i>=0; i--) {
// converte o i-ésimo caractere do CNPJ em um número:
// por exemplo, transforma o caractere '0' no inteiro 0
// (48 eh a posição de '0' na tabela ASCII)
        num = (int)(CNPJ.charAt(i) - 48);
        sm = sm + (num * peso);
        peso = peso + 1;
        if (peso == 10)
           peso = 2;
      }

      r = sm % 11;
      if ((r == 0) || (r == 1))
         dig13 = '0';
      else dig13 = (char)((11-r) + 48);

// Calculo do 2o. Digito Verificador
      sm = 0;
      peso = 2;
      for (i=12; i>=0; i--) {
        num = (int)(CNPJ.charAt(i)- 48);
        sm = sm + (num * peso);
        peso = peso + 1;
        if (peso == 10)
           peso = 2;
      }

      r = sm % 11;
      if ((r == 0) || (r == 1))
         dig14 = '0';
      else dig14 = (char)((11-r) + 48);

// Verifica se os dígitos calculados conferem com os dígitos informados.
      if ((dig13 == CNPJ.charAt(12)) && (dig14 == CNPJ.charAt(13)))
         return(true);
      else return(false);
    } catch (InputMismatchException erro) {
        return(false);
    }
  }
}

这段代码仅供cnpj使用!

结论

虽然结果代码可能显得有些冗长,但我对清晰度和自我解释的强调导致了我满意的结果。代码设计得更加直观,对其正确性提供了更大的信心,而且大多数核心功能无需向下滚动页面即可可见。

我欢迎任何进一步改进的建议,因此请随时分享您的反馈。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

github中文官网入口 github中文版官网网页进入
github中文官网入口 github中文版官网网页进入

github中文官网入口https://docs.github.com/zh/get-started,GitHub 是一种基于云的平台,可在其中存储、共享并与他人一起编写代码。 通过将代码存储在GitHub 上的“存储库”中,你可以: “展示或共享”你的工作。 持续“跟踪和管理”对代码的更改。

4237

2026.01.21

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

497

2023.08.14

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

1

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

41

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

171

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

50

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

90

2026.03.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 4.2万人学习

Git版本控制工具
Git版本控制工具

共8课时 | 1.6万人学习

Git中文开发手册
Git中文开发手册

共0课时 | 94人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号