Skip to content

Commit cbce691

Browse files
committed
Final push
1 parent 5b87cc9 commit cbce691

File tree

4 files changed

+242
-89
lines changed

4 files changed

+242
-89
lines changed

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"python.pythonPath": "/bin/python3",
2+
"python.pythonPath": "C:\\Users\\Miguel Lages\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe",
33
"discord.enabled": true
44
}

v2/README.txt

+36-10
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,54 @@
1-
Grupo 22 - Novembro/2020
1+
Grupo 22 - Dezembro/2020
22

33
55373 - José Almeida
44
55371 - Augusto Gouveia
55
54975 - Miguel Lages
66

77
Funcionalidades:
88

9-
• Suporta duas versões de paralelismo: multiprocessing (pgrepwc.py) e multithreading (pgrepwc_threads.py).
9+
[pgrepwc.py]:
1010

11-
- O uso do pacote multiprocessing neste projeto permite-nos certificar que este funciona de maneira igualmente eficiente em Linux e Windows (algo impossibilitado pelo uso de os.fork(), visto que este não funciona em Windows).
11+
• Utilização: pgrepwc [-c|-l] [-p n] [-a s] [-f file] [-h] palavra <ficheiros>
1212

13-
Realçamento dos números das linhas a verde e das correspondências a vermelho, em ambas versões do programa (disponível em ambos Linux e Windows).
13+
Opção "-h" que permite esconder o output.
1414

15-
• Uso de mecanismos de exclusão mútua (mutex lock) para assegurar que não se dão problemas de sincronização (e.g. escrita simultânea na mesma variável por parte de processos/threads diferentes) e que a exposição dos resultados por parte dos processos/threads se dá de maneira intercalada.
15+
• Uso de mecanismos de memória partilhada para comunicar resultados de pesquisas/contagens ao
16+
processo pai por parte dos processos-filho.
1617

17-
• Uso de expressões regulares para encontrar correspondências exatas da palavra especi-
18-
ficada em situações que esta esteja isolada.
18+
• Suporta paralelismo: multiprocessing.
1919

20-
Estrutura robusta que permite aceder aos ficheiros-alvo sem ter que os carregar diretamente para a memória (especialmente útil para ficheiros de maiores dimensões).
20+
Realçamento dos números das linhas a verde e das correspondências a vermelho.
2121

22-
• Programação defensiva que permite que o programa não falhe quando um dos ficheiros referidos não é encontrado. Inclui também prevenção de repetição de procura em ficheiros, descartando ficheiros repetidos que possam ter sido introduzidos pelo utilizador.
22+
• Uso de mecanismos de exclusão mútua (mutex lock) para assegurar que não se dão problemas de
23+
sincronização (ex. escrita simultânea na mesma variável por parte de processos/threads diferentes)
24+
e que a exposição dos resultados por parte dos processos/threads se dá de maneira intercalada.
2325

24-
• [pgrepwc.py] -> Uso de mecanismos de memória partilhada para comunicar resultados de pesquisas/contagens ao processo pai por parte dos processos-filho.
26+
• Uso de expressões regulares para encontrar correspondências exatas da palavra especificada em
27+
situações que esta se encontre isolada.
28+
29+
• Estrutura robusta que permite aceder aos ficheiros-alvo sem ter que os carregar diretamente para
30+
a memória (especialmente útil para ficheiros de maiores dimensões).
31+
32+
• Programação defensiva que permite que o programa não falhe quando um dos ficheiros referidos não
33+
é encontrado. Inclui também prevenção de repetição de procura em ficheiros, descartando ficheiros
34+
repetidos que possam ter sido introduzidos pelo utilizador.
2535

2636
• Leitura de nomes dos ficheiros-alvo através de stdin ou como argumento.
2737

