【问题标题】:Need Luhn Modulus 16 coding example to generate a check digit in PL/SQL需要 Luhn Modulus 16 编码示例在 PL/SQL 中生成校验位
【发布时间】:2018-06-14 01:06:42
【问题描述】:

是否有人可以在 Oracle 内使用 PL/SQL 使用工作函数,该函数实现 Luhn Mod 16 算法来为输入代码生成校验位,如下例所示? 0B012722900021AC35B2

逻辑

  1. 从十六进制映射到十进制等效0 B 0 1 2 7 2 2 9 0 0 0 2 1 A C 3 5 B 2 - 变为0 11 0 1 2 7 2 2 9 0 0 0 2 1 10 12 3 5 11 2

  2. 从字符串中的最后一个字符开始,向左移动每隔一个数字加倍 - 变为0 22 0 2 2 14 2 4 9 0 0 0 2 2 10 24 3 10 11 4

  3. 将“double”转换为 Base 16(十六进制)格式。如果转换导致数字输出,则保留该值。 变为:0 16 0 2 2 E 2 4 9 0 0 0 2 2 10 18 3 A 11 4

  4. 通过将任何结果值拆分为单个数字长度来减少。 变为0 (1+6) 0 2 2 E 2 4 9 0 0 0 2 2 10 (1+8) 3 A 11 4

  5. 对所有数字求和。应用从上一个计算序列返回的最后一个数值(如果当前值是 A-F,则替换步骤 1 中的数值) 变为0 7 0 2 2 7 2 4 9 0 0 0 2 2 10 9 3 5 11 4

  6. 所有 l 位数字之和为 79 (0+7+0+2+2+7+2+4+9+0+0+0+2+2+10+9+3+5+11+4)

  7. 计算获得下一个 16 的倍数所需的值,在这种情况下,下一个 16 的倍数是 80,因此该值为 1

  8. 关联的校验字符是1

谢谢李

【问题讨论】:

  • 由于是十六进制,所以不太重复。
  • 我已经根据您对我的回答的评论修改了step 5) 的措辞。请检查我的解释是否正确。
  • @LeeReynolds - 我付出了很多努力来帮助您解决您的问题,但您在此线程关闭之前已经放弃了它。表现不佳,

标签: oracle stored-procedures plsql luhn


【解决方案1】:

这个怎么样?

CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);


DECLARE
    luhn VARCHAR2(100) := '0B012722900021AC35B2';

    digits VARCHAR_TABLE_TYPE;
    DigitSum INTEGER;
BEGIN

    SELECT REGEXP_SUBSTR(luhn, '.', 1, LEVEL)
    BULK COLLECT INTO digits
    FROM dual
    CONNECT BY REGEXP_SUBSTR(luhn, '.', 1, LEVEL) IS NOT NULL;  

    FOR i IN digits.FIRST..digits.LAST LOOP
        digits(i) := TO_NUMBER(digits(i), 'X'); -- Map from HEX into Decimal equivalent
        IF digits.COUNT MOD 2 = i MOD 2 THEN -- every second digit from left            
            digits(i) := 2 * TO_NUMBER(digits(i)); -- doubling number
            digits(i) := TO_CHAR(digits(i), 'fmXX'); -- Convert the "double" to a Base 16 (Hexadecimal) format
            IF (REGEXP_LIKE(digits(i), '^\d+$')) THEN
                -- Reduce by splitting down any resultant values over a single digit in length. 
                SELECT SUM(REGEXP_SUBSTR(digits(i), '\d', 1, LEVEL))
                INTO digits(i)
                FROM dual
                CONNECT BY REGEXP_SUBSTR(digits(i), '\d', 1, LEVEL) IS NOT NULL;
            END IF;    
        END IF;
    END LOOP;

    FOR i IN digits.FIRST..digits.LAST LOOP
        -- I don't understand step 5), let's simulate it
        IF digits(i) = 'E' THEN digits(i) := 7; END IF;
        IF digits(i) = 'A' THEN digits(i) := 5; END IF;
    END LOOP;

    -- The sum of all digits
    SELECT SUM(COLUMN_VALUE)
    INTO DigitSum
    FROM TABLE(digits);

    -- Calculate the value needed to obtain the next multiple of 16 
    DBMS_OUTPUT.PUT_LINE ( 16 - DigitSum MOD 16 );

END;

【讨论】:

  • 您知道我是否可以在此线程中添加文档吗?这将比我能解释的更好地显示这些步骤?
【解决方案2】:

这是一个实现您在问题中概述的步骤的函数:

create or replace function get_luhn_16_check_digit 
    ( p_str in varchar)
    return pls_integer 
is
    b16 sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll() ;
    n16 sys.odcinumberlist := sys.odcinumberlist();
    tot simple_integer := 0;
    chk_digit pls_integer;
begin
    -- step 1)
    select to_number(tkn, 'X')
    bulk collect into n16
    from ( select substr(p_str, level, 1) as tkn
                 from dual
                 connect by level <= length(p_str));
    b16.extend(n16.count());

    for idx in 1..n16.count() loop
        if mod(idx, 2) = 0
        then 
            -- step 2) and 3)
            b16(idx) := to_char( to_char(n16(idx)) * 2, 'fmXX');
            -- step 4)
            if length( b16(idx)) = 2 then
                 b16(idx) := to_number(substr(b16(idx),1,1)) + to_number(substr(b16(idx),2,1));
            end if;  
        else 
            b16(idx) := trim(to_char(n16(idx)));
        end if;
    end loop;

    -- step 5) and 6) 
    for idx in 1..b16.count() loop
        if  b16(idx) not in ('A','B','C','D','E','F') then
            tot := tot + to_number(b16(idx));
        else
            tot := tot + n16(idx);
        end if;
    end loop;

    -- step 7) and 8)
    chk_digit := (ceil(tot/16)*16) - tot; 
    return chk_digit;
end;
/

运行:

select get_luhn_16_check_digit('0B012722900021AC35B2') from dual;

现在返回1。这里是a LiveSQL demo


还有一个问题,当模数 16 大于 9 时会发生什么;例如,此值返回15

select get_luhn_16_check_digit('22111111111111111111') from dual; 

15 等十进制结果显然不是校验 digit,这表明您需要额外的步骤 9)。也许我们需要转换15 -> (1+5) -> 6。或者也许数字应该是base16?请编辑您的问题以确认适当的规则。

【讨论】:

  • 感谢以上。我会尝试解释。我拥有的文件状态-
  • 早上再给我解释一下我的意思
  • 对不起,我想我已经离开了字符映射部分。该文档指出必须使用以下字符映射。位置 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 字符 0 1 2 3 4 5 6 7 8 9 A B C D E F 这有帮助吗?
  • 从我可以从文档中得出的结论,我相信逻辑是,在 char#6 的情况下,它从 7 开始,然后加倍到 14,char 映射中的 14 变为 E,因为 E 不能使用,因为它不是数字,所以在计算中使用了 7 的原始值。 char#18 也是如此
  • 非常感谢您的帮助。对此,我真的非常感激。这适用于我尝试过的许多输入。我来看看两位校验字符串的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多