Как мы можем устранить нежелательные значения в поле? - PullRequest
1 голос
/ 28 июля 2011

У меня есть некоторые записи CSV, которые имеют переменную длину, например:

0005464560,45667759, ZAMTR,! К ACC 12345678, DR, 79,85

0006786565,34567899, ZAMTR ,!Для ACC 26575443, DR, 1000

мне нужно отделить каждое из этих полей, и мне нужно последнее поле, которое должно быть деньгами.

Однако, когда я читаю файл и освобождаю строкузаписав в поля, я обнаружил, что последнее поле содержит ненужное значение в конце самого себя.Поле суммы (денег) должно состоять из 8 символов, 5 цифр спереди, 1 точка, 2 цифры в конце.Значения на входе могут быть любыми, например, 13,5, 1000 и 354,23.

    "FILE SECTION"

        FD INPUT_FILE.
            01 INPUT_REC                                   PIC X(66).

    "WORKING STORAGE SECTion"

            01 WS_INPUT_REC                                 PIC X(66).

            01 WS_AMOUNT_NUM                                PIC 9(5).9(2).
            01 WS_AMOUNT_TXT                                PIC X(8).

"MAIN SECTION"

                        UNSTRING INPUT_REC DELIMITED BY ","
                        INTO WS_ID_1, WS_ID_2, WS_CODE, WS_DESCRIPTION, WS_FLAG, WS_AMOUNT_TXT

                        MOVE WS_AMOUNT_TXT(1:8) TO WS_AMOUNT_NUM(1:8)

                        DISPLAY WS_AMOUNT_NUM

С дисплея это значение довольно нормальное: 345,23, 1000, как и после того, как я написалполе в файл, вот что они становятся:

79.85 ^ M ^ @ ^ @ 137.35^M^@

Я проверил поле WS_AMOUNT_NUM, которое пришло из поля WS_AMOUNT_TXT,и обнаружил, что ^ @ является своего рода НИЗКОЙ ЦЕННОСТЬЮ.Тем не менее, я не могу найти, что такое ^ M, это не пробел, не высокое значение.

Ответы [ 2 ]

4 голосов
/ 28 июля 2011

Я предполагаю, но, похоже, вы читаете записи переменной длины из файла в запись COBOL фиксированной длины.Мусор в конце записи на COBOL приносит вам немного горя.Трудно сказать, насколько последовательным будет этот мусор от одного чтения к другому (данные за пределами фактической длины входной записи технически не определены).Этот мусор в конечном итоге включается в WS_AMOUNT_TXT после UNSTRING

. Существует несколько способов решения этой проблемы.Предложение, которое я даю вам здесь, может быть неоптимальным, но оно простое и должно выполнить работу.

Последнее поле INTO, WS_AMOUNT_TXT, в вашем выражении UNSTRING - это то, чтополучает весь конечный мусор.Эту ерунду нужно убрать.Зная, что единственными действительными символами в последнем поле являются цифры и десятичный знак, вы можете очистить его следующим образом:

PERFORM VARYING WS_I FROM LENGTH OF WS_AMOUNT_TXT BY -1
          UNTIL WS_I = ZERO
    IF WS_AMOUNT_TXT(WS_I:1) IS NUMERIC OR
       WS_AMOUNT_TXT(WS_I:1) = '.'
       MOVE ZERO TO WS_I
    ELSE
       MOVE SPACE TO WS_AMOUNT_TXT(WS_I:1)
    END-IF
END-PERFORM

Основная идея в приведенном выше коде состоит в том, чтобы сканировать с конца последнегоUNSTRING поле вывода в начало, заменяющее все, что не является действительной цифрой или десятичной точкой, пробелом.Как только найдена правильная цифра / десятичное число, выйдите из цикла, предполагая, что остальные значения будут действительными.

После очистки используйте встроенную функцию NUMVAL, как описано в моем ответе на ваш предыдущий вопрос для преобразования WS_AMOUNT_TXT в числовой тип данных.

