> Java > java지도 시간 > 본문

CPF 및 CNPJ 검사 숫자 알고리즘 이해하기: 명확하고 간결한 접근 방식

PHPz
풀어 주다: 2024-09-03 12:32:03
원래의
611명이 탐색했습니다.

Demystifying CPF and CNPJ Check Digit Algorithms: A Clear and Concise Approach

저는 학부 시절 CPF(Brazillian ID) 검증 알고리즘을 처음 접했던 기억이 생생합니다. 미나스 제라이스 연방 대학교 UFMG의 정밀 과학 연구소(Institute of Exact Sciences)에 인턴십을 지원하는 동안 우리는 알고리즘에 대한 간단한 설명을 마친 후 CPF 검사 숫자를 검증하기 위해 Java 코드를 직접 작성해 달라는 요청을 받았습니다.

그 이후로 저는 다양한 전문적 맥락에서 이 문제를 여러 번 접했으며 종종 인터넷에서 솔루션을 복사하고 일부 단위 테스트를 추가했습니다. 그러나 매번 이러한 솔루션에서 반복되는 문제에 놀랐습니다. 이는 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만을 위한 코드입니다!

결론

결과 코드가 다소 장황해 보일 수 있지만, 명확성과 자기 설명을 강조한 결과 만족스러운 결과를 얻었습니다. 코드는 보다 직관적으로 설계되어 정확성에 대한 더 큰 확신을 제공하며 페이지를 아래로 스크롤하지 않고도 대부분의 핵심 기능을 볼 수 있습니다.

향후 개선을 위한 제안을 환영합니다. 피드백을 자유롭게 공유해 주세요.

위 내용은 CPF 및 CNPJ 검사 숫자 알고리즘 이해하기: 명확하고 간결한 접근 방식의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!