Нужен пример кодирования Luhn Modulus 16 для генерации контрольной цифры в PL/SQL

Есть ли у кого-нибудь рабочая функция, доступная для использования в Oracle с использованием PL/SQL, которая реализует алгоритм Luhn Mod 16 для генерации контрольной цифры для входного кодового номера, например, в следующем примере? 0B012722900021AC35B2

ЛОГИКА

  1. Преобразование из HEX в десятичный эквивалент 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. Преобразуйте «двойной» в формат с основанием 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

Спасибо, Ли.


person Lee Reynolds    schedule 04.01.2018    source источник
comment
Возможный дубликат PL/SQL Check Digit, luhn с использованием MOD 11   -  person krokodilko    schedule 04.01.2018
comment
Это не совсем дубликат из-за шестнадцатеричной части.   -  person Lee Reynolds    schedule 04.01.2018
comment
Я изменил формулировку step 5) из вашего комментария к моему ответу. Пожалуйста, проверьте правильность моей интерпретации.   -  person APC    schedule 05.01.2018
comment
@LeeReynolds - я приложил много усилий, чтобы помочь вам решить вашу проблему, но вы покинули эту ветку до того, как она была закрыта. Плохое шоу,   -  person APC    schedule 21.01.2018


Ответы (2)


Как насчет этого?

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;
person Wernfried Domscheit    schedule 04.01.2018
comment
Вы не знаете, могу ли я добавить документ в эту тему? Это покажет шаги намного лучше, чем я могу это объяснить? - person Lee Reynolds; 04.01.2018

Вот функция, которая реализует шаги, указанные в вашем вопросе:

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. Вот демонстрация LiveSQL.


Остается еще вопрос, что происходит, когда модуль 16 больше 9; например, это значение возвращает 15.

select get_luhn_16_check_digit('22111111111111111111') from dual; 

Десятичный результат, такой как 15, очевидно, не является контрольной цифрой, что говорит о необходимости дополнительного шага 9). Возможно, нам нужно преобразовать 15 -> (1+5) -> 6. Или, возможно, цифра должна быть base16? Пожалуйста, отредактируйте свой вопрос, чтобы подтвердить соответствующее правило.

person APC    schedule 04.01.2018
comment
спасибо за вышеизложенное. Я постараюсь объяснить. В документе, который у меня есть, указано - - person Lee Reynolds; 04.01.2018
comment
дайте мне время до утра, чтобы попытаться объяснить, что я имею в виду более подробно - person Lee Reynolds; 04.01.2018
comment
Извините, кажется, я пропустил часть отображения персонажей. В документе указано, что необходимо использовать следующее сопоставление символов. Позиция 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 Помогает ли это? - person Lee Reynolds; 04.01.2018
comment
Из того, что я могу понять из документа, я считаю, что логика такова: в случае символа № 6 он начинается с 7, затем удваивается до 14, 14 в сопоставлении символов становится E, потому что E нельзя использовать как это не числовое значение, в расчете используется исходное значение 7. То же самое для char # 18 - person Lee Reynolds; 05.01.2018
comment
Большое спасибо за Вашу помощь. Я очень ценю это. Это сработало для ряда входов, которые я пробовал. Я рассмотрю проблему с контрольной строкой из двух цифр. - person Lee Reynolds; 05.01.2018
comment
Я не могу решить проблему с двумя контрольными цифрами. Есть ли способ связаться с вами за пределами этого форума, чтобы обсудить решение. Я рад заплатить вам за ваше время. Спасибо - person Lee Reynolds; 09.01.2018
comment
@LeeReynolds - начать с проблемы с двумя контрольными цифрами - это проблема бизнес-логики: каково правило ее решения? Если вы можете указать правило, исправление будет очень простым. Что касается меня, то да, это возможно, так как я фрилансер. Тем не менее, есть сложности с моим текущим размещением; маловероятно, что эта задача оправдает бумажную работу. - person APC; 13.01.2018
comment
Обычно я не выступаю против анонимных отрицательных голосов, но этот вызывает больше всего провокаций. Я потратил много времени на формирование этого ответа. Так что, кем бы вы ни были, пусть у вас всегда будет камень в ботинке. - person APC; 13.01.2018