Używanie Perla do zliczania liczb naukowych w pliku

10

Jak mogę policzyć liczbę liczb naukowych w pliku? Plik ma także kilka wierszy nagłówka, które należy pominąć.

Część zawartości pliku znajduje się poniżej.

FileHeaderLine1
FileHeaderLine2
FileHeaderLine3
FileHeaderLine4
2.91999996E-001 2.97030300E-001 3.02060604E-001 3.07090908E-001 3.12121212E-001 3.17151517E-001
3.22181821E-001 3.27212125E-001 3.32242429E-001 3.37272733E-001 3.42303038E-001 3.47333342E-001
3.52363646E-001 3.57393950E-001 3.62424254E-001 3.67454559E-001 3.72484863E-001 3.77515137E-001
3.82545441E-001 3.87575746E-001 3.92606050E-001 3.97636354E-001 4.02666658E-001 4.07696962E-001
4.12727267E-001 4.17757571E-001 4.22787875E-001 4.27818179E-001 4.32848483E-001 4.37878788E-001
4.42909092E-001 4.47939396E-001 4.52969700E-001

Jak mogę pominąć pierwsze cztery linie powyższego przykładu i policzyć liczbę liczb naukowych w pliku?

AFP
źródło

Odpowiedzi:

14

Za pomocą modułu podstawowego Scalar::Utilmożesz:

$ perl -MScalar::Util=looks_like_number -anle '
    $count += grep { looks_like_number($_) } @F;
    END { print $count }
' file
33

Więcej na temat looks_like_numbermożna zobaczyć w perldoc perlapi.

Cuonglm
źródło
+1 fajne, nie wiedziałem olooks_like_number
steeldriver,
7

Korzystanie z GNU grep

Możesz grepto zrobić za pomocą narzędzi PCRE. Nawiasem mówiąc, ten sam wzór można również zastosować w Perlu:

$ grep -oP '\d+E[-+]?\d+' file.txt  | wc -l
33

Możesz także użyć wc -wdo liczenia słów, liczę wiersze powyżej, ale grepzwraca pojedyncze dopasowanie w wierszu, więc tak naprawdę nie ma znaczenia w tym scenariuszu.

Korzystanie z Perla

W przypadku Perla możesz użyć tej jednej linijki:

$ perl -lane '$c += grep /\d+E[-+]?\d+/, @F; END { print $c; }' file.txt 
33

Bibliografia

slm
źródło
@StephaneChazelas - dzięki za edycję. Przepraszam, że kiedykolwiek korzystam tylko z systemów GNU, więc często zapominam o tym punkcie. Spróbuję nie popełnić tego błędu.
slm
4

egrep będzie działać:

egrep "[0-9].[0-9]E-[0-9]" YourFile | wc -w

AKTUALIZACJA:

jeśli wiersz zawierał zarówno liczbę, jak i inny ciąg, możemy użyć awktego rozwiązania:

awk -F' ' '{for(i=1;i<=NF;i++)if(!(i%1))$i=$i "\n"}1' YourFile | egrep "[0-9].[0-9]E-[0-9]" | wc -w ( or wc -l )
Nidal
źródło
Dałoby to niepoprawne wyniki, gdyby wiersz zawierał zarówno liczbę, jak i inny ciąg. Powyższa odpowiedź, która używa opcji -o grep do wyprowadzania tylko dopasowań, jest bardziej poprawna.
Johnny
Nie wiedziałem wcześniej o -oPopcji wymienionej w odpowiedzi awkSLM , ale naprawiłem swój problem używając @Johnny
Nidal
3

Zakładając, że masz tylko liczby naukowe po czwartej linii, możesz zrobić coś takiego jak poniżej.

tail -n +5 filename | wc - w

Dla podanego przez ciebie sygnału wyjściowego jest 33 po uruchomieniu powyższej komendy.

Ramesh
źródło
3

Jeśli potrzebujesz po prostu policzyć liczbę białych znaków oddzielonych po liniach nagłówka w perlu, myślę, że możesz to zrobić

perl -lane '$sum += $#F+1 if $. > 4; END{print $sum}' file

Jeśli naprawdę musisz liczyć tylko naukowo sformatowane liczby, wówczas jednym z podejść może być wyszukiwanie i zamienianie liczb zgodnie z odpowiednim wyrażeniem regularnym, a następnie policzenie liczby zamienników (wyrażenie podstawienia perla zwraca liczbę zamienników po powiązaniu go ze zmienną )

perl -lane '$sum += s/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?//g if $. > 4; END{print $sum}' file
steeldriver
źródło
2

Wszystko sprowadza się do tego, co faktycznie chcesz wziąć pod uwagę liczbę naukową , czego możesz oczekiwać, że Twój wkład będzie zawierał, i gdzie możesz zaakceptować znalezienie tych liczb na wejściu.

Na przykład w:

That's inferior to the LK2E2000 model.

Mogę znaleźć 0 lub 2 (inf i 2E2000) lub 3 (inf, 2E200, 0) liczb (lub przejść do skrajności, szukając wszystkich sekwencji znaków, które tworzą prawidłową liczbę: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Jeśli wiesz, że twoje dane wejściowe zawierają tylko liczby w X.XXXXXXXXE-XXX i że są one oparte na własnych słowach, może być bezpieczniej szukać tylko takich całych słów, takich jak:

tr -s '[[:blank:]]' '[\n*]' | LC_ALL=C grep -xEc '[0-9]\.[0-9]{8}E-[0-9]{3}'

Chodzi o to, aby uzyskać jedno słowo w wierszu i dopasować całą linię ( -x) do żądanego wzorca. Aby zezwolić na dowolny numer notacji naukowej (-1,2e + 1234 ... tak długo, jak istnieje znak elub E), możesz zmienić wzór na:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])[eE][-+]?[0-9]+

Lub uczyń tę e...część opcjonalną, aby zezwolić na wszelkiego rodzaju dziesiętne liczby zmiennoprzecinkowe:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])([eE][-+]?[0-9]+)?

To wszystko daje tę samą odpowiedź na twoje konkretne dane wejściowe, ale to by miało znaczenie, to gdzie dane wejściowe odbiegają od ścisłego wzorca pokazanego w twojej próbce.

Stéphane Chazelas
źródło