
텍스트 파일에는 여러 레코드가 있습니다. 모든 레코드에는 쉼표로 구분된 여러 열이 있으며, 일부 열에는 중괄호 세트가 하나 있고 다른 열에는 중괄호가 두 개 이상 있습니다.
나는 필요하다:
하나 이상의 중괄호 세트 외부에 쉼표가 있는 경우 쉼표를 파이프로 바꿔야 합니다.
하나 이상의 중괄호 안에 쉼표가 있는 경우 쉼표는 그대로 두어야 합니다. 따라서
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))*\})
(*SKIP)(*F)|,
첫 번째 부분
(\{(?:[^{}]|(?1))*\})
- 이 트릭은 중괄호가 올바르게 쌍을 이루는 경우에만 작동합니다.
()
캐릭터를 캡처하는 데 사용되는 캡처 그룹입니다.\{
여는 중괄호와 일치합니다.(?:[^{}]|(?1))
(?:...)
비캡처 그룹이라고 합니다.[^{}]
{
모든 문자와 일치하지만 또는 와 일치하지 않습니다 .}
|
논리 OR 연산자.(?1)
첫 번째 캡처 그룹을 반복합니다.
(?:[^{}]|(?1))*
이전 토큰을 0회 이상 일치시킵니다.\}
종료}
기호.
아래 예제와 그 안에 중첩된 대괄호와 일치하는 패턴을 고려하세요.
끈:
h{foo{bar}foobar}
무늬:
h(\{(?:[^{}]|(?1))*\})
- 처음에 정규식 엔진은
h
(패턴에 있던거) 입력 문자열에 대해. 그래서 첫 글자h
가 일치했습니다. - 균형 잡힌 괄호를 찾기 위한 패턴은 캡처 그룹에 제공됩니다.
- 이제 엔진은
\{
패턴의 두 번째 문자(예: )를 가져와 입력 문자열과 일치시키려고 합니다. 그래서 먼저{
얻은캡처.\{
은(는) 캡처 그룹 내에 있기 때문에 일치라는 단어 대신 캡처라는 단어를 사용했습니다 . (?:[^{}]|(?1))*
이는 정규식 엔진이 0회 이상의 문자를{
제외한 모든 문자와 일치하도록 지시합니다. 또는 문자}
를 찾은 경우 첫 번째 캡처 그룹을 다시 한 번 반복합니다. 이제 문자열이 캡처되었습니다. 다음 문자는 이므로 첫 번째 캡처 그룹으로 재귀됩니다. 이제 정규식 엔진은 재귀에서 한 수준 아래입니다. 첫 번째 캡처 그룹의 첫 번째 패턴은 무엇입니까({
}
foo
{
정규식을 참조하세요)? 입니다 . 이제 문자열 바로 뒤에 있는 기호와\{
일치합니다 .{
foo
- 엔진은 여전히 재귀의 한 수준 깊이에 있으며 패턴은
(?:[^{}]|(?1))*
문자열과 일치합니다bar
. 이제bar
is 다음의 문자}
이므로 string 을 일치시킨 후에bar
정규식 엔진이 들어가지 않으므로(?1)
비캡처 그룹을 반복하도록 만들었습니다.영또는 그 이상. 다음 패턴(이후의 패턴(?:[^{}]|(?1))*
) 정규식에는 가 있습니다\}
. 따라서 이것은 to 바로 뒤에 있는 중괄호\}
와 일치합니다 . 이제 정규식 엔진은 재귀의 한 수준에서 벗어나고 패턴은 다음 문자열과 일치합니다 . 마지막은 마지막 중괄호와 일치합니다.}
bar
[^{}]*
foobar
\}
- 이제 첫 번째 캡처 그룹에는
{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)