简体   繁体   English

如何验证澳大利亚医疗保险号码?

[英]How do I validate an Australian Medicare number?

I'm developing an online form in which user-entered Medicare numbers will need to be validated.我正在开发一个在线表格,其中需要验证用户输入的医疗保险号码。

(My specific problem concerns Australian Medicare numbers, but I'm happy for answers regarding American ones too. This question is about Medicare numbers in general.) (我的具体问题涉及澳大利亚医疗保险号码,但我也很高兴得到有关美国医疗保险号码的答案。这个问题是关于一般医疗保险号码的。)

So how should I do it?那我应该怎么做呢?

(It would be good to have the answer in Javascript or a regex.) (最好用 Javascript 或正则表达式来回答。)

The regex supplied by Jeffrey Kemp (March 11) would help to validate the allowed characters, but the check algorithm below should be enough to validate that the number conforms to Medicare's rules. Jeffrey Kemp(3 月 11 日)提供的正则表达式将有助于验证允许的字符,但下面的检查算法应该足以验证数字是否符合 Medicare 的规则。

The Medicare card number comprises: Medicare 卡号包括:

  • Eight digits;八位数字;
  • A check digit (one digit);一个校验位(一位); and
  • An issue number (one digit).问题编号(一位)。

Note: the first digit of the Medicare card number should be in the range 2 to 6.注意:Medicare 卡号的第一位数字应在 2 到 6 之间。

Medicare card number check digit calculation医保卡号校验位计算

  1. Calculate the sum of: ((digit 1) + (digit 2 * 3) + (digit 3 * 7) + (digit 4 * 9) + (digit 5) + (digit 6 * 3) + (digit 7 * 7) + (digit 8 * 9))计算总和:((digit 1) + (digit 2 * 3) + (digit 3 * 7) + (digit 4 * 9) + (digit 5) + (digit 6 * 3) + (digit 7 * 7) +(数字 8 * 9))

where digit 1 is the highest place value digit of the Medicare card number and digit 8 is the lowest place value digit of the Medicare card number.其中第 1 位是 Medicare 卡号的最高位值数字,第 8 位是 Medicare 卡号的最低位值位。

Example: for Medicare card number '2123 45670 1', digit 1 is 2 and digit 8 is 7.示例:对于 Medicare 卡号“2123 45670 1”,数字 1 为 2,数字 8 为 7。

  1. Divide the calculated sum by 10.将计算出的总和除以 10。
  2. The check digit is the remainder.校验位是余数。

Example: For Medicare card number 2123 4567.示例:对于 Medicare 卡号 2123 4567。

  1. (2) + (1 * 3) + (2 * 7) + (3 * 9) + (4) + (5 * 3) + (6 * 7) + (7 * 9) = 170 (2) + (1 * 3) + (2 * 7) + (3 * 9) + (4) + (5 * 3) + (6 * 7) + (7 * 9) = 170
  2. Divide 170 by 10. The remainder is 0. 170 除以 10,余数为 0。
  3. The check digit for this Medicare number is 0.此 Medicare 号码的校验位是 0。

Source: "Use of Healthcare Identifiers in Health Software Systems - Software Conformance Requirements, Version 1.4", NEHTA, 3/05/2011来源:“在健康软件系统中使用医疗保健标识符 - 软件一致性要求,版本 1.4”,NEHTA,3/05/2011

If you are looking for a C# version, give this a try:如果您正在寻找 C# 版本,请尝试一下:

using System.Linq;

//...

public bool IsMedicareFormatValid(string medicareNumber)
{
    if (!(medicareNumber?.Length >= 10 && medicareNumber.Length <12) || !medicareNumber.All(char.IsDigit))
        return false;

    var digits = medicareNumber.Select(c => (int) char.GetNumericValue(c)).ToArray();
    return digits[8] == GetMedicareChecksum(digits.Take(8).ToArray());
}

private int GetMedicareChecksum(int[] digits)
{
    return digits.Zip(new[] { 1, 3, 7, 9, 1, 3, 7, 9 }, (m, d) => m*d).Sum() % 10;
}

Note: This will return false for null values, you might want to throw an exception.注意:对于空值,这将返回 false,您可能想要抛出异常。

To clarify:澄清:

  1. The first 9 Numbers in the medicare card would correspond to the actual medicare number (used in the check).医疗保险卡中的前 9 个数字将对应于实际医疗保险编号(用于支票)。
  2. The 9th digit is a check digit calculated in the GetMedicareChecksum method.第 9 位是在GetMedicareChecksum方法中计算的校验位。
  3. The 10th digit identifies the number of the card, so if you've been issued 3 cards (because you've lost it or whatever), the number would be 3第 10 位数字标识卡的编号,因此如果您已获得 3 张卡(因为您丢失或其他原因),则编号为 3
  4. The 11th digit would identify the family member inside the group.第 11 位数字将标识组内的家庭成员。

