Разделить разделенный запятыми список, игнорирующий запятые в соответствии с {}

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

Предположения:

  • Всегда будут совпадать пары фигурных скобок. То есть, ввод, такой как {{ {a,b,c}, x , не будет происходить.

Ожидаемый результат:

 Word='{0,1}' Word='alpha' Word='{(x,y,z)}' Word='{{1,2,3}, {a,b,c}}' 

Рекомендации:

  • Как разбить список запятой, а не пробелом
  • В случае, если это имеет значение, я нахожусь в Mac OS 10.9.5.

Код:

 #!/bin/bash #TEST_STRING="alpha, beta, gamma" ## <--- works great for simple case TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}" echo "${TEST_STRING}" | sed -n 1'p' | tr ',' '\n' | while read Extracted_Word; do printf "Word='%s'\n" "${Extracted_Word}" done 

Я попытался адаптировать 123-е (теперь удаленное) решение:

 #!/bin/bash #TEST_STRING="alpha, beta, gamma" ## <--- works great for simple case TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}" echo "${TEST_STRING}" \ | sed -n 1'p' \ | sed 's/\({[^}]*\({[^}]*}[^}]*\)*} *\)\(,\|$\) */\1\n/g;:1;s/\(\n[^{}]*\), */\1\n/;t1' \ | tr ',' '\n' \ | while read Extracted_Word; do printf "Word='%s'\n" "${Extracted_Word}" done 

но для меня появляется следующее сообщение об ошибке:

 ./testcsv.sh sed: 1: "s/\({[^}]*\({[^}]*}[^}] ...": bad flag in substitute command: ':' ./testcsv.sh: line 18: {{ {a,b,c}, x: command not found 

  • разбивать строку на путь и приложение
  • Разделить текстовый файл на строки с фиксированным числом слов
  • Как разбить изображение по вертикали с помощью строки comand?
  • Транспортировка тара, чтобы разделить трубопровод на scp
  • разбивать файл на несколько частей
  • Перечислите дни рождения в том порядке, в котором они придут с сегодняшнего дня
  • Разделить файл на две строки перед шаблоном
  • K3b для разделения громкости на носители
  • 4 Solutions collect form web for “Разделить разделенный запятыми список, игнорирующий запятые в соответствии с {}”

    Попробуйте чистый баш

     #!/bin/bash TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}" TEST_STRING="$TEST_STRING""," count=0 newword='' while [ "${TEST_STRING::1}" ] ; do l="${TEST_STRING::1}" TEST_STRING=${TEST_STRING:1} [ "$l" = '{' ] && ((count++)) [ "$l" = '}' ] && ((count--)) if [ "$l" = ',' ] && ! ((count)) ; then echo "Word='$newword'" newword='' else if [ "$newword" ] || [ "$l" != " " ] ; then newword="$newword""$l" fi fi done 

    Вот сценарий sed, который разделит ваш пример:

     #!/bin/sed -Ef # replace all commas with newlines s/,/\ /g # Do we need to re-join any lines? :loop # Unmatched brace containing possibly another (matched) level of # braces: s/(\{([^{}]|\{[^{}]*\})*)\ /\1,/ tloop # remove any leading space s/\n */\ /g # At first line, print result, then exit. 1q 

    Предостережение: оно будет обрабатывать только два уровня фигурных скобок (согласно комментариям к вопросу).

    тесты:

     $ ./259252.sed <<<'{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}' {0,1} alpha {(x,y,z)} {{1,2,3}, {a,b,c}} 

    И чтобы показать, что он выходит после обработки первой строки:

     $ ./259252.sed <<<$'a,b,c\nd,e,f' a b c 

    Я запускаю это в Linux и используя ответы на Различия между sed на Mac OSX и другом «стандартном» sed? для его переноса в MacOS. Если это не сработает, то этот ответ подсказывает, что вы можете установить GNU sed с помощью brew install gnu-sed , а затем использовать gsed вместо sed для его вызова.

    В использовании:

     #!/bin/bash TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}" echo "${TEST_STRING}" | sed -E -f 259252.sed | while read Extracted_Word; do printf "Word='%s'\n" "${Extracted_Word}" done 

    который дает:

     Word='{0,1}' Word='alpha' Word='{(x,y,z)}' Word='{{1,2,3}, {a,b,c}}' 
     str='{0,1},alpha,{(x,y,z)},{{1,2,3},{a,b,c}}' OPTIND=1 l=0 r=0; set "" while getopts : na -"$str" do [ "$l" -gt "$r" ] case $?$OPTARG in (1,) ! l=0 r=0 ;; (0}) r=$((r+1)) ;; (?{) l=$((l+1)) ;; esac && set -- "$@$OPTARG" || set -- "$@" "" done; printf %s\\n "$@" 

    dash есть ошибка, которая требует чего-то вроде:

     set -- "$@" ""; str=${str#?} 

    … но в стороне от этого выше должно быть довольно быстро, как это происходит, и работать в основном любой оболочки POSIX, кроме того, что это довольно просто. Он также должен обрабатывать несогласованные пары (даже если вам это не нужно) , пренебрегая специально распознающим a } который встречается перед ведущим { .


     {0,1} alpha {(x,y,z)} {{1,2,3},{a,b,c}} 

    Чтобы получить префиксную строку и окружающие цитаты, вы можете заменить следующие …

     printf "Word='%s'\n" "$@" 

    … для используемого выше printf %s\\n "$@" . Учитывая пример значения $str здесь, он будет печатать:

     Word='{0,1}' Word='alpha' Word='{(x,y,z)}' Word='{{1,2,3},{a,b,c}}' 

    Более уверенно вы можете сделать …

     for W do alias "Word=$W" Word; done 

    … что бы сделать …

     Word='{0,1}' Word=alpha Word='{(x,y,z)}' Word='{{1,2,3},{a,b,c}}' 

    … цитируется по мере необходимости и корректно цитирует встроенные кавычки (хотя, если вы используете bash , вы можете сначала выполнить set --posix ) .

    Итак, ради демонстрации …

     str="{0,1 }}, {,}alph}'a, {(x,y,z)}, {{1,2,3}, {a,b,c}}" OPTIND=1 l=0 r=0; set "" while getopts : na -"$str" do [ "$l" -gt "$r" ] case $?$OPTARG in (1,) ! l=0 r=0 ;; (0}) r=$((r+1)) ;; (?{) l=$((l+1)) ;; esac && set -- "$@$OPTARG" || set -- "$@" "" done; for W do alias "Word=${W# }" Word done 

     Word='{0,1 }}' Word='{,}alph}'\''a' Word='{(x,y,z)}' Word='{{1,2,3}, {a,b,c}}' 

    … где даже ведущие пространства обрабатываются довольно просто …

    Дополнительное решение bash:

    • Он будет обрабатывать непревзойденные пары фигурных скобок { .
    • Не будет принимать закрывающую фигуру, пока не появится одна или несколько открывающих скобок.
    • Сбросит количество фигурных скобок до 0 в конце строки.
    • Будет принимать запятую как действительную после более закрывающих фигурных скобок, чем открытие фигурных скобок.
    • Удалит одно пространство перед решением.
    • Процитирует полученное слово.

    Код:

     str="}}{0,1}}, {,}alph}'a" fin='false' d='0' until $fin do IFS= read -r -d '' -n 1 a || fin='true' if [[ $a == '{' ]] ; then (( d++ )) ; fi ### count openning braces. if [[ $a == ',' ]] && (( d<1 )) || $fin ### ',' out of braces or end. then $fin && s="${s%$'\n'}" ### removing a last newline. set -- "$@" "$s" ### store in an array. unset asd ### unset working variables. fi if [[ $a == '}' ]] && ((d>0)); then ((d--)); fi ### close braces. s="$s$a" done <<<"$str" printf 'Word=%q\n' "${@# }" ### print a quoted value removing front space. 

    Вывод:

     Word=\}\}\{0\,1\}\} Word=\{\,\}alph\}\'a 

    Или несколько более загадочным:

     str="{0,1 }}, {,}alph}'a, {(x,y,z)}, {{1,2,3}, {a,b,c}}" fin='false' d='0' until $fin do IFS= read -r -d '' -n 1 a || fin='true' [[ $a == '{' ]] && (( d++ )) ### count openning braces. [[ $a == ',' ]] && (( d<1 )) || $fin && { ### ',' no braces (or end). $fin && s="${s%$'\n'}" ### removing a last newline. set -- "$@" "$s" ### store in an array. unset asd ### unset working variables. } [[ $a == '}' ]] && (( d>0 )) && ((d--)) ### substract closing braces. s="$s$a" done <<<"$str" printf 'Word=%q\n' "${@# }" ### print a quoted value with front space removed. 

    Результат:

     Word=$'{0,1\n\n}}' Word=\{\,\}alph\}\'a Word=\{\(x\,y\,z\)\} Word=\{\{1\,2\,3\}\,\ \{a\,b\,c\}\} 
    Linux и Unix - лучшая ОС в мире.