LINUX.ORG.RU

Есть строкодробилка на замену Перлу?

 , , ,


1

9

Добрый день, ЛОР.

Шёл 2018 год. И некоторые (в т.ч. на ЛОРе) считают, что Перл мёртв.

А что вы можете посоветовать более современное и удобное для задач обработки строк и текстовых файлов? Стоит ли смотреть в сторону Питона, например? Мне показалось, что он более многословен и менее заточен под такие задачи, но могу ошибаться. Сравнением быстродействия применительно к строкообработке кто-нибудь занимался?

Upd: может быть, есть альтернативы, сравнимые по лаконичности, но не настолько write-only, как Перл? Ибо в комментариях правильно подметили особенность

перла, на котором легко и непринужденно можно писать такое, что потом месяц будешь вспоминать и пытаться понять что это и как оно работает.

★★★★★

Последнее исправление: hobbit (всего исправлений: 5)
Ответ на: комментарий от ashot

Хз, может txt такой попался. Вот на awk уже минут 20 у меня считает)... Интересно сколько перл или питон провозится.

#!/usr/local/bin/gawk

BEGIN {
    FS=" ";
}
{
    i = 0;
    Words = 0;
    while (i < FNR) {
        Words += NF;
        i++;
    }

}
END {
    printf "Всего слов: " Words "\n" ;
}

Deleted
()
Ответ на: комментарий от Deleted

Вот Война и Мир, скопированная раз 600 в txt файл.

У тебя файл в виндовой кодировке, да ещё с досовскими концами строк. Локаль какая? Соответствует файлу? Сдаётся мне, что 40 миллионов слов — неправильная цифра.

Смотри сюда:

$ time wc -w v_i_m.txt
38652894 v_i_m.txt

real	0m53.631s
user	0m53.370s
sys	0m0.111s

$ iconv -f cp1251 -t utf8 < v_i_m.txt > v_i_m.utf

$ dos2unix v_i_m.utf

$ time wc -w v_i_m.utf
134721552 v_i_m.utf

real	0m37.964s
user	0m37.664s
sys	0m0.196s

Слов — 134 миллиона с большим хвостиком.

Теперь перл:

$ time ./wc.pl v_i_m.utf
134721552

real	0m43.743s
user	0m42.803s
sys	0m0.793s

$ cat ./wc.pl 
#!/usr/bin/perl
use open ':locale';
my $wc = 0;
local $/ = undef;
while ( my $text = <> ) {
    while ( $text =~ m{\S+}g ) {
        ++ $wc;
    };
};
printf( "%d\n", $wc );

Проигрывает wc всего процентов 15%. Кстати, wc (и мой вариант wc.pl) считает словами любую хрень, разделённую пробелами, в том числе большое количество одиночно стоящих минусов (типа тире). Перловую программу легко исправить чтобы она считала именно слова (скорость, конечно, проседает):

$ cat wc.pl
#!/usr/bin/perl
use open ':locale';
my $wc = 0;
local $/ = undef;
while ( my $text = <> ) {
    while ( $text =~ m{\b\w+\b}g ) {
        ++ $wc;
    };
};
printf( "%d\n", $wc );

$ time ./wc.pl v_i_m.utf
132161274

real	1m43.836s
user	1m42.922s
sys	0m0.753s

А вообще спор дурацкий. Лучший язык — тот, которым владеешь.

debugger ★★★★★
()
Ответ на: комментарий от debugger

без регулярок, наверно быстрее wc будет

anonymous
()

похапе по скорости рвёт всю скриптотню

anonymous
()
Ответ на: комментарий от Twissel
$ time ./test.py
134721552
./test.py  19.93s user 0.27s system 94% cpu 21.309 total

$ time wc -w v_i_m.utf 
134721552 v_i_m.utf
wc -w v_i_m.utf  33.26s user 0.18s system 99% cpu 33.461 total

с регулярками медленнее раза в три

anonymous
()
Ответ на: комментарий от debugger

php, как тут выше заметили, будет быстрее, в том числе и с правильным подсчетом

$ time wc -w v_i_m.utf 
134721552 v_i_m.utf

real    0m28.020s
user    0m27.877s
sys     0m0.127s
$ time php wc.php v_i_m.utf 
134721552 v_i_m.utf

