Newsgroup: it.comp.os.linux.development
Titolo: Creare un indice analitico

Sinceramente non so se esiste giא qualcosa del genere cosל
ho pensato di scrivermi uno script per raggiungere lo scopo.
Ho un semplice file di testo e vorrei generare automaticamente
un indice analitico di *tutte* le parole presenti.
Per ogni parola dovrei visualizzare l'occorrenza (il numero
delle volte in cui compare all'interno del documento) e
l'elenco di tutte le righe (basta solo il numero) in cui
compare.
L'output dovrebbe essere qualcosa del genere:
...
pizza (5): 1, 5, 10, 15, 20
peluche (2): 4, 19
...
La cosa si complica poichי le parole possono contenere lettere
accentate (אטילעש) e inoltre una parola potrebbe essere sillabata
a fine riga. Per esempio, se su un riga compare piz-
za, questa dovrebbe essere considerata come la parola "pizza"
e conteggiata normalmente.
Io ci sono riuscito ma ho duvuto lavorare prima di C per togliere
la sillabazione, poi nuovamente di C per elencare tutte le
parole su righe isolate e togliendo i segni di interpunzione ed
infine ho lavorato di grep.
Voi cosa proponete?

 Voi cosa proponete?
Python 2.3 (non che sia tanto piu` difficile con Python piu`
vecchi), qualcosa come (con varie scelte un poco arbitrarie
su specifiche che non dai precisissimamente...):
# -*- coding: iso-8859-1 -*-
def make_index_dict(filename):
    import re, warnings
    reword = re.compile(r'\b\w+\b', re.UNICODE)
    indx = {}
    prev = ''
    for n, line in enumerate(open(filename)):
        words = reword.findall(prev+line)
        if line.endswith('-\n'): prev = words.pop()
        else: prev = ''
        for word in words:
            indx.setdefault(word, []).append(n+1)
    if prev: warning.warn('Ignoro parola tronca su ultima riga (%r)' % prev)
    return indx
def show_index_from_dict(indx):
    words = indx.keys()
    words.sort()
    for word in words:
        lines = indx[word]
        print '%s (%d):' % (word, len(lines)),
        lines = dict.fromkeys(lines).keys()
        print ', '.join(map(str, lines))
def simple_test():
    fn = '/tmp/lap.txt'
    flo = open('/tmp/lap.txt', 'w')
    flo.write(
'''bé, pizza e birra e ancora birra e poi piz-
za e una birra, magari, la pizza é pizza!
''' )
    flo.close()
    indx = make_index_dict(fn)
    show_index_from_dict(indx)
