Универсальная процедура манипулирования строками в RPG

Еще одна вещь в RPG, которую я никогда не знаю, как сделать правильно: написание функций/процедур манипулирования строками.

Поскольку в большинстве случаев строки в RPG имеют фиксированную длину (по крайней мере, в наших программах), и, что, возможно, более важно, они всегда имеют конечную длину, я всегда немного теряюсь, когда хочу написать процедуру для общих операций со строками.

Как написать процедуру, которая обрабатывает строку любой длины? Есть ли проблема, если я сделаю это в функциональном стиле (как в text = manip_str(text);)? Работает ли он вообще с разной длиной, если я манипулирую аргументом напрямую (например, manip_str(text);)?

Я опубликую свою попытку в качестве ответа, но там есть некоторые проблемы, в которых я не уверен. Как вы это делаете, ведь я уверен, что многие сталкивались с такой задачей раз или тысячу раз. Приветствуются разные подходы, лучше всего, если вы упомянете проблемы с этими подходами.

Прежде чем вы спросите: у меня есть эта проблема для строк байтов (EBCDIC) как для строк Unicode (UTF-16). Но я могу жить с процедурой дважды, по одному разу для каждого.


person kratenko    schedule 06.03.2013    source источник
comment
Мы называем их строками фиксированной длины. Даже строки переменной длины имеют конечный предел. Если вы найдете систему с бесконечным хранилищем, дайте мне знать. ;-)   -  person WarrenT    schedule 07.03.2013
comment
@WarrenT, конечно, все в компьютерах конечно. Но в RPG я должен принять решение об объявлении: эта строка не будет длиннее x символов. В других языках (высокий, как python, или низкий, как c) я просто использую свою переменную, и мне все равно, 2 символа или 1000000 (или столько, сколько я могу получить часть памяти). Для меня это разница. Возможно, вы могли бы использовать указатели и в RPG, но они не так естественны, как в C или C++.   -  person kratenko    schedule 08.03.2013


Ответы (2)


Большинство символьных переменных в RPG действительно имеют фиксированную длину. Что означает конечную длину. Символ, определенный как 50a, всегда будет содержать ровно 50 символов. Оценка myChar = 'A'; приведет к тому, что myChar будет содержать 50 символов: букву A, за которой следуют 49 пробелов. Это скучно, но важно.

Второй скучный, но важный момент — понять, что память выделяет вызывающая программа, а не вызываемая. Если вызывающий объект объявляет myChar 50a, а вызываемый объект объявляет myParm 65535a, вызывающий объект инициализирует только 50 байт памяти. Если вызываемый объект пытается работать с myParm дальше 50-го байта, он работает с хранилищем, состояние которого неизвестно. Как говорится, могут быть непредсказуемые результаты.

Это предыстория вашего вопроса о подпроцедуре, обрабатывающей символьные переменные, размер которых заранее не известен подпроцедуре. Классический способ справиться с этим — передать не только символьную переменную, но и ее длину. eval myProcedure(myChar: %len(myChar)); Это довольно некрасиво и заставляет каждого вызывающего абонента вычислять длину myChar. Конечно, было бы неплохо, если бы подпроцедура могла опрашивать входящий параметр, чтобы узнать, как его определил вызывающий.

IBM предоставила именно такую ​​возможность с помощью того, что они называют операционными дескрипторами. С операционными дескрипторами вызывающий объект передает метаданные о символьном параметре вызываемому объекту. Его можно получить через CEEDOD API. Пример использования CEEDOD здесь.

По сути, подпроцедура должна объявить, что ей нужны операционные дескрипторы:

 dddeCheck         pr              n   opdesc
 d test                          20a   const options(*varsize)

Затем вызывающая сторона делает обычный вызов подпроцедуры:

if ddeCheck(mtel) = *on;               // 10 bytes
...
endif;
if ddeCheck(mdate: *on) = *on;         // 6 bytes
...
endif;

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

Подпроцедура должна использовать CEEDOD для запроса длины входящего параметра:

     dddeCheck         pi              n   opdesc
     d test                          20a   const options(*varsize)