38+
• Processamento seguro do sinal SIGINT: ao ser recebido o sinal SIGINT, é necessária confirmação
39+
para que o programa termine o processamento, de modo a evitar acidentes. Ao ser recebida
40+
confirmação, a paragem de processamento é efectuada de maneira segura e não abrupta, assegurando-se
41+
que todos os dados recolhidos até ao momento são corretamente apresentados e possivelmente
42+
guardados (opção "-f").
43+
44+
• Código detalhadamente documentado.
45+
46+
[hpgrepwc.py]:
47+
48+
• Utilização: hpgrepwc <ficheiro>
49+
50+
• Realçamento de dados considerados mais importantes a verde, há excepção de quando o total de
51+
bytes processado não corresponde a 100% (neste caso a percentagem do total de bytes processado
52+
apresenta-se realçada a vermelho).
53+
2854
• Código detalhadamente documentado.

v2/hpgrepwc.py

+97-18
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@
3535
def main(argv):
3636

3737
try:
38-
# Obter nome do ficheiro
38+
# Obter nome do ficheiro.
3939
file = argv[0]
4040

4141
except:
42-
# Mensagem de ajuda caso o comando seja malformado
42+
# Mensagem de ajuda caso o comando seja mal formado.
4343
print("Utilização: hpgrepwc <ficheiro>")
4444
sys.exit(2)
4545

@@ -48,25 +48,37 @@ def main(argv):
4848
with open(file, "rb") as f:
4949
data = pickle.load(f)
5050

51-
except FileNotFoundError as e:
52-
print(f"Ficheiro '{file}' não encontrado. Verifique o seu input.")
51+
except:
52+
print(f"Ficheiro '{file}' não encontrado ou inválido. Verifique o seu input.")
5353
sys.exit(2)
5454

55-
### Leitura dos dados e envio para stdout
55+
### Leitura dos dados e envio para stdout.
5656

5757
output = []
5858

59+
# Obtenção de dados a partir do tuplo de dados no ficheiro binário.
5960
startDateStamp = data[START_DATE_STAMP]
6061
duration = data[DURATION]
6162
processData = data[PROCESS_DATA]
6263
opts = data[OPTS]
6364
word = data[WORD]
6465
haltValue = data[HALT_VALUE]
6566

67+
68+
# Adicionar campos ao output.
6669
output.append(f"\nPalavra a pesquisar: {colorWrite(word, 'red')}")
6770
output.append(f"Início da execução: {colorWrite(dt.strftime(startDateStamp, '%d/%m/%Y, %H:%M:%S.%f'), 'green')}")
6871
output.append(f"Duração da execução: {colorWrite(timedelta(seconds = duration), 'green')}")
6972

73+
74+
75+
# Organização de dados: passagem e um dicionário partilhado de estrutura
76+
# orientada a processos, para um dicionário bi-dimensional local de estrutura
77+
# organizada por PROCESSOS -> FICHEIROS, em que a chave de primeira dimensão
78+
# é referente ao processo e de segunda dimensão é referente ao ficheiro.
79+
# Esta estrutura mais organizada permite-nos saber com quanto de cada ficheiro
80+
# é que cada processo lidou.
81+
7082
sortedProcessData = dict()
7183

7284
for process in processData:
@@ -77,54 +89,82 @@ def main(argv):
7789
sortedProcessData[process][loadData[LOAD].getFile()] = []
7890
sortedProcessData[process][loadData[LOAD].getFile()].append(loadData)
7991

92+
# Obtenção dos vários nomes de ficheiros numa lista de valores únicos.
8093
files = set([processedFile for processedFile in getNested(sortedProcessData, process) for process in sortedProcessData])
94+
95+
# Transformação de todos esses nomes na sua respetiva versão colorida.
8196
files = [colorWrite(argFile, 'green') for argFile in files]
8297

98+
# Adicionar campos ao output.
8399
output.append(f"Ficheiros em argumento: " + ",\n ".join(files))
84100

101+
# Inicialização de variáveis importantes ao resto do processo.
85102
fileSizes = {}
86103
totalProcessed = 0
87104
totalLC = 0
88105
totalWC = 0
89106

107+
90108
for process in sortedProcessData:
109+
91110
output.append(f"\nProcesso: {colorWrite(process, 'green')}")
111+
112+
# Uso da função getNested para aceder à primeira dimensão do dicionário sortedProcessData
113+
# e obter os nomes dos vários ficheiros com os quais o processo lidou.
92114
files = getNested(sortedProcessData, process)
93115