real    0m18.538s
user    0m18.325s
sys     0m0.197s
$ cat ./wc.php 
<?php
$fn = $argv[1];
$fp = fopen($fn, 'r');
$wc = 0;
while (false !== $line = fgets($fp)) {
    $wc += count(preg_split('/\s+/u', $line, -1, PREG_SPLIT_NO_EMPTY));
}
fclose($fp);
printf("%d %s\n", $wc, $fn);
$ sed -i "s/s+/W+/g" wc.php
$ time php wc.php v_i_m.utf 
132161274 v_i_m.utf

real    0m17.522s
user    0m17.333s
sys     0m0.177s
$

e1nste1n ★★★★★
()

Ну и я на сишечке приложусь...

Нет, опечаток тут нет ни одной. =)))

Результат:

time ./wow v_i_m.utf 
w.c. = 178284630

real	0m1,523s
user	0m1,465s
sys	0m0,059s

Сырец:

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* To compile: gcc -O2 -march=native -mtune=native wc.c -o wow */
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>

int main(int argc, const char *argv[]) {
	unsigned char *f;
	char c;
	int size = 0, count = 0;
	struct stat s;
	const char *file_name = argv[1];
	int fd = open(argv[1], O_RDONLY);

	int status = fstat(fd, &s);
	size = s.st_size;

	f = (char *) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
	for (int i = 0; i < size; i++) {
		if(isspace(f[i]))
			count++;
	}
	printf("w.c. = %d\n", count);
	return 0;
}

А так-то, если надо регулярки, то их в сишечке аж две штуки. На выбор: - Это из glibc https://www.gnu.org/software/libc/manual/html_node/Regular-Expressions.html

- И отдельная libpcre. Эта пошустрее малость GNUсной.

Moisha_Liberman ★★
()
Ответ на: Ну и я на сишечке приложусь... от Moisha_Liberman
--- wc-old.c    2018-09-05 04:10:02.591315128 +0300
+++ wc.c        2018-09-05 04:10:41.151317026 +0300
@@ -22,10 +22,10 @@
        size = s.st_size;
 
        f = (char *) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
-       for (int i = 0; i < size; i++) {
-               if(isspace(f[i]))
-                       count++;
-       }
+    int prev, cur;
+    for (int i = 1, prev = isspace(f[0]), cur; i < size; i++, prev = cur) {
+        if((cur = isspace(f[i])) && !prev) ++count;
+    }
        printf("w.c. = %d\n", count);
        return 0;
 }
time ./wow v_i_m.utf 
w.c. = 134721552

real    0m2.541s
user    0m2.496s
sys     0m0.045s
anonymous
()
Ответ на: комментарий от anonymous

завтыкал убрать лишние инты перед циклом + не учёл последнее слово, которое не завершается пробельным символом

anonymous
()
Ответ на: комментарий от debugger

По идее, такие вещи надо на одной машине сравнивать... То есть к твоим-то двум тестам вопросов нет, а у того, кто ниже на сишечке считал, железо может быть совсем другое.

Так-то, я вижу, исходники все выложили, можно дополнить картину...

hobbit ★★★★★
() автор топика
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от anonymous

Да, конечно!

Вообще-то, ежу понятно что isspace() это ни разу не нормальный парсер, т.к. эта функция выхватывает из потока символы пробела, возврата каретки, горизонтальной табуляции, вертикальной табуляции, перевода формата или новой строки. На нормальный парсер это ну ни как не тянет.

Я не случайно оговорился про регулярочки в С. Я так просто ни чего не делаю и не говорю. ;)

Тут в другом прикол, если честно. И вовсе не здесь. =)))

Moisha_Liberman ★★
()
Ответ на: комментарий от hobbit

Да! =)))

Всё верно! Именно:

По идее, такие вещи надо на одной машине сравнивать...

Именно так. Но не только в этом дело. Хотя, мысль в абсолютно верном направлении. Мой ответ чуть ниже будет, во втором ответе к анонимусу. И он тоже не туда полез... =)))

Moisha_Liberman ★★
()
Ответ на: комментарий от anonymous

Не. Не туда... =)))

Код с правками в части isspace() тоже не туда. На самом деле, нам похрен как там оно считается, т.к. повторю сказанное — isspace() это ни как не нормальный парсер. Отчасти нормальный. Просто потому, что например, дефис, окружённый двумя пробелами это будет отдельное слово. Или окружённый символом табуляции и пробела. Как-то не тянет на нормальный парсер слов, если честно.

