-
Notifications
You must be signed in to change notification settings - Fork 2
/
shorten_french_names.py
339 lines (267 loc) · 11.3 KB
/
shorten_french_names.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# ------------------------------------------------------------------------------
# shorten_french_names.py - Benjamin Chartier
# licence MIT (cf. license.txt à la racine du dépôt)
# Ce programme crée les noms contractés des communes françaises
# Testé avec Python 3
# Paramètres en entrée du script :
# - chemin du fichier csv contenant les noms de communes françaises
# - chemin du fichier de sortie
# ------------------------------------------------------------------------------
# sys est utilisé pour importer des modules et sortir de l'exécution du script
import sys
# getopt est utilisé pour interpréter les paramètres de la ligne de commande
import getopt
# os est utilisé pour l'accès au système de fichier
import os
# string est utilisé pour la manipulation de chaînes de caractères
import string
import copy
import random
import codecs
# Fonction découpant une chaîne en plusieurs parties
# Les séparateurs à utiliser sont présents dans une liste
# Grosso modo, cette fonction ne fait qu'appeler la fonction
# divide_string_with_one_sep de manière itérative avec un séparateur
# différent
def divide_string_with_mutiple_seps(s, seps):
parts = [s]
temp_parts = []
for sep in seps:
for part in parts:
temp_parts.extend( divide_string_with_one_sep(part, sep) )
parts = copy.copy(temp_parts)
temp_parts = []
return parts
# Fonction découpant une chaîne en plusieurs parties en utilisant
# le séparateur fourni en paramètre
def divide_string_with_one_sep(s, sep):
parts = []
if s == None or sep == '' or sep == None:
return parts.append(s)
before_sep = None
after_sep = s
while len(after_sep) > 0:
before_sep, sep, after_sep = after_sep.partition(sep)
if len(before_sep) > 0:
parts.append(before_sep)
if len(sep) > 0:
parts.append(sep)
return parts
# ------------------------------------------------------------------------------
# Classe gérant le traitement d'un fichier csv
class NameShortener:
# Description
"""Classe gérant le traitement du fichier csv"""
# Fichiers
_input_file = None
_output_file = None
# Paramètres de lecture du fichier CSV
# Séparateur utilisé pour séparer chaque champ dans le fichier CSV
# Par défaut c'est la virgule. Plus bas, le code essaye de détecter si
# c'est un ";"
_csv_sep = ","
# Séparateur utilisé pour le fichier en sortie
_out_sep = ","
# Numéro d'ordre du champ contenant le nom de la commune
_complete_name_field_num = 7
_code_insee_field_num = 6
_pop_field_num = 10
# Résultat
_outputLines = []
# --------------------------------------------------------------------
# Constructeur
def __init__(self, input_file, output_file):
# Initialisation de la liste des étapes
self._input_file = input_file
self._output_file = output_file
# --------------------------------------------------------------------
# Exécution du processus traitant l'intégralité d'un fichier csv en entrée
def run(self):
new_lines = []
# Lecture et traitement du fichier en entrée
input_file_object = open(self._input_file)
try:
num_line = 0
for line in input_file_object:
# Est-ce que le séparateur ne devrait pas plutôt être un point-virgule ?
# On regarde combien de virgules et de points-virgules sont présents dans
# la première ligne du fichier en entrée. On utilsie le séparateur présent
# le plus grand nombre de fois
if num_line == 0:
if line.count(",") < line.count(";"):
self._csv_sep = ";"
new_lines.append(self._out_sep.join(["code_insee", "pop", "nom", "nom_court", "nom_tres_court"]) + "\n")
if num_line > 0:
fields = line.split(self._csv_sep)
original_name = fields[self._complete_name_field_num]
pop = "".join(fields[self._pop_field_num].split())
code_insee = fields[self._code_insee_field_num]
# Cas particulier des communes des DOM pour lesquelles on retire le troisième caractère
# du code INSEE
if len(code_insee) == 6:
code_insee = code_insee[:3] + code_insee[4:]
# Calcul du nom court et du nom très court à partir du nom complet
(complete_name, short_name, very_short_name) = self.processLine(num_line, original_name)
# On ajoute un nouvel enregistrement dans le tableau qui servira à écrire le fichier en sortie
# Chaque enregistrement comprend le code INSEE, la population, le nom complet,
# le nom un peu plus court, et le nom très court
new_lines.append(self._out_sep.join([code_insee.rjust(5, "0"), pop, complete_name, short_name, very_short_name]) + "\n")
# Les lignes qui suivent servent juste à afficher quelques enregistrement pour
# contrôler visuellement le résultat sur un échantillon
# if len(name_parts) > 4 and num_line % 3 == 0:
# if len(name_parts) > 2 and num_line < 50:
# if "Saint" in complete_name:
# if "Arrondissement" in complete_name:
# if "L'" in complete_name:
# if "(" in original_complete_name:
# if "Vieille" in complete_name:
# if "Notre" in original_complete_name:
# if len(name_parts) > 1 and "y" in original_complete_name:
if random.randint(1, 700) == 1: # Affichage d'un enregistrement au hasard sur 700
print(complete_name.ljust(38), short_name.ljust(23), very_short_name.ljust(15))
num_line += 1
finally:
input_file_object.close()
# Ecriture du fichier en sortie
output_file_object = codecs.open(self._output_file, 'w', "utf-8")
output_file_object.writelines(new_lines)
output_file_object.close()
# --------------------------------------------------------------------
# Traitement d'une ligne : Calcul du nom court et du nom très court à partir du nom complet
def processLine(self, num_line, original_name):
short_name_parts = []
very_short_name_parts = []
# Suppression des espaces multiples
complete_name = " ".join(original_name.split())
# Suppression des espaces situés après une apostrophe
complete_name = complete_name.replace("' ", "'")
# Suppression des parenthèses
# et des espaces de début et fin
complete_name = complete_name.split("(")[0].strip()
# Découpage du nom en mots
# les mots sont des " " ou des "-"
name_parts = []
name_parts = divide_string_with_mutiple_seps(complete_name, [' ', '-'])
# Chaines de caractères à conserver dans le nom court
parts_to_keep = (" ", "-", "et", "en", "dit", "le", "la", "les", "l'", "lès", "en", "sur", "du", "de", "des", "d'", "sous", "aux", "à", "au", "aux")
parts_to_keep_2 = ("Grand", "Grande", "Grands", "Grandes", "Petit", "Petits", "Petite", "Petites", "Haute", "Hautes", "Hauts", "Haut", "Basse", "Basses", "Bas", "Vieux", "Vieille", "Vieilles", "Neuf", "Neufs", "Neuve", "Neuves","Jeune", "Jeunes", "Gros", "Grosse", "Grosses", "Beau", "Beaux", "Belle", "Belles", "Un", "Deux", "Trois", "Quatre", "Cinq", "Six", "Sept", "Treize", "Vingt", "Cent", "Mille", "Entre")
parts_to_keep_3 = ("Notre")
# Première abréviation
if "-" not in name_parts and "Arrondissement" not in name_parts:
short_name_parts = copy.copy(name_parts)
else:
# Indicateurs utilisés pour orienter le traitement des mots suivants
seen_first_dash = False
seen_second_dash = False
begin_with_saint = False
keep_next_word = False
# Traitement de chaque mot du nom original
for i in range(len(name_parts)):
part = name_parts[i]
if part.lower() == "saint":
short_name_parts.append("St")
if i == 0:
begin_with_saint = True
elif part.lower() == "sainte":
short_name_parts.append("Ste")
if i == 0:
begin_with_saint = True
elif part.lower() == "saintes":
short_name_parts.append("Stes")
if i == 0:
begin_with_saint = True
elif part.lower() == "saints":
short_name_parts.append("Sts")
if i == 0:
begin_with_saint = True
elif part.lower() == "arrondissement":
short_name_parts.append("arr.")
elif i==0 :
short_name_parts.append(part)
elif part in parts_to_keep:
short_name_parts.append(part)
elif part.upper().lower() == part:
short_name_parts.append(part)
elif part in parts_to_keep_3:
short_name_parts.append(part)
elif keep_next_word:
short_name_parts.append(part)
keep_next_word = False
elif part.upper().lower()[0:2] in ("l'", "d'"):
short_name_parts.append("{0}.".format(part[0:3]))
elif seen_first_dash and not begin_with_saint:
short_name_parts.append("{0}.".format(part[0]))
elif seen_second_dash:
short_name_parts.append("{0}.".format(part[0]))
else:
short_name_parts.append(part)
if part in parts_to_keep_2 and not seen_first_dash:
keep_next_word = True
if part in parts_to_keep_3 and not seen_first_dash:
keep_next_word = True
if part == "-":
if seen_first_dash:
seen_second_dash = True
else:
seen_first_dash = True
# Deuxième abréviation
very_short_name_parts = copy.copy(short_name_parts)
last_part_removed = True
# Traitement de chaque mot du nom court (on ne repart pas du nom complet)
for i in range(len(short_name_parts)-1, -1, -1):
part = short_name_parts[i]
if not last_part_removed:
pass
elif "." in part:
very_short_name_parts.pop()
elif part in parts_to_keep:
very_short_name_parts.pop()
elif part != " " and part.upper().lower() == part and not part[0].isdigit():
very_short_name_parts.pop()
elif part in ("St", "Sts", "Ste", "Stes"):
very_short_name_parts.pop()
elif part == "Notre":
very_short_name_parts.pop()
else:
last_part_removed = False
return (complete_name, "".join(short_name_parts), "".join(very_short_name_parts))
# ------------------------------------------------------------------------------
# entrées :
# - le répertoire des fichiers à traiter
# - le répertoire de destination des nouveaux fichiers
def main():
'''Fonction principale du script'''
root_dir = os.getcwd()
print(root_dir)
input_file_path = 'input.csv'
output_file_path = 'output.csv'
# Récupération des paramètres et des options de la ligne de commande
args = None
opts = None
try:
opts,args = getopt.getopt(sys.argv[1:], '', ['input=','output='])
print(opts)
print(args)
except getopt.GetoptError:
print("Pas bon")
sys.exit(1)
# Traitement des options
for(o,a) in opts:
if o in('--input'):
input_file_path = a
elif o in('--output'):
output_file_path = a
# Calcul des différents répertoires
root_dir = os.getcwd()
print(input_file_path)
print(output_file_path)
simplifier = NameShortener(input_file_path, output_file_path)
simplifier.run()
print('')
print("--------- Script achevé avec succès ---------")
print('')
# ------------------------------------------------------------------------------
if __name__ == '__main__':
main()