awk, sed lub inne sugestie przetwarzania tekstu, proszę

1

Mam następujący powtarzający się wzór tekstu, który należy ponownie sformatować.

Normalnie powinno to być łatwe, nawet przy użyciu standardowego edytora tekstu, ale w tym przypadku muszę rozwinąć informacje w nawiasie i wyliczyć je.

Najlepiej podam przykład:

"Gene Code (1A - 1F) D2 fragment, D74F"

Muszę być w stanie wyglądać tak:

Gene Code, 1A, D2 fragment, D74F
Gene Code, 1B, D2 fragment, D74F
Gene Code, 1C, D2 fragment, D74F
Gene Code, 1D, D2 fragment, D74F
Gene Code, 1E, D2 fragment, D74F
Gene Code, 1F, D2 fragment, D74F

Problem polega na tym, że początkowy łańcuch zawarty w nawiasie może mieć postać 1A-1F lub 3D-3H itp. Jest to jedyne przesunięcie bitów informacji. Numer w nawiasie jest zawsze taki sam, tylko litery alfabetu wymagają rozszerzenia wraz z przypisanym im numerem.

Potrzebna jest więc korelacja alfabetu z liczbami.

Dla mnie wygląda to na zginanie umysłu. Każda pomoc bardzo doceniana. Nawiasem mówiąc, nowy w tym.

jeffschips
źródło
Czy jest to wrażliwe na wydajność? Łatwe rozwiązanie z pętlą for byłoby nie bardzo szybkie.
Eugen Rieck

Odpowiedzi:

2

Ten skrypt basha

#!/bin/bash

PART1=$(echo "$1" | sed 's/\(.*\)\s(.*/\1/')
PART3=$(echo "$1" | sed 's/.*)\(.*\)/\1/')
PART2=$(echo "$1" | sed 's/.*(\s*\(.*\)).*/\1/')

START=$(echo "$PART2" | sed 's/\s*-.*//')
END=$(echo "$PART2" | sed 's/.*-\s*//')

STARTNUM=$(echo "$START" | sed 's/^\(.\).*/\1/')
ENDNUM=$(echo "$END" | sed 's/^\(.\).*/\1/')
if test "$STARTNUM" '!=' "$ENDNUM"; then
    echo "Error: Numeral is different"
    exit 1
fi

STARTLETTER=$(echo "$START" | sed 's/^.\(.\).*/\1/')
ENDLETTER=$(echo "$END" | sed 's/^.\(.\).*/\1/')

OUTPUT=''
for LETTER in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ; do
    test "$LETTER" '==' "$STARTLETTER" && OUTPUT='yes'
    test -n "$OUTPUT" && echo "$PART1, $STARTNUM$LETTER,$PART3"
    test "$LETTER" '==' "$ENDLETTER" && OUTPUT=''
done

Zrobi to, czego potrzebujesz, choć nie w bardzo wydajny sposób, gdy zostanie wywołany z oryginalnym tekstem jako $1

EDYTOWAĆ

Na prośbę kilka słów o sed wyrażenia:

  • Izoluję PART1 biorąc wszystko przed spacją i otwarcie (
  • Izoluję PART3 biorąc wszystko od zamknięcia ) dalej
  • Izoluję PART2 biorąc to, co jest pomiędzy ( i ), ignorując białe znaki
  • START i END są izolowane myślnikiem, ponownie ignorując białe znaki
  • Numer i litera są izolowane przez bycie pierwszym i drugim znakiem
Eugen Rieck
źródło
Podział wyrażeń sed byłby fantastyczny, wygląda jak niektóre podwyrażenia i a \s to ...
Xen2050
@ Xen2050 The \s jest tylko dla solidności: Ignoruj ​​lub poprawnie przetwarzaj białe znaki wokół odpowiednich części. Wszystko inne powinno być dość samo-wyjaśniające.
Eugen Rieck
1
Nie liczyłbym na to, że jest to wyjaśnienie dla kogoś, kto szuka „awk, sed, lub w zasadzie wszystkiego”, każda wskazówka pomaga +1
Xen2050
1

Jeśli dostępny jest GNU sed

sed -r 's/([^(]+) \((.)(.) - .(.)\)(.*)/printf \x27\1, \2%s,\5\\n\x27 {\3..\4}/e' <<<'Gene Code (1A - 1F) D2 fragment, D74F'
Gene Code, 1A, D2 fragment, D74F
Gene Code, 1B, D2 fragment, D74F
Gene Code, 1C, D2 fragment, D74F
Gene Code, 1D, D2 fragment, D74F
Gene Code, 1E, D2 fragment, D74F
Gene Code, 1F, D2 fragment, D74F

Jeśli nie, uruchom go wysyłając jako potok do powłoki

sed -r 's/([^(]+) \((.)(.) - .(.)\)(.*)/printf \x27\1, \2%s,\5\\n\x27 {\3..\4}/' <<<'Gene Code (1A - 1F) D2 fragment, D74F'|bash
Gene Code, 1A, D2 fragment, D74F
Gene Code, 1B, D2 fragment, D74F
Gene Code, 1C, D2 fragment, D74F
Gene Code, 1D, D2 fragment, D74F
Gene Code, 1E, D2 fragment, D74F
Gene Code, 1F, D2 fragment, D74F

