2016-05-17 4 views
2

Я хочу разбить строку на , в качестве разделителя. Моя проблема в том, что в некоторых случаях вход может содержать запятые. Изменение разделителя не является вариантом. Я хочу, чтобы пользователи могли избежать запятая с \, и поэтому я хочу разделить только на ,но не на \, так:Разделить строку только с неэкранированным разделителем

str="1,10,100,1\,000,10\,000,100\,000" 
while [[ ${#str} -gt 0 ]]; do 
    #Get index of delimiter 
    index=$(echo "$str" | grep -boP '(?<!\\),' | head -c 1) 

    #If index is empty, there is nothing to do 
    if [[ -z "$index" ]]; then 
     echo "$str" 
     break 
    fi 

    #Get the next string we're looking for 
    echo "$str" | cut -c1-$index 
    #Cut the original string 
    str=$(echo "$str" | cut -c$(($index+2))-${#str}) 
done 

Это в настоящее время печати:

1 
10 
100 
1\,000 
10\,000 
100\,000 

Но Я хочу, чтобы напечатать:

1 
10 
100 
1,000 
10,000 
100,000 

теперь я могу использовать sed заменить \, с ,, но все это решение кажется довольно громоздким для относительно простой проблемы. Есть лучший способ сделать это?

+0

Если у вас есть возможность манипулировать вашими данными, прежде чем пытаться разбить его, почему бы просто не использовать символ, который, как вы знаете, не будет там. # например. Затем вы можете просто разделить запятой и заменить все # с, потом? –

+0

@Remuze да, это тоже вариант, но при этом я буду добавлять ограниченный символ, который должен быть ','. Поэтому, если кто-то хочет использовать '#' один день, я буду сидеть с аналогичной проблемой. – Ian2thedv

+0

При необходимости вам нужно будет избежать обратных косых черт (если один из ваших объектов заканчивается на '\', вы хотите записать его как '\\'). Это может стать совершенно неуправляемым в чистой оболочке ... –

ответ

3

Попробуйте это:

$ str="1,10,100,1\,000,10\,000,100\,000" 
$ sed 's/\([^\]\),/\1\n/g' <<< $str 
1 
10 
100 
1\,000 
10\,000 
100\,000 

bash С однострочником:

$ sed 's/\([^\]\),/\1\n/g' <<< $str | while read -r line; do echo "-> $line"; done 
-> 1 
-> 10 
-> 100 
-> 1\,000 
-> 10\,000 
-> 100\,000 

В соответствии с комментария по @fedorqui, этим путем можно избежать открытий подоболочки.

while IFS= read -r line; do echo "-> $line"; done < <(sed 's/\([^\]\),/\1\n/g' <<< "$str") 
+0

@fedorqui Потеря персонажа - это то, что я хотел в этом случае, благодаря сильному, отличному решению! – Ian2thedv

+0

@fedorqui, Обновленный ответ. Благодарю. – sat

+0

@ lan2thedv, я терял один символ с вашего первоначального значения. Теперь это исправлено. – sat

1

Это способ:

str="1,10,100,1\,000,10\,000,100\,000" 
echo "$str" |sed -n 's/\([0-9]\+\(\\,[0-9]*\)*\),\+/\1\n/gp' 
1 
10 
100 
1\,000 
10\,000 
100\,000 

С tr вы можете просто удалить эти обратные косые черты:

str="1,10,100,1\,000,10\,000,100\,000" 
echo "$str" |sed -n 's/\([0-9]\+\(\\,[0-9]*\)*\),\+/\1\n/gp' |tr -d '\\' 
1 
10 
100 
1,000 
10,000 
100,000 
0

Использование гну awk вы можете использовать FPAT использовать сложные регулярные выражения для разбора каждого поля отдельно:

str="1,10,100,1\,000,10\,000,100\,000" 

awk -v FPAT='[^,\\\\]*(\\\\.[^,\\\\]*)*|[^,]*' '{ 
    for (i=1; i<=NF; i++) printf "%d: <%s>\n", i, $i}' <<< "$str" 

1: <1> 
2: <10> 
3: <100> 
4: <1\,000> 
5: <10\,000> 
6: <100\,000> 
+0

@ Downvoter: Могу ли я узнать, что случилось? – anubhava

+1

Спасибо за ответ! – Ian2thedv

Смежные вопросы