...
     dCEEDOD           pr
     d parmNum                       10i 0 const
     d descType                      10i 0
     d dataType                      10i 0
     d descInfo1                     10i 0
     d descInfo2                     10i 0
     d parmLen                       10i 0
     d ec                            12a   options(*omit)

     d parmNum         s             10i 0
     d descType        s             10i 0
     d dataType        s             10i 0
     d descInfo1       s             10i 0
     d descInfo2       s             10i 0
     d parmLen         s             10i 0
     d ec              s             12a
...
       CEEDOD (1: descType: dataType: descinfo1: descinfo2: parmlen: *omit);

В этот момент parmlen содержит длину, которую вызывающая сторона определила для входящей переменной. Теперь мы должны что-то сделать с этой информацией. Если мы обрабатываем символ за символом, нам нужно сделать что-то вроде этого:

for i = 1 to parmLen;
  char_test = %subst(test: i: 1);
  ...
endfor;

Если мы обрабатываем одну строку, нам нужно сделать что-то вроде этого:

returnVar = %xlate(str_lc_letters_c: str_uc_letters_c: %subst(s: 1: parmLen));

Важно никогда не ссылаться на входной параметр, если эта ссылка каким-либо образом не ограничена фактической длиной переменной, определенной вызывающей стороной. Эти меры предосторожности необходимы только для переменных фиксированной длины. Компилятор уже знает длину символьных переменных переменной длины.

Что касается того, как компилятор отображает myFixed в myVarying через CONST, поймите, как это работает. Компилятор скопирует все байты из myFixed в MyVarying — все до единого. Если myFixed равно 10a, myVarying станет длиной 10 байт. если myFixed равно 50a, то myVarying станет длиной 50 байт. Конечные пробелы всегда включаются, потому что они являются частью каждой символьной переменной фиксированной длины. Эти пробелы не очень важны для процедуры перевода, которая игнорирует пробелы, но они могут быть важны для процедуры, которая центрирует строку. В этом случае вам нужно будет прибегнуть к операционным дескрипторам или сделать что-то вроде upperVary = str_us(%trimr(myFixed));

person Buck Calabro    schedule 06.03.2013
comment
Спасибо, что указали мне на CEEDOD, мы будем использовать это в будущем для наших сервисных программ. И объяснение фона, отлично! Однако по-прежнему не доволен возвратом строк из процедур. Думаю, вместо этого мы будем манипулировать параметрами, я знаю, что происходит. - person kratenko; 13.03.2013
comment
Я пропустил это ... возможно, вы можете задать новый вопрос, касающийся возврата значений символов в RPG ... что-то с конкретным примером того, что вас беспокоит? - person Buck Calabro; 13.03.2013

Самый гибкий способ передачи строк в RPG, который я нашел, работает со строками 64k-varlength и передачей с *varsize (на самом деле предполагается отправлять только количество байтов в переданной строке, поэтому 64k не должно быть проблемой, я думаю, я нашел что где-то предложил Скотт Клемент). Вот как бы я написал функцию upcase только от A до Z с этим (поскольку это самый простой пример):

 * typedefs:
Dstr_string_t     S          65535A   VARYING TEMPLATE

 * constants:
Dstr_uc_letters_c C                   'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
Dstr_lc_letters_c C                   'abcdefghijklmnopqrstuvwxyz'

 * prototype:
Dstr_uc           PR                  like(str_string_t)       
D s                                   like(str_string_t)       
D                                       options(*varsize) const

 * implementation:
Pstr_uc           B                   export                   
D                 PI                  like(str_string_t)       
D s                                   like(str_string_t)       
D                                       options(*varsize) const
 /free                                                         
  return %xlate(str_lc_letters_c:str_uc_letters_c:s);          
 /end-free                                                     
Pstr_uc           E 

Теперь есть несколько вещей, которые беспокоят меня здесь:

  • Могут ли возникнуть проблемы со строками фиксированной длины, которые я передаю этому?
  • Работает ли это «только столько байтов, сколько необходимо» для возвращаемого значения? Я бы не хотел, чтобы тысячи байтов были зарезервированы и переданы каждый раз, когда я хочу преобразовать 3-символьную строку.
  • Это только гибкий до 64k байт. Но я думаю, что это больше теоретически проблема с нашими программами, по крайней мере, на данный момент...
person kratenko    schedule 06.03.2013