Mam plik, który wygląda mniej więcej tak:
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
I trzeba wyodrębnić coś wewnątrz cudzysłowów, które następują name=
, to znaczy content_analyzer
, content_analyzer2
i content_analyzer_items
.
Robię to na Linuksie, więc rozwiązanie wykorzystujące sed, perl, grep lub bash jest w porządku.
regex
perl
sed
html-parsing
text-extraction
kowboj
źródło
źródło
Odpowiedzi:
Ponieważ musisz dopasować zawartość bez uwzględniania jej w wyniku (musi pasować,
name="
ale nie jest częścią pożądanego wyniku), wymagana jest pewna forma dopasowania o zerowej szerokości lub przechwytywania grupowego. Można to łatwo zrobić za pomocą następujących narzędzi:Perl
W Perlu możesz użyć
n
opcji zapętlenia wiersz po wierszu i wydrukowania zawartości grupy przechwytywania, jeśli pasuje:perl -ne 'print "$1\n" if /name="(.*?)"/' filename
GNU grep
Jeśli masz ulepszoną wersję grep, taką jak GNU grep, możesz mieć
-P
dostępną opcję. Ta opcja włączy regex w stylu Perla, umożliwiając użycie\K
skrótu lookbehind. Zresetuje pozycję dopasowania, więc wszystko, co jest przed nią, ma zerową szerokość.grep -Po 'name="\K.*?(?=")' filename
Ta
o
opcja powoduje, że grep drukuje tylko dopasowany tekst zamiast całej linii.Vim - edytor tekstu
Innym sposobem jest bezpośrednie użycie edytora tekstu. W Vimie jednym z różnych sposobów na osiągnięcie tego byłoby usunięcie linii bez,
name=
a następnie wyodrębnienie zawartości z linii wynikowych::v/.*name="\v([^"]+).*/d|%s//\1
Standardowy grep
Jeśli z jakiegoś powodu nie masz dostępu do tych narzędzi, coś podobnego można osiągnąć za pomocą standardowego grep. Jednak bez rozglądania się dookoła będzie wymagać późniejszego uporządkowania:
grep -o 'name="[^"]*"' filename
Uwaga dotycząca zapisywania wyników
We wszystkich powyższych poleceniach wyniki zostaną przesłane do
stdout
. Należy pamiętać, że zawsze można je zapisać, przesyłając je do pliku, dołączając:do końca polecenia.
źródło
grep
):grep -Po '.*name="\K.*?(?=".*)'
.*
odłożyłem obie na bok, mam nadzieję, że się na mnie nie zdenerwujesz. Chciałbym zapytać, czy dostrzegasz jakieś korzyści wynikające z niechciwej walki zamiast „czegokolwiek oprócz"
”? Nie traktuj tego jako walki, jestem po prostu ciekawy i nie jestem ekspertem od regexów. Również\K
wskazówka, naprawdę fajna. Dzięki, Dennis..*
, możesz to zrobićgrep -Po '(?<=name=").*?(?=")'
.\K
Mogą być wykorzystywane do stenografii, ale to naprawdę potrzebne tylko wtedy, gdy mecz się jej lewej stronie jest zmienna długość. W takich przypadkach powód używania obejść jest dość oczywisty. Niegrzeczne operacje wyglądają trochę schludniej (w[^"]*
przeciwieństwie do.*?
i nie musisz powtarzać znaku kotwicy. Nie wiem o szybkości. Myślę, że to zależy w dużej mierze od kontekstu. Mam nadzieję, że to pomoże.)\K
(po zbadaniu tego) i usunąłem,.*
był taki sam: nadaj mu ładny wygląd (prostszy). I nigdy nie myślałem o użyciu.*?
zamiast „tradycyjnego sposobu”, którego się gdzieś nauczyłem. Ale niechciwość tutaj naprawdę ma sens. Dzięki Dennis, najlepsze życzenia.Wyrażenie regularne wyglądałoby tak:
.+name="([^"]+)"
Wtedy grupowanie byłoby w \ 1
źródło
Jeśli używasz Perla, pobierz moduł do analizy XML: XML :: Simple , XML :: Twig lub XML :: LibXML . Nie wymyślaj ponownie koła.
źródło
<type="global"
na przykład), więc większość parserów XML po prostu narzeka i umiera.W tym celu należy użyć parsera HTML zamiast wyrażeń regularnych. Program w Perlu, który wykorzystuje
HTML::TreeBuilder
:Program
#!/usr/bin/env perl use strict; use warnings; use HTML::TreeBuilder; my $tree = HTML::TreeBuilder->new_from_file( \*DATA ); my @elements = $tree->look_down( sub { defined $_[0]->attr('name') } ); for (@elements) { print $_->attr('name'), "\n"; } __DATA__ <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>
Wynik
źródło
może to zrobić:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
źródło
Oto rozwiązanie wykorzystujące HTML tidy i xmlstarlet:
htmlstr=' <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table> ' echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | sed '/type="global"/d' | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
źródło
Ups, polecenie sed musi oczywiście poprzedzać polecenie uporządkowane:
echo "$htmlstr" | sed '/type="global"/d' | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
źródło
Jeśli struktura twojego xml (lub ogólnie tekstu) jest ustalona, najłatwiej jest użyć
cut
. W Twoim przypadku:echo '<table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>' | grep name= | cut -f2 -d '"'
źródło