заменить несколько запятых за пределами одного или нескольких наборов фигурных скобок и исключение, которое находится в одном или нескольких наборах фигурных скобок

заменить несколько запятых за пределами одного или нескольких наборов фигурных скобок и исключение, которое находится в одном или нескольких наборах фигурных скобок

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

Мне нужно:

  1. Если запятая находится за пределами одной или нескольких фигурных скобок, ее следует заменить вертикальной чертой.

  2. Если запятая находится внутри одного или нескольких наборов фигурных скобок, запятую следует оставить. Таким образом, THING1,{THING2,{THING3,}},THING4вывод должен быть THING1|{THING2,{THING3,}}|THING4.

Образец записи:

(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})

результат должен быть:

(**999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|**{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}**|NULL|**{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}**|**{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}**|NULL|NULL|NULL|NULL|NULL|**{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})

решение1

Это можно сделать просто с помощью комбинации Perl+ regex.

perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file

Пример:

$ perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file
(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})

Объяснение:

Для пояснения я разделил регулярное выражение на две части.

  1. (\{(?:[^{}]|(?1))*\})
  2. (*SKIP)(*F)|,

1-я часть

(\{(?:[^{}]|(?1))*\})
  • Этот трюк сработает только в том случае, если фигурные скобки правильно соединены.
  • ()Это группы захвата, используемые для захвата персонажей.
  • \{соответствует открывающейся фигурной скобке.
  • (?:[^{}]|(?1))

    • (?:...)Называется «незахватывающая группа».
    • [^{}]Это будет соответствовать любому символу, кроме {или}
    • |Логический оператор ИЛИ.
    • (?1)Рекурсирует первую группу захвата.
  • (?:[^{}]|(?1))*Совпадение с предыдущим токеном ноль или более раз.
  • \}Закрывающий }символ.

Рассмотрим приведенный ниже пример и шаблон, который соответствует вложенным скобкам в нем.

Нить:

h{foo{bar}foobar}

Шаблон:

h(\{(?:[^{}]|(?1))*\})
  • Сначала движок регулярных выражений пытается сопоставить h(который был в шаблоне) против входной строки. Таким образом, первая буква hсовпала.
  • Шаблон для поиска сбалансированных скобок передается в группу захвата.
  • Теперь движок берет второй символ (т.е. \{) в шаблоне и пытается сопоставить его с входной строкой. Так что первый {получилзахвачен. Я использовал слово «захвачен» вместо «совпадение», потому что \{оно находится внутри группы захвата.
  • (?:[^{}]|(?1))*Это говорит движку регулярных выражений сопоставлять любой символ, кроме {или }ноль или более раз. Если вы нашли любой символ {или, }то рекурсия к первой группе захвата еще один раз. Итак, теперь строка fooзахвачена. Следующий символ — {, поэтому она рекурсия к первой группе захвата. Теперь движок регулярных выражений на один уровень ниже в рекурсии. Какой первый шаблон в нашей первой группе захвата (см. регулярное выражение)? \{Теперь он соответствует {символу, который был сразу после строки foo.
  • Движок все еще на один уровень глубже в рекурсии, снова шаблон (?:[^{}]|(?1))*соответствует строке bar. Теперь символ после barявляется }, поэтому после сопоставления строки barдвижок регулярных выражений не попадет в , (?1)поэтому мы сделали так, чтобы группа без захвата повторяласьнульили более раз. Следующий шаблон (шаблон после того, как(?:[^{}]|(?1))*) в регулярном выражении равно \}. Так что это \}будет соответствовать }фигурной скобке, которая была сразу после to bar. Теперь движок регулярных выражений выйдет на один уровень глубже в рекурсии, и шаблон [^{}]*будет соответствовать следующей строке foobar. Last \}будет соответствовать последней фигурной скобке.
  • Теперь наша первая группа захвата содержит {foo{bar}foobar}.

2-я часть

  • (*SKIP)(*F)Вызывает сбой символов, которые сопоставляются или захватываются. Так что в нашем случае все захваченные сбалансированные фигурные скобки были пропущены. То есть, это заставляет движок регулярных выражений сопоставлять символы из оставшейся строки.
  • Синтаксис или формат(*SKIP)(*F)

        part1(*SKIP)(*F)|part2
         |                  |
     |----                  -----> Match this
    Don't match this 
    
  • Таким образом, шаблон, который находится сразу после, |будет пытаться сопоставить символы из оставшейся строки (строка, за исключением вложенных фигурных скобок).

  • В нашем случае шаблон после |. ,Таким образом, все запятые, которые находятся вне вложенных скобок, были сопоставлены.

Читатьэтотчтобы понять Regular Expression Recursion.

Примечание:

  • (?R)рекурсирует весь подшаблон, т.е. все совпадение. Мы также могли бы написать (?R)как(?0)
  • (?1)рекурсирует первый подшаблон (т.е. шаблон внутри первой захватывающей группы)

решение2

заменять

,{ 

с

|{ 

и

}, 

с

}|

 echo "THING1,{THING2,{THING3,}},THING4" | sed -re "s/,\{/|{/gi" | sed -re "s/},/}|/gi"

результаты в

THING1|{THING2|{THING3,}}|THING4

решение3

Не бойтесь, это жесткое sedутверждение. Но оно должно учитывать каскадирование. Вот оно в одну строку:

sed -e 's/,/|/g;:a;s/{\([^{}]*\)|\([^{}]*\)}/{\1,\2}/g;ta;s/{\([^{}]*\)}/<\1>/g;ta;:b;s/<\([^<>]*\)>/{\1}/g;tb' file

Вот прокомментированная версия:

sed -e '
        s/,/|/g;                                 #replaces all commas (,) with pipes (|)
        :a;                                      #sets a label called a
            s/{\([^{}]*\)|\([^{}]*\)}/{\1,\2}/g; #replaces {a|b|c} with {a,b|c}
          ta;                                    #go back to the label `a` and repeat the
                                                 #prevous part until there is nothing more
                                                 #to replace: when {a|b|c} became {a,b,c}
          s/{\([^{}]*\)}/<\1>/g;                 #replace {...} with <...>
        ta;                                      #go back to label a again until all {} are 
                                                 #replaces by <>
        :b;                                      #create a new label called b
          s/<\([^<>]*\)>/{\1}/g;                 #replace <...> back to {...}
        tb;                                      #and back to label b to repeat the previous
                                                 #part
' file

Благодаря этому я получил желаемый результат.

решение4

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

sed 's/^/\n/;:b
/\n\n/!s/\(\n[^,{}]*\),/\1|/;tb
s/\(\n\n*\)\([^{}]*[{}]\)/\2\1/
s/{\(\n\)/&\1/;s/\(}\n\)\n/\1/;tb
s/\n//g' <<\DATA
(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL},poopoer,sciioooper)
DATA

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

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

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

Вывод вашего образца:

(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL}|poopoer|sciioooper)

Связанный контент