116+
94117
for file in files:
118+
# Uso da função getNested para aceder à segunda dimensão do dicionário sortedProcessData
119+
# e obter informação sobre o processamento do atual ficheiro por parte do atual processo.
95120
fileData = getNested(sortedProcessData, process, file)
96121

97122
output.append(f" Ficheiro: {colorWrite(file, 'green')}")
123+
124+
# Cálculo do tempo total demorado para o processo lidar com o ficheiro atual.
98125
timeSum = sum([loadData[TIME_TAKEN] for loadData in fileData])
126+
99127
fileSize = fileData[0][SIZE]
128+
129+
# Cálculo do total de bytes do ficheiro com o qual o processo atual lidou
130+
# e cálculo da respetiva percentagem relativamente ao tamanho total do ficheiro
131+
# em questão.
100132
searchSum = sum([loadData[LOAD].getBytesToHandle() for loadData in fileData])
101133
searchPercentage = (str(round((searchSum/fileSize)*100, 1)) + "%").replace(".0", "")
102134

135+
136+
# Incrementação do total de bytes analisado.
137+
totalProcessed += searchSum
138+
139+
# Organização de dados: dicionário utilizado para registar o tamanho de cada
140+
# ficheiro referenciado.
103141
if file not in fileSizes:
104142
fileSizes[file] = fileSize
105-
totalProcessed += searchSum
143+
106144

107145
allLines = []
108146
fileWC = 0
109147

148+
149+
# Criação de lista que inclui os números de todas as linhas com ocorrências
150+
# (útil para saber o total de linhas com ocorrências encontradas neste ficheiro).
110151
for loadData in fileData:
111152
for match in loadData[LOAD_MATCHES]:
112153
allLines.append(match.getLineNumber())
113154
fileWC += match.getAmount()
114-
115-
116155
fileLC = len(set(allLines))
117156

157+
158+
# Incrementação do total de bytes analisado.
118159
totalWC += fileWC
119160
totalLC += fileLC
120-
121161

122-
123-
# print(getNested(sortedProcessData, process, file))
162+
# Adicionar campos ao output.
124163
output.append(f" Tempo de pesquisa: {colorWrite(timedelta(seconds= timeSum), 'green')}")
125164
output.append(f" Dimensão do ficheiro: {colorWrite(fileSize, 'green')} bytes")
126165
output.append(f" Dimensão processada: {colorWrite(searchSum, 'green')} bytes ({colorWrite(searchPercentage, 'green')})")
127166

167+
# Imprimir output consoante a presença de opções nos argumentos do utilizador.
128168
if any("-c" in opt for opt in opts):
129169
output.append(f" Total de ocorrências: {colorWrite(fileWC, 'green')}")
130170

@@ -133,19 +173,27 @@ def main(argv):
133173

134174
output.append("")
135175

176+
177+
# Cálculo da soma do tamanho do agregado de ficheiros e respetiva percentagem
178+
# relativamente ao total de bytes analisado.
136179
totalSize = sum([fileSizes[file] for file in fileSizes])
137180
totalPercentage = str(round((totalProcessed/totalSize)*100, 1)).replace(".0", "")
138181
totalPercentageString = colorWrite(str(totalPercentage) + "%", 'green') if totalPercentage == "100" else colorWrite(str(totalPercentage) +"%", 'red')
139182

183+
184+
# Imprimir output consoante a presença de opções nos argumentos do utilizador.
140185
if any("-c" in opt for opt in opts):
141186
output.append(f"Total de ocorrências: {colorWrite(totalWC, 'green')}")
142187

143188
if any("-l" in opt for opt in opts):
144189
output.append(f"Total de linhas com ocorrências: {colorWrite(totalLC, 'green')}")
145190

191+
192+
# Adicionar campos ao output.
146193
output.append(f"Total de bytes: {colorWrite(totalSize, 'green')}")
147194
output.append(f"Total de bytes processado: {colorWrite(totalProcessed, 'green')} ({totalPercentageString})")
148195