if __name__ == '__main__': simple_test()
Qui ho scelto di usare codifica iso-8859-1 (qualcosa bisogna pur usare se
vuoi lettere accentate...), "addebitare" una parola tronca a fine riga
alla riga su cui _termina_, mostrare il numero di occorrenze come totale
(anche nel caso di + occorrenze su di una riga) ma non mostrare duplicati
nell'elenco di righe, considerare separate le parole minuscole e maiuscole,
dare un warning ma non un errore se una parola tronca e` alla fine
dell'ultima riga ed ignorare tale "mezza parola"... e senza dubbio altre
scelte non rigorosamente specificate.  Buffo come il 90% dei pensieri di
un programmatore debbano sempre rivolgersi ai "casi limite" e alle varie
ambiguita` che nella vita comune non ci disturbano + di tanto:-).
Comunque, ecco l'output nel caso di esempio simple_test:
[alex@lancelot bo]$ python ia.py
ancora (1): 1
birra (3): 1, 2
bé (1): 1
e (4): 1, 2
la (1): 2
magari (1): 2
pizza (4): 1, 2
poi (1): 1
una (1): 2
é (1): 2
[alex@lancelot bo]$
Il programmillo e` cross-platform (gira altrettanto bene su Mac, Windows
ecc:-) ma dipende naturalmente dal linguaggio che usa (Python) quindi se
vuoi discuterne conviene magari spostarci su it.comp.lang.python .
Alex

...
una soluzione l'hai già avuta (probabilmente migliore)
Ma tanto per infilarsi nella discussione, ecco la mia in ruby
(non è stat testata molto ,linux con Latin1 e msys su winxp, ma
dovrebbe funzionare su win,dos,mac,*nix.. poi chissà se funge davvero
:-)
gabriele@MOSES-058H45GRC /e
$ ruby index.rb tmp.txt  
ancora (1) [0]
birra (2) [0, 1]
bé (1) [0]
e (2) [0, 1]
la (1) [1]
magari (1) [1]
pizza (2) [0, 1]
poi (1) [0]
una (1) [1]
é (1) [1]
gabriele@MOSES-058H45GRC /e
$ cat tmp.txt 
bé, pizza e birra e ancora birra e poi piz-
za e una birra, magari, la pizza é pizza!
gabriele@MOSES-058H45GRC /e
$ cat index.rb
idx= Hash.new {|k,v| k[v]=Hash.new } # creiamo un Hash di Hash
a_capo=false # quando ci capita '-' uniremo la parola della 
             # linea prima alla linea dopo
File.new(ARGV[0]).each_with_index do |lin,num| # iteriamo sul file, 
  if a_capo        # se all'iterazione precedente la linea aveva un a
capo
    lin=a_capo+lin # lo uniamo a questa
    a_capo=false   
  end
   # scan() prende le parole valide e le passa al blocco una per volta
  lin.scan(/[-a-zA-Zàèìòùé]+/).each do |parola|
    if parola[-1].chr == '-'  # se l'ultimo carattere è '-'
      a_capo=parola.chop      # lo salviamo come a_capo senza il '-'
      next                    # e passiamo all'iterazione seguente  
    end  
    idx[parola][num]=true
  end
end
idx.sort.each do |parola,linee|
    # inspect è la rappresentazione intuitiva dell'array, [elem,
elem,...]
    puts parola+' ('+linee.length.to_s+') '+linee.keys.inspect 
end

 Python 2.3
Grazie ad entrambi per le risposte. Mi piace molto
la caratteristica cross-platform di entrambi i programmi.
Mi stupisce ogni volta vedere questi programmi nei
"linguaggi alternativi".
Io sono un inossidabile programmatore C (anche perché
programmo microcontrollori per lavoro) e tendo sempre
a risolvere problemi in quel modo, a furia di puntatori
e allocazioni varie.
Vedo, invece, che questi linguaggi si diffondono sempre
più e un giorno dovrò interessarmi maggiormente a loro.

 writes:
bard@prism:~$ cat occur.rb
text = "Sinceramente non so se esiste già qualcosa del gene-
re così ho pensato di scrivermi uno script per raggiungere lo sco-
po.
Ho un semplice file di testo e vorrei generare automatica-
mente un indice analitico di tutte le parole presenti.
Per ogni parola dovrei visualizzare l'occorrenza (il nu-
mero delle volte in cui compare all'interno del documento) e
l'elenco di tutte le righe (basta solo il numero) in cui
compare."
class String
  def dehyphenate
    gsub(/-\n([^\s]+)\s*/m, "\\1\n")
  end
  def depunctuate 
    gsub(/[[:punct:]]/, '')
  end
end
findings = {}
text.dehyphenate.depunctuate.each_with_index do |line, number|
  line.split(/\s+/).each do |word|
    findings[word] = {'occur' => 0, 'lines' => []} if findings[word].nil?
    findings[word]['occur'] += 1
    findings[word]['lines'] << number
  end
end
findings.each do |word, data|
  puts "#{word} (#{data['occur']}): #{data['lines'].join(', ')}"
end
bard@prism:~$ ruby occur.rb
visualizzare (1): 4
analitico (1): 3
vorrei (1): 2
script (1): 1
uno (1): 1
del (2): 0, 5
cui (2): 5, 6
generare (1): 2
scopo (1): 1
genere (1): 0
Sinceramente (1): 0
documento (1): 5
semplice (1): 2
scrivermi (1): 1
esiste (1): 0
non (1): 0
il (2): 4, 6
parola (1): 4
le (2): 3, 6
un (2): 2, 3
lo (1): 1
delle (1): 5
dovrei (1): 4
tutte (2): 3, 6
basta (1): 6
in (2): 5, 6
indice (1): 3
e (2): 2, 5
di (4): 1, 2, 3, 6
così (1): 1
righe (1): 6
loccorrenza (1): 4
automaticamente (1): 2
pensato (1): 1
so (1): 0
Per (1): 4
presenti (1): 3
parole (1): 3
testo (1): 2
Ho (1): 2
per (1): 1
ho (1): 1
se (1): 0
lelenco (1): 6
volte (1): 5
ogni (1): 4
raggiungere (1): 1
già (1): 0
solo (1): 6
compare (2): 5, 7
qualcosa (1): 0
allinterno (1): 5
numero (2): 4, 6
file (1): 2
bard@prism:~$ 
-- 
    if (argc > 1 && strcmp(argv[1], "-advice") == 0) {
	printf("Don't Panic!\n");
	exit(42);
    }

 più e un giorno dovrò interessarmi maggiormente a loro.
Dagli un occhio, li troverai divertenti, nonche` utili per
un tot di cose.  A mio parere Ruby e` piu` da smanettone,
anche perche` meno maturo, quindi ancora piu` "plastico",
piu` adatto a "stupire con effetti speciali", "one-liners",
etc; Python piu` maturo, stabile, ottimizzato, con una
congerie di estensioni e add-on compreso un "compilatore
specializzante", piu` adatto alla programmazione di gruppo
in quanto centrato sulla semplicita` e l'uniformita`; ma
sono sfumature -- dal punto di vista del programmatore C,
entrambi i linguaggi sono ad altissimo livello, OO ma non
"aggressivi" nell'importi l'OO (come potrebbe esserlo ad
esempio Java o Eiffel o Smalltalk...), con tipizzazione
stretta (Python un po' piu` stretta) ma dinamica (forse
Ruby un po' piu` dinamico), ricoprono piu` o meno la stessa
nicchia (entrambi assai bene, benche` con tante differenze).
Personalmente preferisco Python, ma giudico Ruby degno del
piu` alto rispetto; se i miei obbiettivi generali fossero
diversi (smanettare per conto mio, lasciare "la mia impronta"
sul linguaggio, ecc ecc) potrei facilmente preferire Ruby.
Alex