(z sh i ksh wyjście jest takie samo)

Paulo
źródło
1

Perlowy sposób:

#!/usr/bin/perl
use feature 'say';

my $str = '"Gene Code (3D - 3H) D2 fragment, D74F"';
# get begin number, begin letter, end number, end letter
my ($bn,$bl,$en,$el) = $str =~ /\((.)(.) - (.)(.)\)/;
# loop from begin letter to end letter
for my $i ($bl .. $el) {
    # do the substitution and print
    ($_ = $str) =~ s/ \(.. - ..\)/, $bn$i,/ && say;
}

Wydajność:

"Gene Code, 3D, D2 fragment, D74F"
"Gene Code, 3E, D2 fragment, D74F"
"Gene Code, 3F, D2 fragment, D74F"
"Gene Code, 3G, D2 fragment, D74F"
"Gene Code, 3H, D2 fragment, D74F"
Toto
źródło
Dziękujemy wszystkim za dostarczenie tych wspaniałych rozwiązań. Jestem naprawdę zachwycony wspaniałomyślnością i profesjonalizmem. To działa! Nie wiedziałem, że sed jest tak potężny. Teraz muszę dowiedzieć się, jak przekazać wpisy, które nie pasują do tego konkretnego wzoru. Dziękujemy wszystkim i życzymy udanego Nowego Roku !!
jeffschips
@jeffschips: Nie ma za co. Proszę oznaczyć jedną z odpowiedzi jako zaakceptowaną, zobacz: superuser.com/help/someone-answers
Toto
0

Wersja, która nie wymaga pętli, i używa tylko czterech wywołań sed. Oczywiście moja wersja nie sprawdza, czy obie liczby są równe. W rzeczywistości drugi jest ignorowany i można go nawet pominąć, tak jak w przypadku "Gene Code (91K - Q) D2 fragment, D74F". Również dolna granica i górna granica mogą pojawić się w dowolnej kolejności. Jeśli dolna granica jest większa niż górna granica, to sekwencja wyjściowa jest odwrócona.

$ cat foo
#!/usr/bin/env bash

# Script to expand $1 passed as:

# "Gene Code (91K - 91Q) D2 fragment, D74F"
# 
# into the output:
# 
# Gene Code, 91K, D2 fragment, D74F
# Gene Code, 91L, D2 fragment, D74F
# Gene Code, 91M, D2 fragment, D74F
# Gene Code, 91N, D2 fragment, D74F
# Gene Code, 91O, D2 fragment, D74F
# Gene Code, 91P, D2 fragment, D74F
# Gene Code, 91Q, D2 fragment, D74F


# Copy $1 into FMT_STRING, replacing the " (91K - 91Q)" bit with a ', %s,' 
# printf directive, such as 'Gene Code, %s, D2 fragment, D74F':

FMT_STRING="$(sed -e 's/ (.* - .*)/, %s,/' <<< "$1")"

# Parse the beginning and ending bounds and format them with just a 
# space between, such as '91K 91Q':

BOUNDS="$(sed -e 's/^[^(]*(\(.*\) - \(.*\)) .*/\1 \2/' <<< "$1")"

# Extract the (first) static numeric part from BOUNDS, e.g. '91'

NUMERIC="$(sed -e 's/[^0-9].*//' <<< "$BOUNDS")"

# remove all digits [0-9] from BOUNDS, e.g. 'K Q'
BOUNDS="$(sed -e 's/[0-9]//g' <<< "$BOUNDS")"

FMT_STRING="$(printf "$FMT_STRING" "${NUMERIC}%c")"

jot -w "$FMT_STRING" - $BOUNDS

Przykładowe wyjście:

$ ./foo "Gene Code (737L - 737X) D2 fragment, D74F"
Gene Code, 737L, D2 fragment, D74F
Gene Code, 737M, D2 fragment, D74F
Gene Code, 737N, D2 fragment, D74F
Gene Code, 737O, D2 fragment, D74F
Gene Code, 737P, D2 fragment, D74F
Gene Code, 737Q, D2 fragment, D74F
Gene Code, 737R, D2 fragment, D74F
Gene Code, 737S, D2 fragment, D74F
Gene Code, 737T, D2 fragment, D74F
Gene Code, 737U, D2 fragment, D74F
Gene Code, 737V, D2 fragment, D74F
Gene Code, 737W, D2 fragment, D74F
Gene Code, 737X, D2 fragment, D74F

Odwrócenie granic powoduje odwrócenie wyjścia:

$ ./foo "Gene Code (737X - 737L) D2 fragment, D74F"
Gene Code, 737X, D2 fragment, D74F
Gene Code, 737W, D2 fragment, D74F
Gene Code, 737V, D2 fragment, D74F
Gene Code, 737U, D2 fragment, D74F
Gene Code, 737T, D2 fragment, D74F
Gene Code, 737S, D2 fragment, D74F
Gene Code, 737R, D2 fragment, D74F
Gene Code, 737Q, D2 fragment, D74F
Gene Code, 737P, D2 fragment, D74F
Gene Code, 737O, D2 fragment, D74F
Gene Code, 737N, D2 fragment, D74F
Gene Code, 737M, D2 fragment, D74F
Gene Code, 737L, D2 fragment, D74F
Jim L.
źródło