Lezione del 7/4/2014

Problema N.3 (lezione precedente)

Leggere un file ascii, separare i vari paragrafi cioè i blocchi di parole in linee consecutive non contenenti linee vuote.

Salvare ogni paragrafo in un vettore e farsi stampare sul terminale il numero di paragrafo il numero di parole che contiene. Compattare (giustificare) ogni paragrafo in modo che riempia al meglio 80 colonne. Salvare su un altro file il file giustificato.

Possibile soluzione (versione semplice)

File 5.3_giustificazione_testo_semplice.rb

  1#-------------------------------------------------------------------------#
  2#  Esercitazioni in Laboratorio per il Corso di                           #
  3#  Fondamenti di Informatica e Calcolo Numerico, AA 2013/2014             #
  4#                                                                         #
  5#  Autori:   Enrico Bertolazzi e Carlos Maximiliano Giorgio Bort          #
  6#            Dipartimento di Ingeneria Industriale, Universita` di Trento #
  7#  Sito web: http://www.ing.unitn.it/~bertolaz/                           #
  8#                                                                         #
  9#  Contatti: enrico.bertolazzi@unitn.it, cm.giorgiobort@unitn.it          #
 10#                                                                         #
 11#  Copyright (c) 2014 E.Bertolazzi e C.M. Giorgio Bort                    #
 12#-------------------------------------------------------------------------#
 13
 14#__________________________________________________________________________
 15# 1. Leggere il file 'testo.txt', salvare ogni paragrafo in una hash
 16#    e farsi stampare sul terminale ogni riga.
 17#    Durante questa operazione fare in modo di trascurare le righe
 18#    corrispondenti all'autore, data, e titolo del documento
 19#__________________________________________________________________________
 20file = 'testo.txt'
 21
 22# verifico se il file esiste
 23esiste = File.exists? file
 24raise ArgumentError, "Il file #{file} non esiste" unless esiste
 25
 26ht = {:testo => []}
 27n_linee_vuote = 0
 28IO.foreach(file) do |line|
 29  # stampo la linea
 30  puts line
 31  # pulisco la linea dai terminatori di stringa
 32  line.chomp!
 33  # salvo il testo in un array
 34  ht[:testo] << line if n_linee_vuote >= 2
 35  # conto il numero delle linee vuote
 36  n_linee_vuote += 1 if line.empty?
 37end
 38
 39
 40
 41
 42#__________________________________________________________________________
 43# 2. Contare e farsi stampare il numero di paragrafi, parole e di caratteri
 44#    (inclusi spazi bianchi) nel file.
 45#__________________________________________________________________________
 46ht[:n_paragrafi] = ht[:testo].size
 47ht[:n_parole]    = 0
 48ht[:n_caratteri] = 0
 49ht[:testo].each do |paragrafo|
 50  # numero di caratteri
 51  ht[:n_caratteri] += paragrafo.size
 52  ht[:n_parole]    += paragrafo.split(" ").size
 53end
 54
 55puts "_"*40
 56puts "Il testo contiene:\n\t#{ht[:n_paragrafi]} paragrafi\n\t#{ht[:n_parole]} parole\n\t#{ht[:n_caratteri]} caratteri"
 57puts "_"*40
 58
 59
 60
 61
 62#__________________________________________________________________________
 63# 3. Giustificare il testo con una larghezza della pagina pari a
 64#__________________________________________________________________________
 65ht[:larghezza_testo] = 100 # numero di caratteri in una linea
 66
 67# quando non posso aggiungere caratteri per completare la mia linea, 
 68# aggiungo spazi bianchi dopo ogni parola a partire da sinistra.
 69def riempi_stringa(str, lunghezza)
 70  raise ArgumentError, "Il primo argomento deve essere una stringa" unless str.is_a? String
 71  raise ArgumentError, "Il secondo argomento deve essere un intero" unless lunghezza.is_a? Integer
 72
 73  l = str.size
 74  nsb = lunghezza-l # numero di spazi bianchi da aggiungere
 75  
 76  
 77  return str if nsb == 0
 78    
 79  par = str.chomp.split " "
 80  
 81  i = 1
 82  while nsb > 0
 83    # post poni uno spazio bianco alla fine di ogni parola
 84    par[i].insert(0, " ")
 85    i   += 1
 86    nsb -= 1
 87  end
 88  
 89  nuova_linea = par.join " "
 90  l = nuova_linea.size
 91  
 92  # se le parole nella linea sono meno degli spazi bianchi, 
 93  # aggiungo spazi bianchi dopo l'ultima parola 
 94  nsb = lunghezza-l # numero di spazi bianchi da aggiungere
 95  nuova_linea << " "*nsb
 96  
 97  l = nuova_linea.size
 98  begin
 99    msg = "La lunghezza della linea #{nuova_linea.inspect} di #{l} non e` compatibile con #{lunghezza}"