Я, кстати, замечу, что в файл, который мы тут пытаемся посчитать, даже не заглядывал. Как там оно отформатировано тотально похрен. Тут в другом трюк.

Тут трюк в «магии» в строчке

f = (char *) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);

ТС прав. Надо бы сравнивать на одинаковом железе для чистоты эксперимента. Т.е., чтоб процессор, винчестер и нагрузка на систему были бы одинаковы. Пока же в сравнениях выше, мы просто видим у кого винчестер быстрее. Если бы перловик использовал Sys::Mmap, а питоновец

import mmap ... map = mmap(...)
, то тогда результаты зависели бы от процессора и памяти (скорости обоих). Но хоть не от скорости винчестера. Я, собственно, вот о чём.

Но, hobbit, поверьте на слово, сишечка не сильно ухудшит свои показатели в случае реализации нормального парсера с нормальным подсчётом строк на одном и том же железе и перлу и питону и рубям. Я только ещё попрошу обратить внимание на то, сколько кода потребуется в реализации на С и в реализации на выше перечисленных языках. Просто делаем strip наш_бинарь, смотрим импорт библиотек по ldd, и прикидываем сколько нужно всего добра для запуска аналогичной приблуды на рубях, питоне или перле. Сравнение будет не в пользу скриптовых языков. Тут ещё похапе помянули, ну так и он тоже туда же.

Я, собственно, вот о чём. =)))

Moisha_Liberman ★★
()
Ответ на: Не. Не туда... =))) от Moisha_Liberman

Тьфу, блин!

В строке про питон надо бы вот так как-то:

import mmap

map = mmap.mmap(ну и что там надо)

Прошу прощения.

Moisha_Liberman ★★
()

rperl кто-нибудь пробовал? У меня руки не доходят.

perl5_guy ★★★★★
()
Ответ на: Не. Не туда... =))) от Moisha_Liberman

ясное дело, что дефисы и прочие знаки препинания не учитываются, но важно, что код с патчем выдаёт ровно такой же результат, что и wc, только работает гораздо быстрее.

мы ведь сравниваем один алгоритм используя разные языки/инструменты? поэтому кол-во слов на выходе должно совпадать.

anonymous
()
Ответ на: комментарий от anonymous

В теории да.

Соглашусь. Но здесь у меня вопросы что считать словом и насколько корректно работает сам по себе wc.

А так-то, чисто теоретически, да. Если считаем что wc это эталон, то в итоге код с патчем работает быстрее wc.

Moisha_Liberman ★★
()
Ответ на: комментарий от Deleted

Тред скатился в какую-то бенчмарк-наркоманию.

Как будто что-то плохое. Да, быстродействие не единственное, что я хотел прояснить. Но тоже интересно.

hobbit ★★★★★
() автор топика
Ответ на: Не. Не туда... =))) от Moisha_Liberman

Сравнение будет не в пользу скриптовых языков.

По бинари — вероятно. А вот исходник на скриптовых языках (что куда более важно при необходимости что-то через полгода поправить) явно будет более лаконичным и, скорее всего, более читаемым (но по последнему пункту можно поспорить).

hobbit ★★★★★
() автор топика
Ответ на: комментарий от hobbit

Про исходник и скорость написания соглашусь.

Мой текст (изначальный), если честно, это на основе сниппета в vim. Там есть парочка забавных артефактов. Я про неиспользуемые переменные, например. Ту же char c; Т.е., в vim я просто ввёл wowc (так называется сниппет сам по себе), нажал tab и даже не подтёсывал код. На всё про всё ушло минут 5-7. С компиляцией.

Просто, по факту, у меня по большей части в работе один язык. И это С, чуть реже С++. Так что, оно как-то просто, привычно, понятно. Ну, bash, sql, lua, js какой-нибудь от случая к случаю случающиеся не считаем за языки, с которыми на постоянной основе работаю. Так-то и перл с питоном попадаются, но сишечка это альфа и омега. Так что, тут мне по крайней мере всё просто.

Я абсолютно сознательно ограничиваю себя. Оно понятно что могу, но так же понятно что не хочу. Хотя, я и в курсе что тот же перл одновременно в пределах одного проекта можно и по императивщине и по ООП и по функциональщине задрочить. Но смысл? С тоже может до определённых пределов быть и объектно-ориентированным и даже (о, ужас!) функциональным. Как надо, так и будет. Я нахожу всё, что мне нужно в пределах одного языка (стараюсь, по крайней мере), зачем мне 10 языков?

