İki belirli karakter veya karakter dizisi arasında metin bulma


17

Diyelim ki böyle çizgiler var:

*[234]*
*[23]*
*[1453]*

burada *herhangi bir dizeyi temsil eder (formun bir dizesi hariç [number]). Bu satırları bir komut satırı yardımcı programıyla ayrıştırabilir ve parantezler arasındaki sayıyı nasıl çıkarabilirim?

Daha genel olarak bu araçların hangi cut, sed, grepveya awkböyle bir görev için uygun olur?

Yanıtlar:


16

GNU grep'iniz varsa, -onormal ifadeyi aramak ve yalnızca eşleşen kısmı çıkarmak için bu seçeneği kullanabilirsiniz . (Diğer grep uygulamaları yalnızca tüm satırı gösterebilir.) Bir satırda birden fazla eşleşme varsa, bunlar ayrı satırlara yazdırılır.

grep -o '\[[0-9]*\]'

Köşeli parantezleri değil, yalnızca rakamları istiyorsanız, biraz daha zor; sıfır genişlikli bir iddiayı kullanmanız gerekir: boş dizeyle eşleşen bir regexp, ancak yalnızca önce gelirse veya duruma göre izlenirse bir parantez. Sıfır genişlikli iddialar yalnızca Perl sözdiziminde kullanılabilir.

grep -P -o '(?<=\[)[0-9]*(?=\])'

Sed ile yazdırmayı kapatmanız -nve tüm çizgiyi eşleştirmeniz ve yalnızca eşleşen parçayı tutmanız gerekir. Bir satırda birkaç olası eşleşme varsa, yalnızca son eşleşme yazdırılır. Bkz çevreleyen karakterleri yazdırmadan 'sed' ile eşleşen bir düzenli ifade ayıklanıyor burada sed kullanarak ilgili daha fazla ayrıntı için.

sed -n 's/^.*\(\[[0-9]*\]\).*/\1/p'

veya parantezleri değil, yalnızca rakamları istiyorsanız:

sed -n 's/^.*\[\([0-9]*\)\].*/\1/p'

Olmadan grep -obasit ve anlaşılır hem şey istiyorsanız, Perl burada tercih edilen bir araçtır. Her satırda ( -n), satırda bir eşleşme varsa, \[[0-9]*\]o eşleşmeyi ( $&) ve yeni satırı ( -l) yazdırın .

perl -l -ne '/\[[0-9]*\]/ and print $&'

Yalnızca rakamları istiyorsanız, bir grubu sınırlamak için normal ifadeye parantez koyun ve yalnızca o grubu yazdırın.

perl -l -ne '/\[([0-9]*)\]/ and print $1'

PS yalnızca bir veya daha fazla parantez basamak, değişiklik yapılmasını gerektirebilir istiyorsanız [0-9]*için [0-9][0-9]*, ya da [0-9]+Perl.


Hepsi iyi, bunun dışında " parantezler arasındaki sayıyı çıkarmak" istiyor . Bence "hariç" dışında [number]anlamına gelir[0-9]
Peter.O

1
@ Peter.OI “sayı dışında” bu formun çizgisinin başka bir parçası olmadığı anlamına geliyordu. Ama cevabımı, her halükarda sadece rakamların nasıl yazdırılacağını göstermek için düzenledim.
Gilles 'SO- kötü olmayı kes'

1
Bu perlnormal ifade iddiaları gerçekten yararlı görünüyor! Hem geri hem de ileri iddiaları kullandığınızı gördükten sonra grep'te bile okuduktan sonra okudum (bir regex motoru seçebileceğiniz gerçeğine kapıldım). Buradan perl'in normal ifadesine biraz daha zaman ayıracağım. Teşekkürler ... PS .. Az önce okudum man grep... "Bu son derece deneysel ve grep -P uygulanmamış özellikleri uyarabilir." ... Umarım bu kararsız anlamına gelmez (?) ...
Peter.O

5

İle yapamazsın cut.

  1. tr -c -d '0123456789\012'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

tr sorun için en doğal uyum ve muhtemelen en hızlı çalışacaktı, ancak bence bu seçeneklerden herhangi birini hız açısından ayırmak için devasa girişlere ihtiyacınız olacak.


Sed için, ^.*açgözlüdür ve son rakam hariç hepsini tüketir ve posix +olması \+ya da kullanılması gerekir \([0-9][0-9]*\).... ve her durumda 's/[^0-9]*//g'da aynı şekilde çalışır, ... Thanks for the tr -c` örneği, ancak bu son derece \012gereksiz değil mi?
Peter.O

@Peter Bunu yakaladığınız için teşekkürler. Sed örneğini test ettiğime yemin ederdim. :( Senin sürümüne değiştirdim. İlgili \012: aksi takdirde gerekli trsatırsonu yiyecektir.
Kyle Jones

Aha ... Ben olarak görüyordum \0, 1, 2(hatta \, 0, 1, 2). Öyle görünüyor ki sekizli kadar uyumsuzum .. Teşekkürler.
Peter.O

4

Rakam olmayan karakterler arasında art arda bir dizi rakam çıkarmayı kastediyorsanız, sanırım sedve awken iyisidir ( grepaynı zamanda size eşleşen karakterleri de verebilir):

sed: Tabii ki rakamlarla eşleşebilirsiniz, ancak tam tersini yapmak ilginçtir, rakam olmayanları kaldırın (satır başına sadece bir sayı olduğu sürece çalışır):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep: art arda rakamlarla eşleşebilirsiniz

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

Bir örnek vermiyorum awkçünkü onunla null deneyimim var; Her ne kadar sedbir İsviçre bıçağı olsa da , grepbunu yapmak için size daha basit, daha okunabilir bir yol verdiğini ve bu da her bir giriş satırında birden fazla sayı için çalıştığını belirtmek ilginçtir ( -osadece girişin eşleşen kısımlarını yazdırır, her biri kendi satırında):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54

Bir karşılaştırma olarak, "satır başına birden fazla sayı" örneğinin bir sedeşgörünümü . . . ... (+1)grep -o '[[:digit:]]*'sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'
Peter.O

2

Bunun yapılamayacağı söylendiği için , "en iyi" olarak cutkullanımını desteklemese de, en azından diğerlerinden daha kötü olmayan bir çözüm üretmenin kolayca mümkün olduğunu göstereceğim. cut(hatta özellikle iyi) bir çözüm. Özellikle rakamlara *[ve ]*etrafına bakmayan herhangi bir çözümün basitleştirici varsayımlar yaptığı ve bu nedenle askerler tarafından verilenlerden daha karmaşık örneklerde başarısızlığa eğilimli olduğu söylenmelidir (örneğin , dış *[ve rakamlar ]*gösterilmemelidir). Bu çözüm en azından köşeli parantezleri kontrol eder ve yıldız işaretlerini de kontrol etmek için genişletilebilir (okuyucuya bir egzersiz olarak bırakılır):

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

Bu, -dbir sınırlayıcı belirten seçeneği kullanır . Açıkçası cutbir dosyadan okumak yerine ifadeye yönelebilirsiniz. cutMuhtemelen oldukça hızlı olsa da, basit olduğu için (regex motoru yok), en azından iki kez (veya kontrol etmek için birkaç kez daha) çağırmanız gerekir *, bu da işlem yükü oluşturur. Bu çözümün gerçek bir avantajı, özellikle normal ifadelere sahip yapılarda iyi bilgili olmayan sıradan kullanıcılar için oldukça okunabilir olmasıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.