100    raise RuntimeError, msg unless l == lunghezza
101  rescue
102    binding.pry
103  end
104  nuova_linea << "\n" # la linea deve andare accapo
105  return nuova_linea
106end # def
107
108
109# Separo ogni parola in ogni paragrafo nel testo
110testo_separato = []
111ht[:testo].each{|par| testo_separato << par.split(" ") }
112
113# Giustifico il testo. Ogni array (i.e. paragrafo) contiene le stringhe 
114# giustificate (i.e. linee) separate da terminatori di stringa
115ht[:testo_giustificato] = []
116ht[:testo].each do |paragrafo|
117  parole = paragrafo.split " "
118  
119  paragrafo_giustificato = ""
120
121  linea = ""
122  
123  # fin tanto che non ho giustificato tutte le parole...
124  while parole.size > 0
125    # togli la parola dall'array 'parole' e mettila in 'p'
126    p = parole.shift
127    
128    # devo aggiungere la parola alla linea solo se a valle di questa operazione
129    # la conta dei caratteri non eccede ht[:larghezza_testo]
130    linea_giustificata = linea + " " + p
131    
132    l = linea_giustificata.size
133    
134    if l < ht[:larghezza_testo]
135      linea = linea_giustificata
136      
137      # se la linea e` abbastanza corta e ho finito le parole nel paragrafo,
138      # allora appendi la linea al paragrafo cosi` com'e`
139      if parole.size == 0
140        paragrafo_giustificato << linea << "\n"
141        # reinizializza la linea
142        linea = ""
143      end
144      
145    else
146      linea = riempi_stringa(linea, ht[:larghezza_testo])
147      
148      # aggiungo la linea al paragrafo
149      paragrafo_giustificato << linea
150      
151      # reinizializza la linea con la parola che avanza dalla linea precedente
152      linea = p
153      
154      # se 'p' e` l'ultima parola del paragrafo, mandala accapo
155      paragrafo_giustificato << p << "\n" if parole.empty?
156      
157    end # if
158  end # while parole
159  
160  paragrafo_giustificato << "\n"
161  ht[:testo_giustificato] << paragrafo_giustificato
162end # .each
163
164puts "="*40
165ht[:testo_giustificato].each{|par| puts par}
166
167
168#__________________________________________________________________________
169# 3. Salvare il testo giustificato in un nuovo file
170#__________________________________________________________________________
171
172File.open("testo_giustificato.txt", 'w') do |file|
173  ht[:testo_giustificato].each{|par| file.puts par}
174end

Possibile soluzione (versione completa)

File 5.4_giustificazione_testo_completo.rb

  1#-------------------------------------------------------------------------#
  2#  Esercitazioni in Laboratorio per il Corso di                           #
  3#  Fondamenti di Informatica e Calcolo Numerico, AA 2013/2014             #
  4#                                                                         #
  5#  Autori:   Enrico Bertolazzi e Carlos Maximiliano Giorgio Bort          #
  6#            Dipartimento di Ingeneria Industriale, Universita` di Trento #
  7#  Sito web: http://www.ing.unitn.it/~bertolaz/                           #
  8#                                                                         #
  9#  Contatti: enrico.bertolazzi@unitn.it, cm.giorgiobort@unitn.it          #
 10#                                                                         #
 11#  Copyright (c) 2014 E.Bertolazzi e C.M. Giorgio Bort                    #
 12#-------------------------------------------------------------------------#
 13
 14#__________________________________________________________________________
 15# 1. Leggere il file 'testo.txt', salvare ogni paragrafo in una hash
 16#    e farsi stampare sul terminale ogni riga.
 17#    Durante questa operazione fare in modo di trascurare le righe
 18#    corrispondenti all'autore, data, e titolo del documento
 19#__________________________________________________________________________
 20file = 'testo.txt'
 21
 22# verifico se il file esiste
 23esiste = File.exists? file
 24raise ArgumentError, "Il file #{file} non esiste" unless esiste
 25
 26ht = {:testo => []}
 27n_linee_vuote = 0
 28IO.foreach(file) do |line|
 29  # stampo la linea
 30  puts line
 31  # pulisco la linea dai terminatori di stringa
 32  line.chomp!
 33  # salvo il testo in un array
 34  ht[:testo] << line if n_linee_vuote >= 2
 35  # conto il numero delle linee vuote
 36  n_linee_vuote += 1 if line.empty?
 37end
 38
 39
 40
 41
 42#__________________________________________________________________________
 43# 2. Contare e farsi stampare il numero di paragrafi, parole e di caratteri
 44#    (inclusi spazi bianchi) nel file.
 45#__________________________________________________________________________
 46ht[:n_paragrafi] = ht[:testo].size
 47ht[:n_parole]    = 0
 48ht[:n_caratteri] = 0
 49ht[:testo].each do |paragrafo|
 50  # numero di caratteri
 51  ht[:n_caratteri] += paragrafo.size
 52  ht[:n_parole]    += paragrafo.split(" ").size
 53end
 54
 55puts "_"*40
 56puts "Il testo contiene:\n\t#{ht[:n_paragrafi]} paragrafi\n\t#{ht[:n_parole]} parole\n\t#{ht[:n_caratteri]} caratteri"
 57puts "_"*40
 58
 59
 60
 61
 62#__________________________________________________________________________
 63# 3. Giustificare il testo con una larghezza della pagina pari a
 64#__________________________________________________________________________
 65ht[:larghezza_testo] = 100 # numero di caratteri in una linea
 66
 67# quando non posso aggiungere caratteri per completare la mia linea,
 68# aggiungo spazi bianchi in corrispondenza delle parole piu` lunghe
 69def riempi_stringa(str, lunghezza)
 70  raise ArgumentError, "Il primo argomento deve essere una stringa" unless str.is_a? String
 71  raise ArgumentError, "Il secondo argomento deve essere un intero" unless lunghezza.is_a? Integer
 72
 73  l = str.size
 74  nsb = lunghezza-l # numero di spazi bianchi da aggiungere
 75  
 76  
 77  return str if nsb == 0
 78    
 79  par = str.chomp.split " "
 80  # ordino le parole dalla piu` lunga alla piu` corta
 81  par_ord = par.sort{|x,y| y.size <=> x.size}
 82  
 83  while nsb > 0
 84    # trova la posizione della parola piu` lunga nella frase
 85    i = par.index par_ord.shift
 86    
 87    # non aggiungere spazi bianchi prima della prima parola nella linea
 88    next if i == 0
 89    
 90    # preponi uno spazio bianco alla parola piu` lunga
 91    par[i].insert(0, " ")
 92    
 93    nsb -= 1
 94  end
 95  
 96  nuova_linea = par.join " "
 97  l = nuova_linea.size
 98  
 99  # se le parole nella linea sono meno degli spazi bianchi,
