Utilisateur:PamputtBot/parse dump.cpp

Définition, traduction, prononciation, anagramme et synonyme sur le dictionnaire libre Wiktionnaire.

Ce code c++ lit ligne par ligne le dump d’historique complet du Wiktionnaire que l’on a précédemment récupéré ici et décompressé dans le dossier de parse_dump.cpp.

On compile le programme avec la commande suivante

  • g++ parse_dump.cpp -std=c++17 -o parse_dump

puis on l’exécute en tapant « ./parse_dump frwiktionary-latest-pages-articles.xml ».

Ce programme crée autant de fichier de sortie au format texte qu’il y a d’analyse. Le contenu de chaque fichier peut ensuite être copié dans différentes sous-pages de Wiktionnaire:Maintenance et nettoyage.

/*
  g++ parse_dump.cpp -std=c++17 -o parse_dump
  ./parse_dump /chemin/vers/frwiktionary-latest-pages-articles.xml
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <map>
#include <string>
#include <algorithm> 
#include <cctype>
#include <locale>
 
using namespace std;

string getCodeLangue(string line) {
  size_t pos1 = line.find("{{langue|")+9;
  size_t pos2 = line.find("}}", pos1);
  return line.substr(pos1,pos2-pos1);
}

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    rtrim(s);
    ltrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

 
int main(int argc, char **argv) {

  if(argc == 1 || argc>2) {
    cout << argv[0] << " /chemin/vers/frwiktionary-latest-pages-articles.xml" << endl;
    return 0;
  }
  string nomFichier = argv[1];
  
  //https://dumps.wikimedia.org/frwiktionary/latest/frwiktionary-latest-pages-articles.xml.bz2
  ifstream infile(nomFichier.c_str(),ifstream::in);
  if(!infile) {
    cout << "Le fichier " << nomFichier << " n'existe pas" << endl;
    return 0;
  }

  string nomFichierSortie = "mot_feminin_sans_exemple.txt";
  ofstream outMotsFemininsSansExemple(nomFichierSortie.c_str(), ofstream::out);
  if(!outMotsFemininsSansExemple) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "definition_masculin_ou_feminin.txt";
  ofstream outDefinitionMasculinOuFeminin(nomFichierSortie.c_str(), ofstream::out);
  if(!outDefinitionMasculinOuFeminin) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "genre_et_equi-pour_pas_coherent.txt";
  ofstream outGenreEquivpourPasCoherent(nomFichierSortie.c_str(), ofstream::out);
  if(!outGenreEquivpourPasCoherent) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "trad_hors_section_en_francais.txt";
  ofstream outTradHorsSectionFr(nomFichierSortie.c_str(), ofstream::out);
  if(!outTradHorsSectionFr) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "noms_sans_genre.txt";
  ofstream outNomsSansGenre(nomFichierSortie.c_str(), ofstream::out);
  if(!outNomsSansGenre) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "entrees_sans_aucune_definition.txt";
  ofstream outSansDefinition(nomFichierSortie.c_str(), ofstream::out);
  if(!outSansDefinition) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "debut_incorrect.txt";
  ofstream outDebutIncorrect(nomFichierSortie.c_str(), ofstream::out);
  if(!outDebutIncorrect) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "code_langue_incoherent_etyl.txt";
  ofstream outEtylIncoherent(nomFichierSortie.c_str(), ofstream::out);
  if(!outEtylIncoherent) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "dicoado_mauvaise_section.txt";
  ofstream outDicoado(nomFichierSortie.c_str(), ofstream::out);
  if(!outDicoado) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  nomFichierSortie = "section_vide.txt";
  ofstream outSectionVide(nomFichierSortie.c_str(), ofstream::out);
  if(!outSectionVide) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
 
  std::string titre;
  std::vector<std::string> codesLangue;
  std::unordered_set<std::string> tousLesMots;
  std::vector<std::pair<std::string, std::string>> valeursEquivPour;
  std::unordered_map<std::string, std::pair<std::string, std::string>> etylLanguesMap;

  std::string ns;
 
  string line;
  size_t pos1, pos2;
  unsigned int cmpt = 0;

  while(getline(infile,line)) {
    if(line.find("<page>")!=string::npos) {
      titre = "";
      codesLangue.clear();
      ns = "";
      bool is_redirect = false;
      bool balise_text = false;
      bool is_debut_incorrect = false;
      bool is_first_line = false;
      bool is_nom = false;
      bool f_and_equivpour = false;
      bool m_and_equivpour = false;
      bool equiv_pour_et_genre_pas_coherent = false;
      bool has_example = false;
      bool is_fr = false;
      bool is_habitante = false;
      bool is_masculin_or_feminin = false;
      bool trad_pas_fr = false;
      bool pas_de_genre = false;
      std::unordered_map <string, bool> juste_ebauche_def;
      bool is_fr_reg_x = false;
      bool is_etyl_incoherent = false;
      bool dicoado_mauvaise_section = false;
      std::vector <std::string> sections_vides;
      std::vector <std::string> sections_vides_langue;
      std::string nom_section;
      std::string contenu_section;
      std::string langue;
      
      while(getline(infile,line)) {
	if(line.find("<title>")!=string::npos) {
	  // on extrait le titre des balises <title>
	  pos1  = line.find("<title>");
	  pos2  = line.find("</title>");
	  titre = line.substr(pos1+7,pos2-pos1-7);
	  tousLesMots.insert(titre);
	} 

	if(line.find("<ns>")!=string::npos) {
	  //on vérifie que l'espace de nom est bien « 0 »
	  pos1 = line.find("<ns>");
	  pos2 = line.find("</ns>");
	  ns   = line.substr(pos1+4,pos2-pos1-4);
	}

	if(line.find("<text")!=string::npos) {
	   balise_text = true;
	   is_first_line = true;
	}

	if((line.find("#REDIRECT")!=string::npos ||
	   line.find("#redirect")!=string::npos ||
	   line.find("#Redirect")!=string::npos) &&
	   balise_text)
	  is_redirect = true;

	if(is_first_line) {
	  // <text bytes="2157" xml:space="preserve">WIKICODE
	  size_t pos = line.find(">");
	  if(line.find("=={") > pos+3 && // pour =={{langue|
	     line.find("== ") > pos+3 && // pour == {{langue| 
	     line.find("{{v") > pos+3 && // pour {{voir|x|y|z}})
	     line.find("{{t") > pos+3 && // pour {{titre incorrect|xxxxx}} ou {{tire mis en forme|xxxxx}})
	     line.find("{{s") > pos+3 && // pour {{supprimer ?}} ou {{PàS}}
	     line.find("{{P") > pos+3 && // pour {{PàS}}
	     line.find("{{à") > pos+3 && // pour {{à supprimer}}
	     line.find("{{f") > pos+3 && // pour {{formater}}
	     line.find("{{é") > pos+3 && // pour {{ébauche}}
	     line.find("{{a") > pos+3 && // pour {{alphabet
	     line.back() != '>' // pour une ligne vide
	     ) {
	    is_debut_incorrect = true;
	  }
	}
	
	if(line.find("{{langue|")!=string::npos &&
	   line.find("==")!=string::npos &&
	   balise_text) {
	  langue=getCodeLangue(line);
	  if (langue == "fr")
	    is_fr = true;
	  else
	    is_fr = false;

	  juste_ebauche_def[langue] = true;
	}

	
	// le dico des ados
	if(!is_fr &&
	   (line.find("{{Le Dico des Ados") != string::npos ||
	    line.find("{{Dicoado") != string::npos)) 
	  dicoado_mauvaise_section = true;

	// is_nom
	if(line.find("===")!=string::npos &&
	   balise_text)
	  if(line.find("{{S|nom|")!=string::npos)
	    is_nom = true;
	  else
	    is_nom = false;

	
	// section vide ?
	if(line.find("===")!=string::npos &&
	   balise_text) {
	  if(line.find("{{S|")!=string::npos) {
	    bool is_pron=false;
	    bool is_ref=false;
	    bool is_trad=false;
	    std::string section_prec = nom_section;
	    // on vérifie d'abord le contenu de la section précédente
	    if(trim_copy(contenu_section).empty()) {
	      if(nom_section == "prononciation")
		is_pron = true;
	      if(nom_section == "références")
		is_ref=true;
	      if(nom_section == "traductions")
		is_trad=true;

	      if(!is_pron &&
		 !is_ref &&
		 !is_trad) {
		sections_vides.push_back(nom_section);
		sections_vides_langue.push_back(langue);
	      }
	    }
	    
	    // on réinitialise contenu_section
	    contenu_section = "";

	    // puis, on extrait le nom de la nouvelle section
	    pos1 = line.find("{{S|") + 4;
	    pos2 = line.find("|", pos1+1);
	    if(pos2 == string::npos)
	      pos2 = line.find("}}", pos1+1);
	    nom_section = line.substr(pos1, pos2-pos1);


	    if(is_pron &&
	       nom_section != "paronymes" &&
	       nom_section != "homophones") {
		sections_vides.push_back(section_prec);
		sections_vides_langue.push_back(langue);
	      }
	    
	    if(is_ref &&
	       nom_section != "sources" &&
	       nom_section != "bibliographie") {
		sections_vides.push_back(section_prec);
		sections_vides_langue.push_back(langue);
	      }
	    
	    if(is_trad &&
	       nom_section != "traductions à trier") {
		sections_vides.push_back(section_prec);
		sections_vides_langue.push_back(langue);
	      }
	      
	  }
	} else // ce n'est pas une section de titre
	  contenu_section += line;
	
	// trad_pas_fr
	if(line.find("====")!=string::npos &&
	   balise_text &&
	   !is_fr)
	  if(line.find("{{S|trad")!=string::npos)
	    trad_pas_fr = true;

	// nom sans genre sur la ligne de forme
	const string bold_title = "'''" + titre + "'''";
	if(line.find(bold_title) != string::npos && // titre en gras
	   line.find("{{pron|") != string::npos && // le modèle « pron » est normalement présent sur la ligne de forme
	   line.find("#") == string::npos && // pas un exemple
	   line.find(":") == string::npos && // pas une note
	   line.find("*") == string::npos && // pas une note
	   line.find(";") == string::npos && // pas une note
	   line.find("{{note}}") == string::npos && // pas une note
	   // line.find("|") == string::npos && // pas un exemple sur plusieurs ligne
	   line.find("/") == string::npos && // pas un exemple sur plusieurs ligne
	   line.find("&gt;") == string::npos && // pas un exemple sur plusieurs ligne
	   line.find("[[") == string::npos && // pas une image
	   line.find("]]") == string::npos && // pas une image
	   is_nom &&
	   is_fr) {
	  if(line.find("{{f}}") == string::npos &&
	     line.find("{{m}}") == string::npos &&
	     line.find("{{mf") == string::npos &&
	     line.find("{{fm") == string::npos &&
	     line.find("{{genre") == string::npos)
	    // cout << line << endl;
	    pas_de_genre = true;
	}

	
	size_t pos_equiv_pour = line.find("{{équiv-pour");
	if(pos_equiv_pour != string::npos) {
	  // féminin
	  size_t pos_f = line.find("{{f}}");
	  if(pos_f != string::npos) {
	    f_and_equivpour = true;

	    if (line.find("|une ") != string::npos &&
		line.find("une personne") == string::npos &&
		line.find("une entité") == string::npos &&
		pos_f < pos_equiv_pour) {
	      // std::cout << titre << " -> " << line << std::endl;
	      equiv_pour_et_genre_pas_coherent = true;
	    }
	  }

	  // masculin
	  size_t pos_m = line.find("{{m}}");
	  if(pos_m != string::npos) {
	    m_and_equivpour = true;

	    if (line.find("|un ") != string::npos &&
		line.find("une personne") == string::npos &&
		line.find("une entité") == string::npos &&
		pos_m < pos_equiv_pour)
	      equiv_pour_et_genre_pas_coherent = true;
	  }

	  if(is_fr) {
	    // on extrait le(s) mot(s) contenu(s) dans le modèle « équiv-pour »
	    size_t fin_equiv_pour = line.find("}}", pos_equiv_pour);
	    stringstream ss(line.substr(pos_equiv_pour, fin_equiv_pour-pos_equiv_pour));
	    vector<string> tokens;
	    string mot;
	    unsigned cmpt=0;
	    while(getline(ss, mot, '|')) {
	      if(mot.find("=") != string::npos) continue;
	      if(cmpt++<2) continue;

	      if(mot.empty()) {
		//cout << titre << " -> " << line << endl;
		continue;
	      }
	      
	      valeursEquivPour.push_back(std::make_pair(mot, titre));
	    }
	  }
	}

	if((line.find("{{exemple") != string::npos ||
	    line.find("#*") != string::npos) &&
	   line.find("{{ébauche-exe") == string::npos &&
	   is_fr)
	  has_example = true;

	if(line.find("#") != string::npos &&
	   (line.find("abitante") != string::npos ||
	    line.find("abitant]]e") != string::npos) &&
	   is_fr)
	  is_habitante = true;

	if(line.find("#") != string::npos &&
	   is_nom &&
	   (line.find("''Masculin") != string::npos ||
	    line.find("''Fémini") != string::npos) &&
	   is_fr)
	  is_masculin_or_feminin = true;

	// Codes langue incohérents dans étyl
	size_t pos0 = 0;
	while(line.find("{{étyl|", pos0+1) != string::npos) {
	  // {{étyl|la|fr|-atio|sens=[[-ation]]}}.
	  pos0 = line.find("{{étyl|", pos0+1); // {{étyl|
	  size_t pos1 = line.find("|", pos0+8); // la|
	  size_t pos2a = line.find("|", pos1+1); // fr|
	  size_t pos2b = line.find("}}", pos1+1); // fr|
	  size_t pos2 = std::min(pos2a, pos2b);

	  std::string lang1 = line.substr(pos0+8, pos1-pos0-8);
	  std::string lang2 = line.substr(pos1+1, pos2-pos1-1);
	  // std::cout << line << ", lang1: " << lang1 << ", lang2: " << lang2 << std::endl;

	  if(lang2 != langue
	     && balise_text
	     && ns=="0"
	     && !is_redirect 
	     && titre.find("Titres non pris en charge") == string::npos) {
	    if(titre == "avicultritz")
	      std::cout << titre << " <-> " << line << "(pos1,pos2)=(" << pos1 << "," << pos2 << ")" << std::endl;
	    etylLanguesMap[titre] = std::make_pair(langue, lang2);
	    // is_etyl_incoherent = true;
	  }
	}


	// ne contient que des « ébauche-déf »
	if(line.find("#") != string::npos &&
	   line.find("#*") == string::npos) {
	  if (line.find("{{ébauche-déf") != string::npos)
	    juste_ebauche_def[langue] &= true;
	  else
	    juste_ebauche_def[langue] &= false;	  
	}

	
	is_first_line = false;
	
	if(line.find("</page>")!=string::npos)
	  break;
      }


      ////////////////////////////////////////////////////////
      //
      // On remplit les fichiers de résultats
      // 
      ////////////////////////////////////////////////////////
      
      // début de page incorrect
      if(ns=="0" &&
	 !is_redirect &&
	 is_debut_incorrect)
	outDebutIncorrect << "# [[" << titre << "]]" << endl;

	 
      // noms féminins sans exemple
      if(ns=="0" &&
	 !is_redirect &&
	 titre.find("Titres non pris en charge") == string::npos
	 && f_and_equivpour
	 && !has_example
	 && !is_habitante
	 && is_fr) {

	outMotsFemininsSansExemple << "# [[" << titre << "]]" << endl;
	// cmpt++;
	// if(cmpt%1000 == 0)
	//   cout << cmpt << "\t" << titre << endl;
      }

      // définition commençant par Masculin ou Féminin
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos
	 && is_masculin_or_feminin) {

	outDefinitionMasculinOuFeminin << "# [[" << titre << "]]" << endl;
	// cmpt++;
	// if(cmpt%1000 == 0)
	//   cout << cmpt << "\t" << titre << endl;
      }

      // genre et le contenu d'équiv-pour pas cohérents
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos
	 && equiv_pour_et_genre_pas_coherent) {

	outGenreEquivpourPasCoherent << "# [[" << titre << "]]" << endl;
	// cmpt++;
	// if(cmpt%1000 == 0)
	//   cout << cmpt << "\t" << titre << endl;
      }

      // genre et le contenu d'équiv-pour pas cohérents
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos
	 && trad_pas_fr) {

	outTradHorsSectionFr << "# [[" << titre << "]]" << endl;
	// cmpt++;
	// if(cmpt%1000 == 0)
	//   cout << cmpt << "\t" << titre << endl;
      }

      // genre et le contenu d'équiv-pour pas cohérents
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos
	 && pas_de_genre) {

	outNomsSansGenre << "# [[" << titre << "]]" << endl;
	// cmpt++;
	// if(cmpt%1000 == 0)
	//   cout << cmpt << "\t" << titre << endl;
      }
      
      // ne contient que des « ébauche-déf »
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos) {

	for(const auto& [langue, sans_def] : juste_ebauche_def) {
	  if(langue == "ar"
	     || langue == "ja"
	     || langue == "ko"
	     || langue == "ko-Hani"
	     || langue == "vi"
	     || langue == "vi-chunom"
	     || langue == "zh")
	    continue;

	  if(sans_def) {
	    outSansDefinition << "# [[" << titre << "]] (" << langue << ")" << endl;
	
	
	    // cmpt++;
	    // if(cmpt%1000 == 0)
	    //   cout << cmpt << "\t" << titre << " (" << langue << ")" << endl;
	  }
	  
	}
      } // end ebauche_def

      // dicoado
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos
	 && dicoado_mauvaise_section)
	outDicoado << "# [[" << titre << "]]" << endl;


      // sections vides
      if(ns=="0"
	 && !is_redirect 
	 && titre.find("Titres non pris en charge") == string::npos) {
	int cmpt=0;
	for(int i=0 ; i<sections_vides.size() ; ++i) {
	  if(cmpt==0)
	    outSectionVide << "# [[" << titre << "]] (" << sections_vides_langue.at(i) << "), " << sections_vides.at(i);
	  else
	    outSectionVide << ", " << sections_vides.at(i);

	  cmpt++;
	}
	if(!sections_vides.empty())
	  outSectionVide << std::endl;
      }
      
    }
    // if(cmpt==10) break;
  }
  infile.close();
  outMotsFemininsSansExemple.close();
  outDefinitionMasculinOuFeminin.close();
  outGenreEquivpourPasCoherent.close();
  outTradHorsSectionFr.close();
  outNomsSansGenre.close();
  outSansDefinition.close();
  outDebutIncorrect.close();
  outDicoado.close();
  outSectionVide.close();

  // on vérifie si les mots contenus dans les modèles « équiv-pour »
  // existent déjà comme entrée
  nomFichierSortie = "equi_pour_existe_pas_encore.txt";
  ofstream outEquivpourExistePasEncore(nomFichierSortie.c_str(), ofstream::out);
  if(!outEquivpourExistePasEncore) {
    cout << "Probleme avec le fichier de sortie " << nomFichierSortie << endl;
    return 0;
  }
  
  for(const auto& valeur : valeursEquivPour)
    if (tousLesMots.find(valeur.first) == tousLesMots.end())
      outEquivpourExistePasEncore << "# [[" << valeur.first << "]] ([[" << valeur.second << "]])" << endl;
  
  outEquivpourExistePasEncore.close();

  
  // codes langues incohérents dans étyl
  for (const auto& [titre, langues] : etylLanguesMap) {
    outEtylIncoherent << "# [[" << titre << "]] (" << langues.first << "!=" << langues.second << ")" << endl;
  }
  outEtylIncoherent.close();
 
  cout << cmpt << " articles trouves" << endl;
  return 1;
}