CSVを分割したいのですが、中かっこグループ内のカンマ一致を無視して各リストメンバーを繰り返します。以下のコードはうまく機能しますが、中かっこグループ内のコンマを考慮しません。
仮定:
- ここにありますいつも一致する支柱のペア。つまり、
{{ {a,b,c}, x
willのような入力いいえ発生する。
予想出力:
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
答え1
純粋な試み強く打つ
#!/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
答え2
以下は、サンプルを分割する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
警告:質問の説明に従って、2つのレベルの中かっこを処理します。
テスト:
$ ./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で実行し、次の答えを使用しています。Mac OSXのsedと他の「標準」sedの違いは何ですか?MacOSに移植してください。これがうまくいかない場合この回答brew install gnu-sed
sedを使用してGNUをインストールした後に呼び出す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}}'
答え3
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}}'
...先行スペース処理も非常に簡単です...
答え4
別のbashソリューション:
- 一致しない中括弧のペアを処理します
{
。 - 1つ以上の開かれた括弧が表示されるまで、閉じた中括弧は許可されません。
- 行末で中括弧の数をゼロにリセットします。
- 開かれた中かっこよりも閉じる中かっこが多い場合、カンマは有効なカンマとして許可されます。
- ソリューションの前のスペースは削除されます。
- 結果の単語が引用されます。
パスワード:
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 a s d ### 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 a s d ### 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\}\}