Показаны сообщения с ярлыком string. Показать все сообщения
Показаны сообщения с ярлыком string. Показать все сообщения

суббота, 26 мая 2012 г.

Строковые литералы и символьные литералы. Пробел

Хотя ABAP позволяет практически незаметно и безболезненно конвертировать одни типы  данных в другие, а типы  STRING и C вообще считает чуть ли не синонимами, все таки бывают ситуации, когда в их поведении есть отличия. Одним из таких отличий является то, как типы string  и char  воспринимают пробел.

На само деле С (char) - это всего 1 символ, так что правильнее говорить об элементе данных  большей длины, но с внутренним типом C - например CHAR10. Фактически CHAR10 - это "статический" массив из 10 символов типа C, в тоже время STRING - это "динамический" массив ( то есть с переменной длиной) символов типа C.

Рассмотрим следующий код:
DATA lv_char TYPE char10 VALUE '12345'.
DATA lv_str TYPE string VALUE '12345'.

Здесь переменная lv_char хранит значение, равное '12345     '- то есть символы от 1 до 5 и еще 5 пробелов, потому что у нее фиксированная длина 10 символов.  Переменная lv_str хранит только символы '12345'.

А теперь следующий код:
DATA lv_char TYPE char10 VALUE  space.
DATA lv_str TYPE string VALUE space.


space - это заменитель ' '.
Здесь переменная lv_char хранит значение, равное 10 пробелам.  Переменная lv_str хранит '', то есть ничего. Не один пробел, а именно ничего. Длина строки = 0.

Ну с lv_char думаю все понятно. Почему lv_str не хранит пробел?
Потому что мы инициализируем ее символьным литералом (символьной константой) - то есть неким набором символом (в данном случае пробелом), заключенным между одинарных кавычек. Прикол в том, что в символьных литералах граничные  пробелы удаляются. Поскольку у нас ничего кроме пробела нет, то строка в итоге ничего и не хранит.

Рассмотрим пример, в котором проявляются особенности переменных типа CHAR* и сжатия пробелов.
Допустим у нас извне приходит некое значение в переменной lv_char типа CHAR10 - пользователь что-то вводит, IDOC пришел - что угодно. Нам нужно проверить, что это значение содержит только цифры. Если это не так, выдавать ошибку, писать логи - что угодно. Не суть важно.
Как это можно проверить? Например, так:
IF lv_char CO '0123456789'.
  "только цифры.
ELSE.
  "что-то еще
ENDIF.

Теперь допустим  нам приходит в переменной lv_char число 42. Но после проверки мы попадаем в ветку ELSE. Удивительно? На первый взгляд да - ведь в переменной только цифры 4 и 2, проверка должна проходить. Но потом становится понятно, что на самом деле не только 4 и 2, а еще и 8 пробелов. Ладно, меняем условие на такое, что в переменной допускаются цифры и пробелы:
IF lv_char CO '0123456789 '. "После 9 стоит пробел

Но программа  все равно упорно лезет в ветку ELSE. Ну поскольку про сжатие пробелов Вы уже в курсе, то можете догадаться, что наш трюк с добавлением пробела не сработал, потому что компилятор его все равно выкинул и в итоге в скомпилированном коде у нас все также проверка на 1-9. О пробелах ни слова, ни байта.

Ну это все замечательно, но что делать? Вариантов несколько:

  1. Поставить пробел не в конце, а, например, посередине:
    IF lv_char CO '01234 56789'"После 4 стоит пробел. 
    Поможет в данном случае, поскольку пробелы нам фактически не нужны - мы просто обходим особенность CHAR, но могут быть случаи, когда пробел ключевая фигура - ниже я приведу пример.
  2. Помещать lv_char предварительно в переменную типа string. Это также прокатит, если вам фактически пробел не нужен. Но если нужен - то нет. Причем в отличии от п.1 это еще и лишняя переменная, лишнее присвоение, да и лишняя писанина.
  3. Использовать не символьный литерал, а строковый. Строковый в коде задается набором символов ограниченным апострофами (на русской клавиатуре он обычно на кнопке с буквой Ё). Напомню, символьный ограничивается одинарной кавычкой (на кнопке с Э). В строковых литералах пробелы НЕ сжимаются - что ввели, то и будет в коде. То есть, в нашем примере код может быть таким:
    IF lv_char CO  `0123456789 `"После 9 стоит пробел
Теперь обещанный пример, когда пробел ключевая фигура. Задача найти первую позицию с пробелом в строке . Пишем например так:
FIND ' ' IN '12345 Text' MATCH OFFSET lv_offset MATCH LENGTH lv_length.
В результате в lv_offset у нас будет 0, а не ожидаемое 5. Почему? Все потому же - ' ' сжался до пустого места, а пустое место у нас где угодно, а первое пустое место в начале строки. Оба первых пункта из решения выше нам не подойдут здесь, только 3ий:
FIND ` ` IN '12345 Text' MATCH OFFSET lv_offset MATCH LENGTH lv_length.