Hope someone finds this useful.希望有人觉得这很有用。

Here's a Typescript or modern Javascript solution:这是一个 Typescript 或现代 Javascript 解决方案:

  validateMedicare(medicare) {
    let isValid = false;

    if (medicare && medicare.length === 10) {
      const matches = medicare.match(/^(\d{8})(\d)/);

      if (!matches) {
        return { invalid: true };
      }

      const base = matches[1];
      const checkDigit = matches[2];
      const weights = [1, 3, 7, 9, 1, 3, 7, 9];

      let sum = 0;
      for (let i = 0; i < weights.length; i++) {
        sum += parseInt(base[i], 10) * weights[i];
      }

      isValid = sum % 10 === parseInt(checkDigit, 10);
    }

    return isValid;
  }

Please refer to http://clearwater.com.au/code/medicare for an explanation.请参阅http://clearwater.com.au/code/medicare以获得解释。

To test, generate medicare number here: https://precedencehealthcare.com/rmig/要进行测试,请在此处生成医疗保险编号: https : //precedencehealthcare.com/rmig/

Added Swift version添加了 Swift 版本

class func isMedicareValid(input : String, validateWithIrn : Bool) -> Bool {
    let multipliers = [1, 3, 7, 9, 1, 3, 7, 9]

    let pattern = "^(\\d{8})(\\d)"
    let medicareNumber = input.removeWhitespace()
    let length = validateWithIrn ? 11 : 10

    if medicareNumber.characters.count != length {return false}

    let expression = try! NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive)

    let matches = expression.matchesInString(medicareNumber, options: NSMatchingOptions.ReportProgress, range: NSMakeRange(0, length))

    if (matches.count > 0 && matches[0].numberOfRanges > 2) {
        let base = medicareNumber.substringWithRange(medicareNumber.startIndex...medicareNumber.startIndex.advancedBy(matches[0].rangeAtIndex(1).length))
        let checkDigitStartIndex = medicareNumber.startIndex.advancedBy(matches[0].rangeAtIndex(2).location )
        let checkDigitEndIndex = checkDigitStartIndex.advancedBy(matches[0].rangeAtIndex(2).length)
        let checkDigit = medicareNumber.substringWithRange(checkDigitStartIndex..<checkDigitEndIndex)
        var total = 0

        for i in 0..<multipliers.count {
            total += Int(base.charAtIndex(i))! * multipliers[i]
        }

         return (total % 10) == Int(checkDigit)
    }
    return false
}

I use some String extensions as well to simplify some operations.我也使用一些字符串扩展来简化一些操作。