196+
# Imprimir a flag "[PARAGEM FORÇADA]" caso o utilizador tenha forçado a paragem via sinal SIGINT.
149197
if haltValue == 2:
150198
output.append(colorWrite("[PARAGEM FORÇADA]", "red"))
151199

@@ -155,9 +203,13 @@ def main(argv):
155203
print(line)
156204

157205

158-
159-
160206
def colorWrite(text, color):
207+
"""
208+
Devolve o texto recebido na cor especificada.
209+
Requires: text é um string e color é 'green' ou 'red'.
210+
Ensures: text rodeado pelos códigos de cor referentes à
211+
cor especificada.
212+
"""
161213
if color == "green":
162214
return GREEN_START + str(text) + COLOR_END
163215

@@ -166,6 +218,10 @@ def colorWrite(text, color):
166218

167219

168220
def getNested(data, *args):
221+
"""
222+
Permite aceder a dicionários ninhados (várias dimensões).
223+
Requires: data é um dicionário e args são chaves dos dicionários internos.
224+
"""
169225
if args and data:
170226
element = args[0]
171227
if element:
@@ -176,12 +232,11 @@ def getNested(data, *args):
176232
### CLASSES (O enunciado explicitamente limita a existência de ficheiros ".py"
177233
# a um máximo de 2. Desta forma, incluímos as classes necessárias ao funcionamento
178234
# do programa no ficheiro pgrepwc e no ficheiro hpgrepwc separadamente para que estes
179-
# possam funcionar indepentendemente um do outro.)
180-
235+
# possam funcionar indepentendemente um do outro).
181236

182237
class Load:
183238
"""
184-
TODO: Comentar
239+
Alberga dados sobre carga referente a um processo.
185240
"""
186241
def __init__(self, file, offset, bytesToHandle):
187242
self._file = file
@@ -190,20 +245,32 @@ def __init__(self, file, offset, bytesToHandle):
190245
self._end = offset + bytesToHandle - 1
191246

192247
def getFile(self):
248+
"""
249+
Obtém o ficheiro onde vai correr a pesquisa.
250+
"""
193251
return self._file
194252

195253
def getOffset(self):
254+
"""
255+
Obtém a posição inicial onde vai começar a ser corrida a pesquisa.
256+
"""
196257
return self._offset
197258

198259
def getBytesToHandle(self):
260+
"""
261+
Obtém o número de bytes a pesquisar.
262+
"""
199263
return self._bytesToHandle
200264

201265
def getEnd(self):
266+
"""
267+
Obtém a posição de fim da execução.
268+
"""
202269
return self._end
203270

204271
class Match:
205272
"""
206-
TODO: Comentar
273+
Alberga dados sobre uma linha que contenha ocorrências de uma palavra.
207274
"""
208275
def __init__(self, file, lineNumber, lineContent, amount):
209276
self._lineNumber = lineNumber
@@ -212,17 +279,29 @@ def __init__(self, file, lineNumber, lineContent, amount):
212279
self._amount = amount
213280

214281
def getLineNumber(self):
282+
"""
283+
Obtém o número da linha onde a(s) ocorrência(s) foi/foram encontrada(s).
284+
"""
215285
return self._lineNumber
216286

217287
def getLineContent(self):
288+
"""
289+
Obtém o conteúdo correspondente à linha onde a(s) ocorrência(s) foi/foram encontrada(s).
290+
"""
218291
return self._lineContent
219292

220293
def getFile(self):
294+
"""
295+
Obtém o ficheiro onde a(s) ocorrência(s) foi/foram encontrada(s).
296+
"""
221297
return self._file
222298

223299
def getAmount(self):
300+
"""
301+
Obtém o número de ocorrência(s) que se repete(m) ao longo da linha.
302+
"""
224303
return self._amount
225304

226-
305+
# Invocação de main
227306
if __name__ == "__main__":
228307
main(sys.argv[1:])

0 commit comments

Comments
 (0)