讲师中心 微信公众号
AI工具推荐 视频效率加速

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

阿墨君_9952

阿墨君_9952

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

|

779人浏览过

|

来源于dev.to

转载

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

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

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

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

碰撞问题

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

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

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

Git Factory
Git Factory

为每个编码任务配置并管理独立的 Git worktree,支持并行分支和清晰的 PR 工作流,避免合并冲突。

下载

认知负荷

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

为了解决这个问题,我专注于减少缺乏自我解释的代码量。通过坚持单一职责原则(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工具

更多
音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

相关专题

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

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

6375

2025.09.05

java面向对象
java面向对象

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

342

2025.11.27

C++ 面向对象编程与设计模式实践
C++ 面向对象编程与设计模式实践

本专题整合了 C++ 面向对象编程的核心机制,涵盖类的定义与访问控制、构造函数与析构函数、拷贝语义与移动语义、继承与虚函数多态、抽象类与接口、运算符重载、友元函数等关键知识点,同时结合单例模式、工厂模式、观察者模式、策略模式等常用设计模式的 C++ 实现,帮助开发者写出结构清晰、易于维护的面向对象代码。

344

2026.04.09

Java 面向对象与核心编程机制
Java 面向对象与核心编程机制

系统讲解 Java 面向对象编程的核心知识体系,涵盖类的定义与封装、构造方法与 this/super 关键字、继承与方法重写、多态与向上向下转型、抽象类与接口的区别与设计原则、异常处理机制(try-catch-finally / 自定义异常)、泛型的类型参数与通配符、集合框架(List/Set/Map)的使用与遍历方式,帮助开发者全面掌握 Java 程序设计的核心机制与面向对象思维。

223

2026.04.17

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

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

8525

2026.01.21

GitHub官网入口版本汇总
GitHub官网入口版本汇总

本专题整合了GitHub入口版本地址汇总,阅读专题下面的文章了解更多详细内容。

878

2026.04.02

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

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

5816

2023.08.14

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

20

2026.10.16

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

0

2026.10.16

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
vscode手册
vscode手册

共0课时 | 0人学习

Git 教程
Git 教程

共21课时 | 8万人学习

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

共8课时 | 1.9万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn