Yalnızca tam sayıda sınırlayıcı içeren satırları sakla


9

Virgülle ayrılmış 10 alanlı büyük bir csv dosyam var. Ne yazık ki, bazı satırlar hatalı biçimlendirilmiş ve tam olarak 10 virgül içermiyor (dosyayı R'ye okumak istediğimde bazı sorunlara neden oluyor). Yalnızca tam olarak 10 virgül içeren satırları nasıl filtreleyebilirim?


1
sorunuz ve bağlantılı soru aynı soru değildir . belirli sayıda eşleşmeden daha fazla veya daha az olmayan çizgileri nasıl ele alacağınızı sorarsınız, oysa bu soru yalnızca minimum bir eşleşme sayısı gerektirir. gerçek şu ki, soru daha kolay cevaplanabilir - bir satırın tam olarak taranmasını veya (en azından sedburada olduğu gibi ) sadece arandığından daha fazla eşleşmeyi gerektirmez, ancak bu soru. Bunu kapatmamalıydın.
mikeserv

1
Aslında, yakın görünümlü, asker orada gelmez istiyorum daha fazla veya daha az maçları. bu sorunun yeni bir başlığa ihtiyacı var. ama grepcevap her iki soru için de kabul edilebilir bir cevap yok ...
mikeserv

Yanıtlar:


21

Başka bir POSIX:

awk -F , 'NF == 11' <file

Satırda 10 virgül varsa, bu satırda 11 alan olacaktır. Bu yüzden sadece yapmak awkkullanımını ,alan ayırıcı olarak. Alan sayısı 11 ise, koşul NF == 11doğrudur, awkardından varsayılan eylemi gerçekleştirir print $0.


5
Aslında bu soru üzerine aklıma gelen ilk şey bu. Ben aşırı olduğunu düşündüm, ama koda bakarak ... emin daha açıktır. Diğerlerinin yararına: -Falan ayırıcısını ayarlar ve NFbelirli bir satırdaki alan sayısını ifade eder. Koşula hiçbir kod bloğu {statement}eklenmediğinden NF == 11, varsayılan eylem satırı yazdırmaktır. (@cuonglm, isterseniz bu açıklamayı dahil etmekten çekinmeyin.)
Wildcard

4
+1: Çok şık ve okunabilir bir çözüm, aynı zamanda çok genel. Örn. İle tüm hatalı biçimlendirilmiş hatları bulabilirimawk -F , 'NF != 11' <file
Miroslav Sabo

@gardenhead: OP'nin yorumunda söylediğini gördüğünüz gibi almak kolaydır. Cep telefonumdan bazen cevap veriyorum, bu yüzden detay açıklamalarını eklemek zor.
cuonglm

1
@mikeserv: Hayır, seni şaşırtırsam özür dilerim, bu sadece benim kötü İngilizcem. 1-9 virgül içeren 11 alanınız olamaz.
cuonglm

1
@OlivierDulac: Dosya başlangıcında -veya adlandırılmış dosyalara karşı sizi korur -.
cuonglm

8

Kullanarak egrep(veya grep -EPOSIX'te):

egrep "^([^,]*,){10}[^,]*$" file.csv

Bu, 10 virgül içermeyen her şeyi filtreler: tam satırlarla ( ^başlangıçta ve $sonda) eşleşir ve {10}"," dışında herhangi bir sayıda karakter ve ardından tek bir "," "dizisinin tam olarak on tekrarını ( ) içerir ( ([^,]*,)), ardından tekrar ',' ( [^,]*) dışında herhangi bir sayıda karakter gelir .

-xParametreyi bağlantı noktalarını bırakmak için de kullanabilirsiniz :

grep -xE "([^,]*,){10}[^,]*" file.csv

Bu daha az verimlidir cuonglm 'in awksolüsyonu da; ikincisi yaklaşık 10 virgül içeren satırlar için sistemimde genellikle altı kat daha hızlıdır. Daha uzun çizgiler büyük yavaşlamalara neden olur.


5

Çalışacak en basit grepkod:

grep -xE '([^,]*,){10}[^,]*'

Açıklama:

-xkalıbın bir parçası olmaktan ziyade tüm çizgiyle eşleşmesi gerekir . Bu önemlidir, bu nedenle 10'dan fazla virgül içeren satırlarla eşleşmezsiniz.

-E "genişletilmiş normal ifade" anlamına gelir, bu da normal ifadenizde daha az ters eğik çizgi oluşmasını sağlar.

Parantezler gruplama için kullanılır ve daha {10}sonra parantezler içinde desenin bir satırında tam olarak on eşleşme olması gerektiği anlamına gelir.

[^,]bir karakter sınıfıdır; örneğin, [c-f]a c, a d, a eveya an folan [^A-Z]herhangi bir tek karakterle eşleşir ve büyük harf OLMAYAN tek bir karakterle eşleşir. Yani [^,]virgül hariç herhangi bir karakterle eşleşir.

*Karakter sınıfı araçlarının sonra "sıfır veya bunların daha fazlası."

Yani normal ifade kısmı ([^,]*,)"virgül hariç herhangi bir karakter (sıfır kez dahil) ve ardından virgül gelir" anlamına gelir ve bunların {10}10'unu belirtir. Ardından [^,]*, virgül olmayan karakterlerin geri kalanını satırın sonuna eşleştirmek için.


5
sed -ne's/,//11;t' -e's/,/&/10p' <in >out

Bu, ilk önce 11 veya daha fazla virgül içeren herhangi bir satırı dallar ve daha sonra yalnızca 10 virgülle eşleşenlerin satırlarını yazdırır.

Görünüşe göre bunu daha önce cevapladım ... İşte bir modelin tam 4 örneğini arayan bir sorudan intihal :

Sadece komuta ekleyerek [num]sed s///ubstitution komutuyla bir desenin oluşumunu hedefleyebilirsiniz [num]. Ne zaman seni test başarılı oyuncu değişikliği ve bir hedef belirtmeyen :etiketi, tsenaryo dışına est dalları. Bu, tek yapmanız gereken s///5virgül veya daha fazla virgül testi yapmak ve kalanları yazdırmaktır.

Ya da en azından, maksimum 4'ü aşan çizgileri işler. Görünüşe göre minimum bir gereksiniminiz de var. Neyse ki, bu kadar basit:

sed -ne 's|,||5;t' -e 's||,|4p'

... ,bir satırın 4. oluşumunu kendisiyle pdeğiştirin ve s///ubstitution bayraklarına olan ipinizi yapıştırın. ,5 veya daha fazla kez eşleşen tüm satırlar budanmış olduğundan, 4 ,eşleşme içeren satırlar yalnızca 4 içerir .


1
@cuonglm - ilk başta bu vardı, ama insanlar her zaman bana daha okunabilir kod yazmam gerektiğini söylüyorlar. i diğerleri okunamaz olarak im tutmak ve ne damla emin değilim gibi şeyler okuyabilir beri ...? ikinci virgül koyuyorum.
mikeserv

@cuonglm - beni alay edebilirsin - duygularımı incitmez. şaka yapabilirim. eğer alay ediyorsan biraz komikti. onun ok - ben sadece emin değildi ve bilmek istedim. bence insanlar kendilerine gülebilmeli. Neyse, hala anlamıyorum!
mikeserv

Haha, doğru, bu çok olumlu bir düşünce. Her neyse, seninle sohbet etmek çok komik ve bazen beynimi strese sokuyorsun.
cuonglm

Bu o ilginç Bu yanıt ben değiştirirseniz, s/hello/world/2ile s//world/2, GNU çalışma cezası sed. İkisiyle sedyadigarı gelen /usr/5bin/posix/sedzam segfault, /usr/5bin/sedmastar döngüye girer.
cuonglm

@mikeserv, ve (yorumlarda) hakkında daha önceki tartışmamızasedawk istinaden —Bu yanıtı beğendim ve onayladım, ancak kabul edilen awkcevabın çevirisine dikkat edin : "11 alanlı satırları yazdır" ve bu sedcevabın çevirisi : " 11. virgül kaldırılmaya çalışılır; başarısız olursa sonraki satıra atlayın. 10. virgülün yerini almayı deneyin; başarılı olursa satırı yazdırın. " awkCevap bilgisayara talimatları İngilizce olarak onları ifade tıpkı bir yol sunar. ( awkalan tabanlı veriler için iyidir.)
Wildcard

4

Biraz kısa atmak python:

#!/usr/bin/env python2
with open('file.csv') as f:
    print '\n'.join(line for line in f if line.count(',') == 10)

Bu, her satırı okuyacak ve satırdaki virgül sayısının 10'a eşit olup olmadığını kontrol edecektir line.count(',') == 10, eğer öyleyse yazdırın.


2

Ve işte bir Perl yolu:

perl -F, -ane 'print if $#F==10'

-nNeden perltarafından verilen komut hattı ile onun giriş dosyası satır okumak ve yürütmek -eher satırda. -aOtomatik bölme üzerinde döner: her bir giriş hattı tarafından verilen değeri bölünecektir -F(burada, bir virgül) ve dizi olarak kaydedilir @F.

$#F(Veya daha genel $#array), dizinin en yüksek endeksidir @F. Diziler başlayacak yana 0, 11 alanlarla bir çizgi bir olacaktır @Farasında 10. Bu nedenle komut dosyası, tam olarak 11 alanı varsa satırı yazdırır.


Ayrıca print if @F==11bir skaler bağlamda dizi olarak öğe sayısını döndürme yapabilirsiniz.
Sobrique

1

Alanlarda virgül veya yeni satır varsa, kodunuzun csv'yi anlaması gerekir . Örnek (üç sütunlu):

$ cat filter.csv
a,b,c
d,"e,f",g
1,2,3,4
one,two,"three
...continued"

$ cat filter.csv | python3 -c 'import sys, csv
> csv.writer(sys.stdout).writerows(
> row for row in csv.reader(sys.stdin) if len(row) == 3)
> '
a,b,c
d,"e,f",g
one,two,"three
...continued"

Sanırım şimdiye kadarki çoğu çözüm ikinci ve dördüncü sırayı atacaktı.

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.