Я пошел бы со второй идеей. Вот простая программа Perl, которая прочитает список слов из первого обеспеченного файла и распечатает количество каждого слова в списке из второго файла, обеспеченного в разделенном от вкладки формате. Списку слов в первом файле нужно предоставить тот на строку.
#!/usr/bin/perl
use strict;
use warnings;
my $word_list_file = shift;
my $process_file = shift;
my %word_counts;
# Open the word list file, read a line at a time, remove the newline,
# add it to the hash of words to track, initialize the count to zero
open(WORDS, $word_list_file) or die "Failed to open list file: $!\n";
while (<WORDS>) {
chomp;
# Store words in lowercase for case-insensitive match
$word_counts{lc($_)} = 0;
}
close(WORDS);
# Read the text file one line at a time, break the text up into words
# based on word boundaries (\b), iterate through each word incrementing
# the word count in the word hash if the word is in the hash
open(FILE, $process_file) or die "Failed to open process file: $!\n";
while (<FILE>) {
chomp;
while ( /-$/ ) {
# If the line ends in a hyphen, remove the hyphen and
# continue reading lines until we find one that doesn't
chop;
my $next_line = <FILE>;
defined($next_line) ? $_ .= $next_line : last;
}
my @words = split /\b/, lc; # Split the lower-cased version of the string
foreach my $word (@words) {
$word_counts{$word}++ if exists $word_counts{$word};
}
}
close(FILE);
# Print each word in the hash in alphabetical order along with the
# number of time encountered, delimited by tabs (\t)
foreach my $word (sort keys %word_counts)
{
print "$word\t$word_counts{$word}\n"
}
Если файл words.txt содержит:
linux
frequencies
science
words
И файл text.txt содержит текст Вашего сообщения, следующей команды:
perl analyze.pl words.txt text.txt
распечатает:
frequencies 3
linux 1
science 1
words 3
Обратите внимание, что повреждение на границах слова с помощью \b не может работать способ, которым Вы хотите во всех случаях, например, если Ваши текстовые файлы содержат слова, которые пишутся через дефис через строки, необходимо будет сделать что-то немного более интеллектуальное для соответствия им. В этом случае Вы могли проверить, чтобы видеть, является ли последний знак в строке дефисом и, если это, просто удалите дефис и считайте другую строку прежде, чем разделить строку на слова.
Править: Обновленная версия, которая обрабатывает слова нечувствительно к регистру и обрабатывает написанные через дефис слова через строки.
Обратите внимание, что, если существуют написанные через дефис слова, некоторые из которых повреждаются через строки и некоторых, которые не являются, это не найдет их всех, потому что это только удалило дефисы в конце строки. В этом случае можно хотеть просто удалить все дефисы и распознать слова после того, как дефисы будут удалены. Можно сделать это путем простого добавления следующей строки прямо перед функцией разделения:
s/-//g;
Я делаю этот вид вещи со сценарием как следующее (в синтаксисе удара):
for file in *.txt
do
sed -r 's/([^ ]+) +/\1\n/g' "$file" \
| grep -F -f 'go-words' \
| sort | uniq -c > "${file}.frq"
done
можно настроить regex, который Вы используете для разграничивания отдельных слов; в примере я просто рассматриваю пробел как разделитель.-f аргументом grep является файл, который содержит Ваши слова интереса, один на строку.
Я сделал бы "grep" на файлах для нахождения всех строк, которые содержат ключевые слова. (Grep-f может использоваться для определения входного файла слов для поиска (передайте вывод по каналу grep в файл). Это даст Вам список строк, которые содержат экземпляры Ваших слов. Затем сделайте "sed", чтобы заменить Ваших разделителей слов (скорее всего, пробелы) с новыми строками, дать Вам файл отдельных слов (одно слово на строку). Теперь пробежавший grep снова, с Вашим тем же списком слов, кроме этого времени указывают-c (для получения количества строк с указанными словами; т.е. количество случаев слова в исходном файле).
метод с двумя передачами просто делает жизнь легче для "sed"; первый grep должен устранить много строк.
можно сделать это все в основных командах командной строки Linux. После того как Вы довольны процессом, можно поместить все это в сценарий оболочки довольно легко.
Я предполагаю, что новые файлы представляются со временем, и это - то, как вещи изменяются?
я считаю, что Ваш лучший выбор состоял бы в том, чтобы пойти с чем-то как Ваша опция 2. Нет большого количества точки, предварительно обрабатывающей файлы, если все, что Вы хотите сделать, считать случаи ключевых слов. Я просто прошел бы каждый файл однажды, рассчитав каждый раз, когда слово в Вашем списке появляется. Лично я сделал бы это в Ruby, но язык как жемчуг или Python также сделает эту задачу довольно простой. Например, Вы могли использовать ассоциативный массив с ключевыми словами как ключи и количество случаев как значения. (Но это могло бы быть слишком упрощенно, если необходимо хранить больше информации о случаях).
я не уверен, хотите ли Вы хранить информацию на файл, или о целом наборе данных? Я предполагаю, что это не было бы слишком трудно для слияния.
я не уверен в том, что сделать с данными, после того как у Вас есть они - экспорт, к электронной таблице было бы хорошо, если это дает Вам, в чем Вы нуждаетесь. Или Вы могли бы найти легче в конечном счете только написать немного дополнительного кода, который отображает данные приятно для Вас. Зависит от того, что Вы хотите сделать с данными (например, если Вы хотите произвести всего несколько диаграмм в конце осуществления и поместить их в отчет, затем экспортирование в CSV, вероятно, имело бы большую часть смысла, тогда как, если Вы хотите генерировать новый набор данных каждый день в течение года, затем создающего инструмент, чтобы сделать, это автоматически - почти наверняка лучшая идея.
Редактирование: Я просто выяснил, что, так как Вы изучаете историю, возможности являются Вашими документами, не изменяются со временем, а скорее отражают ряд изменений, которые уже произошли. Извините за недоразумение этого. Так или иначе я думаю в значительной степени все, что я сказал выше, все еще применяется, но я предполагаю, что Вы склонитесь к движению с экспортом в CSV или что имеет Вас, а не автоматизированный дисплей.
Походит на забавный проект - удачи!
Ben
Сначала ознакомьтесь с лексическим анализом и как записать спецификацию генератора сканера. Считайте введения в использование инструментов как YACC, Закон, Бизон, или мой любимый, JFlex. Здесь Вы определяете то, что составляет маркер. Это - то, где Вы узнаете о том, как создать токенизатор.
Следующий Вы имеете то, что называют списком семени. Противоположность стоп-листа обычно упоминается как список запуска или ограниченный словарь. Словарь также был бы хорошей вещью узнать о. Часть приложения должна загрузить список запуска в память, таким образом, это может быть быстро запрошено. Типичным способом сохранить является файл с одним словом на строку, затем считайте это в в начале приложения, однажды, во что-то как карта. Вы могли бы хотеть узнать о понятии хеширования.
Отсюда Вы хотите думать об основном алгоритме и структурах данных, необходимых хранить результат. Распределение легко представлено как двумерный разреженный массив. Изучите основы разреженной матрицы. Вам не требуются 6 месяцев линейной алгебры для понимания то, что она делает.
, поскольку Вы работаете с большими файлами, я защитил бы подход на основе потоков. Не читайте в целом файле в память. Считайте его как поток в токенизатор, который производит поток маркеров.
В следующей части алгоритма думают о том, как преобразовать маркерный список в список, содержащий только слова, которые Вы хотите. Если Вы думаете об этом, список находится в памяти и может быть очень большим, таким образом, лучше отфильтровать non-start-words в запуске. Таким образом в критической точке, где Вы получаете новый маркер от токенизатора и прежде, чем добавить его к маркерному списку, сделайте поиск в start-words-list в оперативной памяти, чтобы видеть, является ли слово словом запуска. Если так, сохраните его в выходном маркерном списке. Иначе проигнорируйте его и переместитесь в следующий маркер, пока целый файл не будет считан.
Теперь у Вас есть список маркеров только интереса. Вещь, Вы не смотрите на другие метрики индексации как положение и случай и контекст. Поэтому Вам действительно не нужен список всех маркеров. Вы действительно просто хотите разреженную матрицу отличных маркеров со связанными количествами.
Так, сначала создайте пустую разреженную матрицу. Затем думайте о вставке недавно найденного маркера во время парсинга. Когда это происходит, увеличьте его количество, если его в списке или иначе вставляет новый маркер с количеством 1. На этот раз, в конце парсинга файла, у Вас есть список отличных маркеров, каждого с частотой по крайней мере 1.
, Что список теперь в мадам и можно сделать то, что Вы хотите. Дамп его к файлу CSV был бы тривиальным процессом итерации по записям и записи каждой записи на строку с ее количеством.
В этом отношении, смотрите на некоммерческий продукт под названием "ЛОГИЧЕСКИЙ ЭЛЕМЕНТ" или коммерческий продукт как TextAnalyst или продукты, перечисленные в http://textanalysis.info
Другая попытка Perl:
#!/usr/bin/perl -w
use strict;
use File::Slurp;
use Tie::File;
# Usage:
#
# $ perl WordCount.pl <Files>
#
# Example:
#
# $ perl WordCount.pl *.text
#
# Counts words in all files given as arguments.
# The words are taken from the file "WordList".
# The output is appended to the file "WordCount.out" in the format implied in the
# following example:
#
# File,Word1,Word2,Word3,...
# File1,0,5,3,...
# File2,6,3,4,...
# .
# .
# .
#
### Configuration
my $CaseSensitive = 1; # 0 or 1
my $OutputSeparator = ","; # another option might be "\t" (TAB)
my $RemoveHyphenation = 0; # 0 or 1. Careful, may be too greedy.
###
my @WordList = read_file("WordList");
chomp @WordList;
tie (my @Output, 'Tie::File', "WordCount.out");
push (@Output, join ($OutputSeparator, "File", @WordList));
for my $InFile (@ARGV)
{ my $Text = read_file($InFile);
if ($RemoveHyphenation) { $Text =~ s/-\n//g; };
my %Count;
for my $Word (@WordList)
{ if ($CaseSensitive)
{ $Count{$Word} = ($Text =~ s/(\b$Word\b)/$1/g); }
else
{ $Count{$Word} = ($Text =~ s/(\b$Word\b)/$1/gi); }; };
my $OutputLine = "$InFile";
for my $Word (@WordList)
{ if ($Count{$Word})
{ $OutputLine .= $OutputSeparator . $Count{$Word}; }
else
{ $OutputLine .= $OutputSeparator . "0"; }; };
push (@Output, $OutputLine); };
untie @Output;
, Когда я поместил Ваш вопрос в файл wc-test
и ответ Robert Gamble в wc-ans-test
, Выходной файл похож на это:
File,linux,frequencies,science,words
wc-ans-test,2,2,2,12
wc-test,1,3,1,3
Это - разделенное значение запятой (csv) файл (но можно изменить разделитель в сценарии). Это должно быть читаемо для любого приложения для обработки электронных таблиц. Для построения графиков я рекомендовал бы gnuplot
, который полностью scriptable, таким образом, можно настроить вывод независимо от входных данных.
К черту большие сценарии. Если вы хотите получить все слова, попробуйте эту оболочку fu:
cat *.txt | tr A-Z a-z | tr -cs a-z '\n' | sort | uniq -c | sort -rn |
sed '/[0-9] /&, /'
Это (проверено) даст вам список всех слов, отсортированных по частоте в формате CSV, которые легко импортируются из вашей любимой электронной таблицы. Если вам нужны стоп-слова, попробуйте вставить grep -w -F -f stopwords.txt
в конвейер (не тестировалось).