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

剪映
剪映

一款全能易用的桌面端剪辑软件

下载

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

结论

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

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

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

651

2023.06.15

java流程控制语句有哪些
java流程控制语句有哪些

java流程控制语句:1、if语句;2、if-else语句;3、switch语句;4、while循环;5、do-while循环;6、for循环;7、foreach循环;8、break语句;9、continue语句;10、return语句。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

455

2024.02.23

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

722

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

725

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

394

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

441

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

427

2023.08.02

笔记本电脑卡反应很慢处理方法汇总
笔记本电脑卡反应很慢处理方法汇总

本专题整合了笔记本电脑卡反应慢解决方法,阅读专题下面的文章了解更多详细内容。

1

2025.12.25

热门下载

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

精品课程

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

共21课时 | 2.2万人学习

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

共8课时 | 1.5万人学习

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

共0课时 | 0人学习

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

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