extension String {

func charAtIndex (index: Int) -> String{
    var character = ""
    if (index < self.characters.count){
        let locationStart = self.startIndex.advancedBy(index)
        let locationEnd = self.startIndex.advancedBy(index + 1 )
        character = self.substringWithRange(locationStart..<locationEnd)
    }
    return character
}

func replace(string:String, replacement:String) -> String {
    return self.stringByReplacingOccurrencesOfString(string, withString: replacement, options: NSStringCompareOptions.LiteralSearch, range: nil)
}

func removeWhitespace() -> String {
    return self.replace(" ", replacement: "")
}
}

Added Java Version添加了 Java 版本

public static boolean isMedicareValid(String input, boolean validateWithIRN){
    int[] multipliers = new int[]{1, 3, 7, 9, 1, 3, 7, 9};
    String pattern = "^(\\d{8})(\\d)";
    String medicareNumber = input.replace(" " , "");
    int length = validateWithIRN ? 11 : 10;

    if (medicareNumber.length() != length) {return false;}

    Pattern medicatePattern = Pattern.compile(pattern);
    Matcher matcher = medicatePattern.matcher(medicareNumber);

    if (matcher.find()){

        String base = matcher.group(1);
        String checkDigit = matcher.group(2);

        int total = 0;
        for (int i = 0; i < multipliers.length; i++){
            total += base.charAt(i) * multipliers[i];
        }

        return ((total % 10) == Integer.parseInt(checkDigit));
    }

    return false;

}

My Australian Medicare number is 11 numeric digits and includes no letters or other characters.我的澳大利亚医疗保险号码是 11 位数字,不包含字母或其他字符。

It is formatted in groups, and the last digit varies according to the member of my family, eg:它是按组格式化的,最后一位数字因我的家庭成员而异,例如:

  • Me: 5101 20591 8-1我: 5101 20591 8-1
  • My wife: 5101 20591 8-2老婆: 5101 20591 8-2
  • My first child: 5101 20591 8-3我的第一个孩子: 5101 20591 8-3

I've seen medicare numbers formatted without the spaces and the dash, but the meaning is the same, so I'd expect to accept 51012059181 as a valid Medicare number as well.我见过格式不带空格和破折号的医疗保险号码,但含义是一样的,所以我希望也接受51012059181作为有效的医疗保险号码。

I've also seen context where the last digit is not required or not supposed to be entered;我还看到了不需要或不应该输入最后一位数字的上下文; eg 5101205918 , I guess where they're only interested in the family as a whole.例如5101205918 ,我猜他们只对整个家庭感兴趣。

Therefore, I think this may be appropriate:因此,我认为这可能是合适的:

^\d{4}[ ]?\d{5}[ ]?\d{1}[- ]?\d?$

EDIT编辑

Based on the logic in user2247167's answer, I've used the following PL/SQL function in my Apex application to give a user-friendly warning to the user:根据 user2247167 的回答中的逻辑,我在我的 Apex 应用程序中使用了以下 PL/SQL 函数来向用户发出用户友好的警告:

FUNCTION validate_medicare_no (i_medicare_no IN VARCHAR2)
  RETURN VARCHAR2 IS
  v_digit1 CHAR(1);
  v_digit2 CHAR(1);
  v_digit3 CHAR(1);
  v_digit4 CHAR(1);
  v_digit5 CHAR(1);
  v_digit6 CHAR(1);
  v_digit7 CHAR(1);
  v_digit8 CHAR(1);
  v_check  CHAR(1);
  v_result NUMBER;