Ну да, профдеформация наступает. Но как-то пофиг. Денег зарабатываю, да и ладно. Lisp с хаскелем это потом, в следующей жизни. =)))

Moisha_Liberman ★★
()
Ответ на: комментарий от hobbit

Подсчёт пробелов?) Это даже на однострочник не тянет, не говоря уже о нормальном парсинге (на котором тут сишники споткнутся и заебутся), и даже не заикаясь о сложном парсинге.

Deleted
()
Ответ на: комментарий от Deleted

Странно...

(на котором тут сишники споткнутся и заебутся), и даже не заикаясь о сложном парсинге.

Но как-то на практике приходилось парсить CDR (call detail records) и не спотыкались и не заёбывались. Хотя, сколько данных выдаёт любой телекоммуникационный контроллер, это охренеть. Да, в те времена питон был не особо распространён, только-только начинал своё восхождение, видимо нам не нужно было парсить эти данные на С, надо было подождать питона, я понимаю... Тогда бы всё было идеологически правильно. А ещё лучше надо было бы подождать рубей, тогда ещё было бы идеологически правильнее.

Видимо, что-то не так делали, да, я понимаю... Вам безусловно виднее... =))) Вам анекдот про огурцы не напомнить?

Ну а так-то, вон выше два варианта для реализации регулярок. Интересно, на чём же написана та же libpcre? И почему она реализует Perl Compatible Regular Expressions? Как вообще она осмелилась! На сях-то?!?

А можно ещё вопрос? Ну хорошо, перл. А он-то на чём написан? Сам по себе? Неужто на питоне? =)))

Впрочем, простите, это я уже издеваюсь... =)))

Moisha_Liberman ★★
()
Ответ на: Ну и я на сишечке приложусь... от Moisha_Liberman

Я выучил хорошо один урок: программа, во-первых, должна работать корректно, и только во-вторых — быстро.

Задача была считать *слова*. Ты на сишечке офигительно быстро посчитал *пробелы*. И то, не все, а только аскишные. U+00A0 (NO_BREAK SPACE) твоя программа за пробел не считает.

Так что сначала сделай задачу корректно, а уж потом приходи письками меряться.

P. S. Имхо, адекватность человека обратно пропорциональна количеству непарных скобок в его постах.

debugger ★★★★★
()
Ответ на: комментарий от debugger

IMHO, впрочем, это не IMHO, а уверенность.

Вы даже не поняли о чём речь. Соболезную. Всё написано выше.

Moisha_Liberman ★★
()
Ответ на: комментарий от debugger

И да. Вдогон, до кучи.

Кстати, wc (и мой вариант wc.pl) считает словами любую хрень, разделённую пробелами, в том числе большое количество одиночно стоящих минусов (типа тире).

Взято из Есть строкодробилка на замену Перлу? (комментарий)

По-моему, Ваши сетования на поиск моей поделкой пробелов, а не слов несколько натянуто выглядят. В особенности при производительности Вашего поделия на уровне «где-то там, в жопе плетётся».

Уж если мы с Вами оба занимаемся хернёй, то я хотя бы делаю это быстро и с минимумом затрат... =))) Последнее это сарказм, если что, но боюсь, Вам не понять.

Moisha_Liberman ★★
()
Ответ на: Не. Не туда... =))) от Moisha_Liberman

Если бы перловик использовал Sys::Mmap, а питоновец <...>, то тогда результаты зависели бы от процессора и памяти (скорости обоих). Но хоть не от скорости винчестера.

Наивный. mmap данные в память прямо из астрала высасывает?

Сравнение будет не в пользу скриптовых языков.

Это смотря что и с чем сравнивать. Если программу не предполагается запускать сотни раз в секунду, то быстрее набросать скрипт (на любом языке — хоть перл, хоть питон, хоть руби с пыхом) из десятка строк и покончить с задачей, чем долго и нудно ловить блох в паре сотен строк на сишечке, которая работает быстро, но почему то не совсем так, как надо.

debugger ★★★★★
()
Ответ на: комментарий от debugger

Бггг...

Наивный. mmap данные в память прямо из астрала высасывает?

Судя по результатам, видимо да. Да и так, в общем и целом, по ходу, поделка ресурсов не жрёт, не потребляет, она их... только нюхает... =))) Просто потому, что отмапленный в адресное пространство процесса файл, читайте отмапленный в память, является почти файлом, но вот только доступ к нему в разы быстрее. Что и показано моим исходником. Если Вы до сих пор думаете что я там искал пробелы или слова, то прочтите что я написал открытым текстом выше.