Один последний совет, MOVE SPACES TO INPUT_REC перед каждым READ, чтобы удалить данные, оставшиеся от предыдущего чтения, которые могут остаться в буфере.Это защитит вас при чтении очень «короткой» записи после «длинной» - в противном случае вы можете отключить данные, оставшиеся после предыдущего чтения.

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ Только что заметил этот ответ на ваш вопрос о чтении файлов переменной длины.Использование входной записи переменной длины - лучший подход.Учитывая фактическую длину входной записи, вы можете сделать что-то вроде:

UNSTRING INPUT_REC(1:REC_LEN) INTO...

Где REC_LEN - переменная, указанная после OCCURS DEPENDING ON для INPUT_REC файла FD.Весь мусор, с которым вы сталкиваетесь, происходит после окончания записи, как определено REC_LEN.Использование модификации эталона, как показано выше, обрезает его до того, как UNSTRING выполнит свою работу по выделению отдельных полей данных.

РЕДАКТИРОВАТЬ 2: Невозможно использовать эталонную модификацию с UNSTRING.Черт ... Это возможно с некоторыми другими диалектами COBOL, но не с OpenVMS COBOL.Попробуйте следующее:

MOVE INPUT_REC(1:REC_LEN) TO WS_BUFFER
UNSTRING WS_BUFFER INTO...

Где WS_BUFFER - переменная рабочего хранилища PIC X, достаточно длинная, чтобы содержать самую длинную входную запись.Когда вы MOVE сокращаете буквенно-цифровое поле на более длинное, поле назначения выравнивается по левому краю с пробелами, используемыми для заполнения оставшегося пространства (т. Е. WS_BUFFER).Поскольку начальные и конечные пробелы приемлемы для функции NUMVAL, у вас есть именно то, что вам нужно.

У меня есть причина подтолкнуть вас в этом направлении.Любой мусор, который заканчивается в конце буфера записи при чтении короткой записи, не определен.Существует вероятность того, что часть этого мусора может оказаться цифрой или десятичной точкой.Если это произойдет, процедура очистки, которую я первоначально предложил, завершится неудачей.

РЕДАКТИРОВАТЬ 3: В результирующем WS_AMOUNT_TXT нет ^ @, но все равно есть ^ M

Похоже, файловая система обрабатывает (это ^ M вещь) в конце каждой записи как данные.

Если файл, который вы читаете, получен с платформы Windows итеперь вы читаете его на платформе UNIX, которая объяснит проблему.В Windows записи заканчиваются на , а в UNIX они заканчиваются только на .Файловая система UNIX обрабатывает так, как если бы она была частью записи.

В этом случае вы можете быть почти уверены, что в конце каждой прочитанной записи будет один .,Есть несколько способов справиться с этим:

Метод 1: Как вы уже заметили, предварительно отредактируйте файл с помощью Notepad ++ или другогоинструмент для удаления символов перед обработкой через вашу программу COBOL.Лично я не думаю, что это лучший способ сделать это.Я предпочитаю использовать решение только на языке COBOL, так как оно включает меньше этапов обработки.

Метод 2: Обрежьте последний символ из каждой входной записи перед обработкой.Последний символ всегда должен быть .Попробуйте следующее, если вы читаете записи как переменную длину и имеете фактическую доступную длину входной записи.

SUBTRACT 1 FROM REC_LEN
MOVE INPUT_REC(1:REC_LEN) TO WS_BUFFER
UNSTRING WS_BUFFER INTO...

Метод 3: Обрабатывайте как разделитель при UNSTRINGing следующим образом:

UNSTRING INPUT_REC DELIMITED BY "," OR x"0D"
    INTO WS_ID_1, WS_ID_2, WS_CODE, WS_DESCRIPTION, WS_FLAG, WS_AMOUNT_TXT

Метод 4: Условие последнего получающего поля из UNSTRING путем замены конечных нецифровых / нецифровых знаков с пробелами.Я изложил это решение чуть раньше в этом вопросе.Вы также можете изучить оператор INSPECT, используя параметр REPLACING (Формат 2).Это должно быть в состоянии сделать почти то же самое - просто замените все x «00» на SPACE и x «0D» на SPACE.

Там, где есть воля, есть путь.Любое из вышеперечисленных решений должно работать на вас.Выберите тот, который вам удобнее всего.

0 голосов
/ 28 июля 2011

^ M - возврат каретки.

Будет ли Google Refine полезным для исправления этих данных?

...