Я использую GnuCOBOL и пишу в файл, организация последовательная, и мне нужно сохранять конечные пробелы. Последовательность строк добавляет символы [CR][LF], но делает это после удаления конечных пробелов.
IDENTIFICATION DIVISION.
PROGRAM-ID. TRAILING-SPACES.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT OUTFILE
ASSIGN 'O:\BATCH_JOBS\TMP\TRAILING-SPACES.COBOL.TXT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD OUTFILE
DATA RECORD IS OUT-RECORD.
01 OUT-RECORD.
05 STUFF PIC X(5).
PROCEDURE DIVISION.
OPEN OUTPUT OUTFILE.
MOVE 'ABCDE' TO STUFF.
WRITE OUT-RECORD.
MOVE 'ABC ' TO STUFF.
WRITE OUT-RECORD.
CLOSE OUTFILE.
STOP RUN.
Результат:
ABCDE[CR][LF]
АБВ[CR][LF]
Как мне получить результат
ABCDE[CR][LF]
ABC[ПРОБЕЛ][ПРОБЕЛ][CR][LF]
Я попытался объявить файл как последовательную организацию и сгенерировать [CR][LF], но, похоже, мне не удалось заставить его работать.
IDENTIFICATION DIVISION.
PROGRAM-ID. TRAILING-SPACES.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT OUTFILE
ASSIGN 'O:\BATCH_JOBS\TMP\TRAILING-SPACES.COBOL.TXT'
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD OUTFILE
DATA RECORD IS OUT-RECORD.
01 OUT-RECORD.
05 STUFF PIC X(5).
05 CR PIC X(1) VALUE X'0D'.
05 LF PIC X(1) VALUE X'0A'.
PROCEDURE DIVISION.
OPEN OUTPUT OUTFILE.
MOVE 'ABCDE' TO STUFF.
WRITE OUT-RECORD.
MOVE 'ABC ' TO STUFF.
WRITE OUT-RECORD.
CLOSE OUTFILE.
STOP RUN.
Результат:
ABCDE[NUL][NUL]ABC[ПРОБЕЛ][ПРОБЕЛ][NUL][NUL]
Всем спасибо!
Я тоже не прикасался к COBOL уже 30 лет. Вы абсолютно правы, и я ожидаю, что COBOL сделает именно это: если буфер слишком мал, то усеките его, а если буфер слишком велик, то дополните его. Что меня ломает голову, так это то, почему запись в файл приводит к удалению конечных пробелов. В других языках давайте просто выберем C#, outfile.writeline("Привет, bdcoder <куча пробелов> "); будет включать пробелы. У него не будет побочных эффектов или каких-то необычных действий... он просто будет делать то, что вы ему скажете. Но спасибо за комментарий и за то, что поделились!
ORGANIZATION LINE SEQUENTIAL
был стандартизирован в COBOL2023, но в то же время в нескольких реализациях все было по-другому.
Для этого в общих правилах для WRITE
указано:
Для последовательного построчного файла с записью описания файла, не содержащей предложения
RECORD
с фразойDEPENDING
, любые пробелы справа от крайнего правого символа, не являющегося пробелом, не переносятся в имя_файла-1.
GnuCOBOL по умолчанию следует стандарту, но позволяет вам настроить поведение, см. руководство в разделе «Приложение I Конфигурация среды выполнения, ввод-вывод файла» (или конфигурацию среды выполнения runtime.cfg
, см. cobcrun --runtime-config
, чтобы проверить, где она находится):
Environment name: COB_LS_FIXED
Parameter name: ls_fixed
Purpose: Defines if LINE SEQUENTIAL files should be fixed length
(or variable, by removing trailing spaces)
Alias: STRIP_TRAILING_SPACES (0 = yes)
Type: boolean
Default: false
Note: This setting is most useful if you want to REWRITE those
files.
Example: LS_FIXED TRUE
Environment name: COB_LS_VALIDATE
Parameter name: ls_validate
Purpose: Defines for LINE SEQUENTIAL files that the data should be
validated as it is read (status 09) / written (status 71).
Type: boolean
Default: true (per COBOL 2022)
Note: If active effectively disables COB_LS_NULLS.
Example: LS_VALIDATE FALSE
Environment name: COB_LS_NULLS
Parameter name: ls_nulls
Purpose: Defines for LINE SEQUENTIAL files what to do with data
which is not DISPLAY type. This could happen if a LINE
SEQUENTIAL record has BINARY/COMP data fields in it.
Type: boolean
Default: false
Note: The TRUE setting will insert a null character x"00" before
those values to escape them, and redo on read-in plus
validating that they only occur after a null character.
Decreases LINE SEQUENTIAL performance and prevents writing
escape sequences or formatting within the data.
Only checked if COB_LS_VALIDATE is disabled.
Example: LS_NULL = TRUE
Environment name: COB_LS_SPLIT
Parameter name: ls_split
Purpose: Defines for LINE SEQUENTIAL files what to do when a record
is longer than the program handles. If 'ls_split=true' then
the data is returned as multiple records with io status 06,
otherwise the record is truncated, io status set to 04 and
the file skips to the next LF.
Type: boolean
Default: true (per COBOL 2022)
Example: LS_SPLIT = FALSE
Если вы хотите изменить это глобально, отредактируйте свой runtime.cfg (или, что лучше, создайте собственный), включив в него
ls_fixed = true
ls_split = false
в качестве альтернативы: просто поместите это в окружающую среду
SET COB_LS_FIXED=1
SET COB_LS_SPLIT=0
или сделайте это из программы COBOL перед обработкой файла:
SET ENVIRONMENT "COB_LS_FIXED" TO "1"
SET ENVIRONMENT "COB_LS_SPLIT" TO "0"
Но имейте в виду, что это также произойдет, если у вас будут записи огромной длины.
Поэтому, вероятно, лучше вместо этого скорректировать запись описания файла (и, возможно, избавиться от стандарта «больше нет» DATA RECORD
, который уже дублируется записью под FD
):
FD OUTFILE
RECORD IS VARYING IN SIZE FROM 1 TO 5 BYTES
DEPENDING ON OUTFILE-LEN.
с
77 OUTFILE-LEN PIC 99 COMP-5 VALUE LENGTH OF OUT-RECORD.
в WORKING-STORAGE SECTION
(в вашем примере предварительно установлено значение 5, но вы также можете указать это перед WRITE
). Примечание. Длина будет автоматически установлена на READ
в соответствии с тем, что найдено в файле.
Это также должно быть более переносимо в другие среды COBOL.
Ух ты! Спасибо, Саймон, за образование. Это сработало! Я внес предложенные вами изменения в FD и добавил 77 уровень. После реализации ваших предложений, но без установки переменных среды, усечение продолжалось, но с переменными (а также FD и 77) все работало нормально. Я использую GnuCOBOL 3.2.0, и ему не нравятся BYTES в FD. Он жалуется на «ошибку: «BYTES» является зарезервированным словом, но не поддерживается»; Я заменил его ПЕРСОНАЖАМИ. Я еще раз смиренно благодарю вас за понимание и за то, что поделились с нами своим опытом.
01 OUT-RECORD.
05 STUFF PIC X(5).
05 CR PIC X(1) VALUE X'0D'.
05 LF PIC X(1) VALUE X'0A'.
Эти пункты VALUE
игнорируются в FILE SECTION.
Есть несколько способов это изменить, но самый простой — разместить код в WORKING-STORAGE SECTION.
01 OUT-RECORD PIC X(7).
WORKING-STORAGE SECTION.
01 WS-OUT-RECORD.
05 STUFF PIC X(5).
05 CR PIC X(1) VALUE X'0D'.
05 LF PIC X(1) VALUE X'0A'.
Затем
MOVE "ABC" TO STUFF
WRITE OUT-RECORD FROM WS-OUT-RECORD
Это не совсем так. Предложения VALUE
просто не меняют начальное содержимое памяти (и по стандарту память определяется как доступная только после успешного OPEN
). Они полностью используются для INITIALIZE OUT-RECORD ALL TO VALUE
, который можно использовать после OPEN
. ... но главный тезис "их нет (автоматически) в памяти, понятнее использовать WS и WRITE FROM
" однозначно верен.
О, вот почему! Я не знал «переменных» в FD, а переменные в рабочей памяти обрабатываются по-другому. Я просто подумал, что переменные — это переменные, которые есть переменные (с учетом правил области видимости). Например, в C вы можете объявить переменную практически где угодно. Рик и Саймон: спасибо вам обоим за содержательные и чрезвычайно полезные ответы!
Просто предполагаю, поскольку я не использовал COBOL почти 35 лет, но согласно документации: при чтении файла «LINE SEQUENTIAL» записи, превышающие размер, указанный в описании файла в «РАЗДЕЛЕ ФАЙЛА», будут обрезаны, а записи короче этого размера будет дополнено справа «ПРОБЕЛАМИ» - поэтому в вашем случае, когда запись будет прочитана обратно, она будет содержать конечные пробелы.