REGEXP для вставки специальных символов, а не удаления

Как бы я поставил двойные кавычки вокруг двух полей, которые отсутствуют? Смогу ли я использовать INSTR/SUBSTR/REPLACE в одном выражении, чтобы выполнить это?

string := '"ES26653","ABCBEVERAGES","861526999728",606.32,"2017-01-26","2017-01-27","","",77910467,"DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA","NE","68144"';

Expected string := '"ES26653","ABCBEVERAGES","861526999728","**606.32**","2017-01-26","2017-01-27","","","**77910467**","DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA","NE","68144"';

Пожалуйста, предложите! Спасибо.


person QuickDrawMcgraw    schedule 14.04.2017    source источник
comment
Откуда эта строка? Это результат SELECT? Содержимое одного столбца в вашей базе данных? То, как вы его получаете, влияет на то, как вы можете с ним работать. Вы ссылаетесь на два поля, но не объяснили, что означает поля. Я предполагаю, что это строка в файле .csv, но вы не сказали ничего, кроме string. Ваш вопрос должен быть конкретным.   -  person Ken White    schedule 14.04.2017
comment
Это файл с разделителями. Это хранимая процедура, которая анализирует его и сохраняет. Я просто вытащил одну строку в качестве примера. Проблема с синтаксическим анализом с использованием разделителя , заключается в том, что поле в двойных кавычках может иметь вид 123 South St, Apt 1, что испортит позиционные значения.   -  person QuickDrawMcgraw    schedule 14.04.2017
comment
Тьфу, у нас есть поставщики, которые настаивают на отправке/получении данных в этом формате. Какая бесполезная трата места, если данные могут содержать запятую! Просто используйте вертикальную черту в качестве разделителя (крайне маловероятно, что она будет в ваших данных) и избавьтесь от двойных кавычек и всех проблем, которые они вызывают. Я виню пользователей электронных таблиц. хе   -  person Gary_W    schedule 14.04.2017
comment
Поэтому отредактируйте свой пост и поместите туда эту информацию, вместо того, чтобы хоронить ее в беспорядке комментариев. Это относится к вашему вопросу.   -  person Ken White    schedule 14.04.2017


Ответы (3)


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

Один довольно грубый метод для внутренних полей:

replace(replace(string, ',', '","'), '""', '"')

Это добавляет двойные кавычки по обе стороны от запятой, а затем удаляет двойные двойные кавычки. Вам не нужно беспокоиться о "". Он становится """", а затем возвращается к "".

Это можно адаптировать и для первого, и для последнего полей, но это усложняет выражение.

person Gordon Linoff    schedule 14.04.2017
comment
Но что, если строка имеет вид «ES26653,ABCBEVERAGES, INC,861526999728,606.32,2017-01-26,2017-01-27,,,77910467,DOROTHY,,RAPP,14219 PIERCE STREET, APT1, ОМАХА, NE, 68144 ' - person QuickDrawMcgraw; 14.04.2017
comment
@QuickDrawMcgraw . . . Я вижу, у вас есть запятая в поле в вашем примере. Увы, в вашем случае это не сработает. - person Gordon Linoff; 14.04.2017

Это предложение пытается решить ряд конечных случаев:

  • Решение проблем с первым и последним полями. Здесь только последнее поле является особым случаем, так как мы ищем конец строки $, а не запятую.
  • Пустые поля без кавычек, т. е. запятые в начале, запятые подряд и запятые в конце.
  • Сохранение пары двойных кавычек в поле, представляющем одну двойную кавычку.

SQL:

WITH orig(str) AS (
     SELECT '"ES26653","ABCBEVERAGES","861526999728",606.32,"2017-01-26","2017-01-27","","",77910467,"DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA","NE","68144"'
     FROM dual
   ),
   rpl_first(str) AS (
     SELECT REGEXP_REPLACE(str, '("(([^"]|"")*)"|([^,]*))(,|$)','"\2\4"\5') 
   FROM orig
   )
   SELECT REGEXP_REPLACE(str, '"""$','"') fixed_string
   FROM rpl_first;

Техника заключается в том, чтобы найти либо поле в кавычках и запомнить его, либо поле без кавычек и запомнить его, заканчивающееся запятой или концом строки, и запомнить это. Затем ответ представляет собой ", за которым следует одно из полей, за которым следует ", а затем терминатор.