100  # aggiungo spazi bianchi dopo l'ultima parola 
101  nsb = lunghezza-l # numero di spazi bianchi da aggiungere
102  nuova_linea << " "*nsb
103  
104  l = nuova_linea.size
105  begin
106    msg = "La lunghezza della linea #{nuova_linea.inspect} di #{l} non e` compatibile con #{lunghezza}" 
107    raise RuntimeError, msg unless l == lunghezza
108  rescue
109    binding.pry
110  end
111  nuova_linea << "\n" # la linea deve andare accapo
112  return nuova_linea
113end # def
114
115
116# Separo ogni parola in ogni paragrafo nel testo
117testo_separato = []
118ht[:testo].each{|par| testo_separato << par.split(" ") }
119
120# Giustifico il testo. Ogni array (i.e. paragrafo) contiene 
121# le stringhe giustificate (i.e. linee) separate da terminatori di stringa
122ht[:testo_giustificato] = []
123ht[:testo].each do |paragrafo|
124  parole = paragrafo.split " "
125  
126  paragrafo_giustificato = ""
127
128  linea = ""
129  
130  # fin tanto che non ho giustificato tutte le parole...
131  while parole.size > 0
132    # togli la parola dall'array 'parole' e mettila in 'p'
133    p = parole.shift
134    
135    # devo aggiungere la parola alla linea solo se a valle di questa
136    # operazione la conta dei caratteri non eccede ht[:larghezza_testo]
137    linea_giustificata = linea + " " + p
138    
139    l = linea_giustificata.size
140    
141    if l < ht[:larghezza_testo]
142      linea = linea_giustificata
143      
144      # se la linea e` abbastanza corta e ho finito le parole nel paragrafo,
145      # allora appendi la linea al paragrafo cosi` com'e`
146      if parole.size == 0
147        paragrafo_giustificato << linea << "\n"
148        # reinizializza la linea
149        linea = ""
150      end
151      
152    else
153      linea = riempi_stringa(linea, ht[:larghezza_testo])
154      
155      # aggiungo la linea al paragrafo
156      paragrafo_giustificato << linea
157      
158      # reinizializza la linea con la parola che avanza dalla linea precedente
159      linea = p
160      
161      # se 'p' e` l'ultima parola del paragrafo, mandala accapo
162      paragrafo_giustificato << p << "\n" if parole.empty?
163      
164    end # if
165  end # while parole
166  
167  paragrafo_giustificato << "\n"
168  ht[:testo_giustificato] << paragrafo_giustificato
169end # .each
170
171puts "="*40
172ht[:testo_giustificato].each{|par| puts par}
173
174
175#__________________________________________________________________________
176# 3. Salvare il testo giustificato in un nuovo file
177#__________________________________________________________________________
178
179File.open("testo_giustificato.txt", 'w') do |file|
180  ht[:testo_giustificato].each{|par| file.puts par}
181end

File testo

testo.txt