BEGIN
  IF NOT REGEXP_LIKE(i_medicare_no, '^\d{10}\d?{2}$') THEN
    RETURN 'Must be 10-12 digits, no spaces or other characters';
  ELSE
    v_digit1 := SUBSTR(i_medicare_no, 1, 1);
    IF v_digit1 NOT IN ('2','3','4','5','6') THEN
      RETURN 'Not a valid Medicare number - please check and re-enter';
    ELSE
      v_digit2 := SUBSTR(i_medicare_no, 2, 1);
      v_digit3 := SUBSTR(i_medicare_no, 3, 1);
      v_digit4 := SUBSTR(i_medicare_no, 4, 1);
      v_digit5 := SUBSTR(i_medicare_no, 5, 1);
      v_digit6 := SUBSTR(i_medicare_no, 6, 1);
      v_digit7 := SUBSTR(i_medicare_no, 7, 1);
      v_digit8 := SUBSTR(i_medicare_no, 8, 1);
      v_check  := SUBSTR(i_medicare_no, 9, 1);
      v_result := mod(   to_number(v_digit1)
                      + (to_number(v_digit2) * 3)
                      + (to_number(v_digit3) * 7)
                      + (to_number(v_digit4) * 9)
                      +  to_number(v_digit5)
                      + (to_number(v_digit6) * 3)
                      + (to_number(v_digit7) * 7)
                      + (to_number(v_digit8) * 9)
                     ,10);
      IF TO_NUMBER(v_check) != v_result THEN
        RETURN 'Not a valid Medicare number - please check and re-enter';
      END IF;
    END IF;
  END IF;
  -- no error
  RETURN NULL;
END validate_medicare_no;

The accepted answer, adapted to JavaScript:接受的答案,适用于 JavaScript:

var validator = function (input, validateWithIrn) {
    if (!input) {
        return false;
    }

    var medicareNumber;
    var pattern;
    var length;
    var matches;
    var base;
    var checkDigit;
    var total;
    var multipliers;
    var isValid;

    pattern = /^(\d{8})(\d)/;
    medicareNumber = input.toString().replace(/ /g, '');
    length = validateWithIrn ? 11 : 10;

    if (medicareNumber.length === length) {
        matches = pattern.exec(medicareNumber);
        if (matches) {
            base = matches[1];
            checkDigit = matches[2];
            total = 0;
            multipliers = [1, 3, 7, 9, 1, 3, 7, 9];

            for (var i = 0; i < multipliers.length; i++) {
                total += base[i] * multipliers[i];
            }

            isValid = (total % 10) === Number(checkDigit);
        } else {
            isValid = false;
        }
    } else {
        isValid = false;
    }

    return isValid;
};

I found a forum discussion on the topic: 我找到了关于这个主题的论坛讨论:

http://regexadvice.com/forums/thread/57337.aspx http://regexadvice.com/forums/thread/57337.aspx

I'm going to try the one 'Aussie Susan' came up with: 我打算尝试一下'澳洲苏珊'想出的:

^\d{9}B[ADGHJKLNPQRTWY1-9,]?$

You can create Validation Attribute to validate Medicare number您可以创建验证属性来验证医疗保险号码

You can use it by您可以通过以下方式使用它

[AustralianMedicareNumberOnly]
public string MedicareNo { get; set; }

Code代码

public class AustralianMedicareNumberOnlyAttribute : ValidationAttribute
{
    private string exampleNumber = "Example: 2234 56789 1-2";

    public AustralianMedicareNumberOnlyAttribute()
    {
        ErrorMessage = string.Concat("{0} is not in correct format, ", exampleNumber);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            string objectValueString;
            int[] checksumDigits = new int[] { 1, 3, 7, 9, 1, 3, 7, 9 };
            int checksumDigit;
            int checksumtotal = 0;
            int checksumDigitCalculated;

            //convert incomming object value to string
            objectValueString = Convert.ToString(value).Trim();

            // check medicare number format should be 1234 56789 1-2
            if (!Regex.IsMatch(objectValueString, @"^[2-6]\d{3}\s\d{5}\s\d{1}-\d{1}$"))
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
            else
            { 
                //Check checksum value
                //--------------------

                // replace two spaces and one dash
                objectValueString = objectValueString.Replace(" ", "").Replace("-", "");

                // Calculate the sum of: ((digit 1) + (digit 2 * 3) + (digit 3 * 7) + (digit 4 * 9) + (digit 5) + (digit 6 * 3) + (digit 7 * 7) + (digit 8 * 9))
                for (int i = 0; i < checksumDigits.Length; i++)
                {
                    int digit = Convert.ToInt32(objectValueString.Substring(i, 1));

                    checksumtotal += digit * checksumDigits[i];
                }

                //find out checksum digit
                checksumDigit = Convert.ToInt32(objectValueString.Substring(8, 1));
                checksumDigitCalculated = checksumtotal % 10;

                // check calculated checksum with medicare checksum digit
                if (checksumDigit!= checksumDigitCalculated)
                {
                    return new ValidationResult("The Medicare Number is not Valid.");
                }

            }

        }