Поле в кавычках — это в основном "[^"]*", где [^"] — любой символ, не являющийся кавычкой, а * повторяется ноль или более раз. Это усложняется тем фактом, что символ без кавычек также может быть парой кавычек, поэтому нам нужна конструкция ИЛИ (|), то есть "([^"]|"")*". Однако мы должны помнить только поле внутри кавычек, поэтому добавьте скобки, чтобы позже мы могли ссылаться только на это, то есть "(([^"]|"")*)".

Поле без кавычек — это просто запятая, повторяющаяся ноль или более раз, когда мы хотим запомнить все ([^,]*).

Итак, мы хотим найти любую из них, снова конструкцию ИЛИ, то есть ("(([^"]|"")*)"|([^,]*)). За которым следует терминатор, либо запятая, либо конец строки, который мы хотим запомнить, то есть (,|$).

Теперь мы можем заменить его одним из двух типов полей, заключенных в кавычки, за которыми следует терминатор, т. е. "\2\4"\5. Число n для обратной ссылки \n — это просто вопрос подсчета открытых скобок.

Второй REGEXP_REPLACE должен обойти то, что, как я подозреваю, является ошибкой Oracle. Если последнее поле заключено в кавычки, то в конец строки добавляется дополнительная пара кавычек. Это говорит о том, что конец строки обрабатывается дважды при разборе, что может быть ошибкой. Однако обработка регулярных выражений, вероятно, выполняется стандартной библиотечной процедурой, поэтому это может быть моя интерпретация правил регулярных выражений. Комментарии приветствуются.

Документацию по регулярным выражениям Oracle можно найти по адресу Использование регулярных выражений в приложениях баз данных.

Спасибо @Gary_W за его шаблон. Здесь я держу два отдельных блока регулярных выражений, чтобы отделить бит, который я могу объяснить, от бита, который я не могу (ошибка?).

person Unoembre    schedule 16.04.2017

Этот метод делает 2 прохода по строке. Сначала найдите группу из двойной кавычки, за которой следует запятая, а затем символ, не являющийся двойной кавычкой. Замените их, сославшись на них сокращением их группы, первая группа, '\1', отсутствующая двойная кавычка, вторая группа '\2'. Затем сделайте это снова, но наоборот. Конечно, вы можете вложить вызовы regex_replace и получить один большой уродливый оператор, но просто сделайте его двумя операторами для упрощения обслуживания. Парень, работающий над этим после вас, поблагодарит вас, и это уже достаточно уродливо.

SQL> with orig(str) as (
     select '"ES26653","ABCBEVERAGES","861526999728",606.32,"2017-01-26","2017
-01-27","","",77910467,"DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA
","NE","68144"'
     from dual
   ),
   rpl_first(str) as (
     select regexp_replace(str, '(",)([^"])', '\1"\2')
   from orig
   )
   select regexp_replace(str, '([^"])(,")', '\1"\2') fixed_string
   from rpl_first;

FIXED_STRING
--------------------------------------------------------------------------------

"ES26653","ABCBEVERAGES","861526999728","606.32","2017-01-26","2017-01-27","",""

,"77910467","DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA","NE","681

44"


SQL>

РЕДАКТИРОВАТЬ: изменены регулярные выражения и добавлен третий шаг, чтобы разрешить пустые поля без кавычек для комментария Unoembre. Хороший улов! Также добавлены дополнительные тестовые случаи. Всегда ожидайте неожиданного и обязательно добавляйте тестовые примеры для всех комбинаций данных.

SQL> with orig(str) as (
        select '"ES26653","ABCBEVERAGES","861526999728",606.32,"2017-01-26","2
017-01-27","","",77910467,"DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OM
AHA","NE","68144"'
        from dual union
        select 'ES26653,"ABCBEVERAGES","861526999728"' from dual union
        select '"ES26653","ABCBEVERAGES",861526999728' from dual union
        select '1S26653,"ABCBEVERAGES",861526999728' from dual union
        select '"ES26653",,861526999728' from dual
      ),
      rpl_empty(str) as (
        select regexp_replace(str, ',,', ',"",')
        from orig
      ),
      rpl_first(str) as (
        select regexp_replace(str, '(",|^)([^"])', '\1"\2')
      from rpl_empty
      )
      select regexp_replace(str, '([^"])(,"|$)', '\1"\2') fixed_string
      from rpl_first;

FIXED_STRING
--------------------------------------------------------------------------------

"ES26653","ABCBEVERAGES","861526999728","606.32","2017-01-26","2017-01-27","",""

,"77910467","DOROTHY","","RAPP","14219 PIERCE STREET, APT1","","OMAHA","NE","681

44"

"ES26653","ABCBEVERAGES","861526999728"
"ES26653","","861526999728"
"1S26653","ABCBEVERAGES","861526999728"
"ES26653","ABCBEVERAGES","861526999728"

SQL>
person Gary_W    schedule 14.04.2017
comment
Это не работает с начальными или конечными полями без кавычек или пустыми полями без кавычек. - person Unoembre; 16.04.2017
comment
Спасибо, исправил мой ответ. - person Gary_W; 16.04.2017