Но я здесь не могу не заметить что если бы Вы удосужились использовать Sys::mmap, то и Вам бы полегчало. А так мы сейчас смотрим на то, как Ваша поделка с винтом борется.

Это смотря что и с чем сравнивать.

Нет. Это смотря у кого, как и подо что /dev/hands заточены и достали ли их предварительно из /dev/ass. Перед тем, как код писать. Или у нас тут самооправдания типа «да, я пишу говно, но хотя бы быстро пишу».

Я же (в отличие от Вас) чётко сказал почему поиск пробелов или слов здесь не существенен. Вы пытались замерить производительность? Чего? Своего винчестера? Замеряли. Производительности парсера, ориентированного на поиск строк в произвольном текстовом файле я здесь и близко не увидел. Да, это даже на однострочник не тянет. chinarulezz тут прав как никогда ранее.

Moisha_Liberman ★★
()
Ответ на: Про исходник и скорость написания соглашусь. от Moisha_Liberman

Ну да, профдеформация наступает.

Ну раз ты так проф.деформирован, то явно можешь быстрее любого другого, только изредка использующего си, прекрасно зная все «тёмные стороны» си, определить те «граничные условия», в которых использовать твой любимый си даже со всеми наработками и знаниями - всё равно что гланды перанально удалять... ;)

yyk ★★★★★
()
Ответ на: комментарий от debugger

Предупреждаю: решение "в лоб"!

tony@lenox:~$ time ./counting.py
134721552

real	0m29,391s
user	0m28,907s
sys	0m0,400s
tony@lenox:~$ 
#!/usr/bin/python3.7

with open('v_i_m_utf8.txt') as infile:
    words=0
    for line in infile:
        wordslist = line.split()
        words += len(wordslist)
print(words)

Но, как уже писали выше, чтобы сравнение было хоть сколько-нибудь объективным тесты нужно проводить на одной и той же машине.

Twissel ★★★★★
()

Когда то сравнивал производительность регекспов в php, perl, python2/3, c++ (std, boost, pcre), golang

Так вот в перле были самые быстрые регекспы, приблизительно такую же скорость показал c++ pcre, golang оказался медленнее всех

apmucm
()
Ответ на: комментарий от apmucm

Я тоже когда-то переживал по этому поводу. Теперь просто использую регулярки в питоне и не парюсь, применять регулярки к гигабайтам приходится не часто, а на типичных юзкейсах разницы никакой.

anonymous
()
Ответ на: комментарий от apmucm

приблизительно такую же скорость показал c++ pcre

Стоит отметить ещё, что в перле регексы мощнее pcre.

П.с. А перл6 ускакал далеко вперёд вообще языка регулярных выражений как такового. И для парсинга есть мощнее инструменты.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от apmucm

У golang регекспы в разы медленнее чем у перла и даже питона.

Зато всё остальное быстрее :) А работа программы лимитируется не одними регулярками.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Зато всё остальное быстрее :)

Критичные по производительности куски кода легко пишутся на псевдоязыке xs, который транслируется в си. При этом мощь и выразительность языка остаётся для быстрого написания программ. А в Го тем временем будешь обходить примитивность языковых средств, мастурбируя на скорость, которая в конечном счёте окажется сравнимой.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 2)
Ответ на: комментарий от MyTrooName

koi-8?

Не, я в другой секте состою.)

А исходный txt в cp1251. После iconv -f cp1251 -t utf8 eщё в 2 раза больше стал.

ashot ★★★★
()
Ответ на: комментарий от Deleted

Сравнение xs с Go бессмысленно - на xs никто не программирует и не будет, а на Go - уже 1 миллион человек. Программистам нужно следить за новыми идеями и технологиями в своей области, и Go в этом отношении важен для ознакомления.

Однако эта тема вообще бессмысленна - участники обсуждения называют любой язык, который смогли припомнить. Помимо xs, я тут заметил не менее маргинальный D. Почесал репу, вспомнил название какого нибудь языка - надо срочно написать о нём сюда.

Partisan ★★★★★
()
Ответ на: комментарий от Deleted

А раст то, раст то где? Го заходит только там где заходит пхп, раст всё-таки претендует на роль системного языка.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.