        return ValidationResult.Success;
    }
}

Added Dart version:添加了 Dart 版本:

bool isMedicareValid(String input, {bool validateWithIrn = true}) {
  final medicareNumber = input.replaceAll(" ", "");
  final length = validateWithIrn ? 11 : 10;

  if (medicareNumber.length != length) {
    return false;
  }

  final multipliers = [1, 3, 7, 9, 1, 3, 7, 9];
  final regexp = RegExp("^(\\d{8})(\\d)", caseSensitive: false);

  final matches = regexp.allMatches(medicareNumber).toList();

  if (matches.length > 0 && matches[0].groupCount >= 2) {
    final base = matches[0].group(1);
    final checkDigit = matches[0].group(2);
    var total = Iterable.generate(multipliers.length)
        .fold(0, (current, index) => current += (int.tryParse(base[index]) * multipliers[index]));

    return (total % 10) == int.parse(checkDigit);
  }
  return false;
}

Another Swift implementation with comments:另一个带有注释的 Swift 实现:

func validateMedicareNumber(input: String) -> Bool {
    let weights = [1, 3, 7, 9, 1, 3, 7, 9]

    // Remove all whitespace
    var trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
        .components(separatedBy: .whitespaces)
        .joined()

    // The medicare card number has 8 digits identifier + 1 digit checksum + 1 digit issue number.
    // Either 9 or 10 numbers will be valid
    guard trimmed.count == 9 || trimmed.count == 10 else {
        return false
    }

    // Drop the issue number if it was added
    if trimmed.count == 10 {
        trimmed = String(trimmed.dropLast())
    }

    // The last digit is a checksum
    guard let lastElement = trimmed.last,
        let checkSum = Int(String(lastElement)) else {
        return false
    }

    // Remove the checksum from our input
    trimmed = String(trimmed.dropLast())

    // Multiply the numbers by weights
    let weightedNumbers: [Int] = trimmed.enumerated().map { index, element in
        guard let number = Int(String(element)) else {
            // -1 will throw the calculations out of the window which
            // will guarantee invalid checksum
            return -1
        }

        return number * weights[index]
    }

    // Validate the weighted numbers against the checksum
    return weightedNumbers.reduce(0, +) % 10 == checkSum
}

If you need a test card number for development, use this one.如果您需要用于开发的测试卡号,请使用此卡号。 It's for John Doe这是给约翰·多伊的

TEST CARD NUMBER > 2428 77813 2/1测试卡号 > 2428 77813 2/1

Expanding on Daniel Ormeño answer, for asp.net you can put the check into an attribute and decorate the property in the model扩展Daniel Ormeño 的答案,对于 asp.net,您可以将支票放入一个属性中并在模型中装饰该属性

public class MedicareValidation : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        string medicareNumber = value.ToString();

        if (!(medicareNumber?.Length >= 10 && medicareNumber.Length < 12) || !medicareNumber.All(char.IsDigit))
            return false;

        var digits = medicareNumber.Select(c => (int)char.GetNumericValue(c)).ToArray();
        return digits[8] == GetMedicareChecksum(digits.Take(8).ToArray());
    }

    private int GetMedicareChecksum(int[] digits)
    {
        return digits.Zip(new[] { 1, 3, 7, 9, 1, 3, 7, 9 }, (m, d) => m * d).Sum() % 10;
    }
}

Then in the model然后在模型中

[DisplayName("Medicare Number")]  
[MedicareValidation] 
public string MedicareNumber {get; set;}

Here's a Python version!这是一个 Python 版本! Note that it expects a full 11 digit Medicare number.请注意,它需要一个完整的 11 位医疗保险号码。 If you're validating 10 digit Medicare numbers without the individual reference number, you'll need to tweak the regex in the re.match line.如果您要验证 10 位医疗保险号码而没有个人参考号码,则需要调整re.match行中的正则表达式。

def validate_medicare_number(medicare_number: str) -> bool:
    """Given a string containing a medicare number, return True if valid,
    False if invalid.

    >>> validate_medicare_number("2428 77813 2/1")
    True
    >>> validate_medicare_number("7428 77818 2/1")
    False
    >>> validate_medicare_number("2428 77815 2/1")
    False
    """

    # See here for checksum algorithm:
    # https://stackoverflow.com/a/15823818
    # https://clearwater.com.au/code/medicare

    # Remove whitespace
    medicare_number = re.sub(r"[^\d]+", "", medicare_number)
    if re.match(r"^[2-6]\d{10}$", medicare_number):

        medicare_digits = list(map(int, medicare_number))
        checksum_weights = (1, 3, 7, 9) * 2
        digit_weight_pairs = zip(medicare_digits, checksum_weights)
        checksum = sum([digit * weight for digit, weight in digit_weight_pairs]) % 10

        return checksum == medicare_digits[8]
    else:
        return False

Microsoft SQL Server 2017微软 SQL Server 2017

Assumes the inputted medicare number is clean.假设输入的医疗保险号是干净的。 Validates 10-digit medicare numbers.验证10 位医疗保险号码。 No reference numbers included.不包括参考编号。

if object_id('dbo.validate_medicare') is not null 
    drop function dbo.validate_medicare;
go 

create function dbo.validate_medicare(@medicareNumber varchar(max)) 
returns int 
as 
begin 

    if len(@medicareNumber) <> 10
        return 0;

    if isnumeric(@medicareNumber) = 0
        return 0;

    declare @count as int = 0;
    declare @firstDigit as int = cast(substring(@medicareNumber, 1, 1) as int);

    if @firstDigit not between 2 and 6
        return 0;

    declare @actualCheckDigit as int = cast(substring(@medicareNumber, 9, 1) as int);
    declare @computedCheckDigit as int;
    declare @weight as int = 1;
    declare @tempStuff table (
        digit int,
        weight int
    );
    declare @isValid as int;


    while @count < len(substring(@medicareNumber, 1, 8))
    begin 
        declare @selected_digit as int = substring(@medicareNumber, @count+1, 1);

        if @weight > 9
            set @weight = 1;

        insert into @tempStuff(digit, weight) values (@selected_digit, @weight);
        set @weight = @weight + 2;

        if @weight = 5
            set @weight = @weight + 2;

        set @count = @count + 1;
    end;

    -- Set the computed Check digit 
    select @computedCheckDigit = (sum(digit * weight)) % 10 from @tempStuff;

    -- Check if computed check digit equals the actual check digit
    set @isValid = case 
                        when @computedCheckDigit = @actualCheckDigit
                            then 1 
                        else 0 
                   end;

    return @isValid;
end;
go

Example例子

declare @medicareNumber as varchar(11) = '2123456701';
select dbo.validate_medicare(@medicareNumber) as IsValid;

You can use simple regex validation: .replace(/\\W/gi, "") .replace(/(.{4})(.{5})/g, "$1 $2 ");您可以使用简单的正则表达式验证: .replace(/\\W/gi, "") .replace(/(.{4})(.{5})/g, "$1 $2 ");

check my example here: codesandbox.io在此处查看我的示例: codeandbox.io

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM