IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Décomposition d'un fichier ics en plusieurs events [Débutant]


Sujet :

C#

  1. #1
    Membre du Club
    Inscrit en
    Mars 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Mars 2009
    Messages : 7
    Par défaut Décomposition d'un fichier ics en plusieurs events
    Bonjour,
    Je cherche à décomposer un fichier ics qui contient plusieurs events en plusieurs fichiers ics contenant 1 seul event.
    J'ai besoin de cela car sinon, Outllook créé un nouveau calendrier lors de l'import du fichier.
    Je ne sais pas trop comment m'y prendre, en essayant d'optimiser en 1 seule passe, cad 1 seule lecture du fichier.
    Si quelqu'un avait un peu de temps pour m'aider ce serait sympa !
    Merci

  2. #2
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 966
    Par défaut
    Dans un fichier ICS, chaque évènement est encapsulé entre des lignes BEGIN:VEVENT et END:VEVENT.
    Pour faire plusieurs fichier contenant chacun un évènement , il suffit de repérer ces lignes de les placer dans un nouveau fichier.

    Attention, les lignes BEGIN:VCALENDAR et END:VCALENDAR sont obligatoires.
    Chaque fichier devra prendre à minima cette forme.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    BEGIN:VCALENDAR
    BEGIN:VEVENT
     
    SUMMARY;LANGUAGE=fr:<String>
    LOCATION:
    DTSTART;VALUE=DATE:<DateTime au format AnneeMoisJour"T"HeureMinuteSecond>
    DTEND;VALUE=DATE:<DateTime au format AnneeMoisJour"T"HeureMinuteSecond>
     
    END:VEVENT
    END:VCALENDAR
    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    BEGIN:VCALENDAR
    BEGIN:VEVENT
     
    SUMMARY;LANGUAGE=fr:Anniversaire de Popo
    LOCATION:
    DTSTART;VALUE=DATE:20241224T000000
    DTEND;VALUE=DATE:20241224T235900
     
    END:VEVENT
    END:VCALENDAR
    La RFC du format est disponible ici :
    https://www.ietf.org/rfc/rfc5545.txt

  3. #3
    Membre du Club
    Inscrit en
    Mars 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Mars 2009
    Messages : 7
    Par défaut
    Bonsoir, et merci pour ta réponse.
    Mais je sais tout ça, c'est la façon de m'y prendre qui me perturbe.
    J'ai fait quelque chose comme ça mais je patauge :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
            private void button1_Click(object sender, EventArgs e)
            {
                openFileDialog1.ShowDialog();   // Calendrier_FoxyMusic.ics
                if(File.Exists(openFileDialog1.FileName))
                {
                    fichierSRC = File.ReadAllLines(openFileDialog1.FileName);
                    string newEvent = "BEGIN:VEVENT";
                    string endEvent = "END:VEVENT";
                    int countEvent = 1;                     // event en cours
                    int counter = 0;                        // cpt de boucle
                    int currentLine = 0;                    // cpt de ligne en cours
                    List<int> eventPos = new List<int>();    // liste contenant la ligne de chaque début d'event
                    int i;
                    int j;
                    int k;
     
                    // lecture du nb d'events
                    for (i = 0; i < fichierSRC.Length; i++)
                    {
                        if (fichierSRC[i].Contains(newEvent))
                        {
                            eventPos.Add(i);
                            //MessageBox.Show("Event " + eventPos.Count + " : " + i);
                        }
                    }
     
                    // s'il y a plus d'un event, on créé les fichiers
                    if (eventPos.Count > 1)
                    {
                        for (i = 0; i < eventPos.Count; i++) // pour chaque event
                        {
                            var scripteur = new StreamWriter("fichier_" + i + ".txt");
                            for (j = 0; j < fichierSRC.Length; j++) // on parcoure le fichier ics
                            {
                                // entete
                                if (j < eventPos[i])    // pour l'entete
                                {
                                    scripteur.WriteLine(fichierSRC[j]);
                                }
                                // event
                                //if (fichierSRC[j].Contains(newEvent) && counter == i)
                                if (j >= eventPos[i] && j < eventPos[i+1])
                                {
                                    counter++;
                                    for (k = j; k < fichierSRC.Length; k++) // on parcoure l'event
                                    {
                                        scripteur.WriteLine(fichierSRC[k]);
                                        if (fichierSRC[k].Contains(endEvent))
                                        {
                                            counter++;
                                            break;
                                        }
                                    }
                                }
     
                            }
                            scripteur.WriteLine("END:VCALENDAR");
                            scripteur.Close();
                            //counter++;
                        }
                        //MessageBox.Show("Terminé ; " + totalEvent.ToString() + " events.");
                        MessageBox.Show("Terminé ; " + eventPos.Count + " events.");
                        this.Close();
                    }
                }
            }

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 540
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 540
    Par défaut
    Tu récupères la position des BEGIN:VEVENT => OK
    Tu créés des fichiers en incrémentant le compteur => OK
    Tu ne mets pas le BEGIN:VCALENDAR => PAS OK
    Tu devrais commencer ton indice de lecture (j) à l'indice de 1ère position d'évènement
    Tu devrais copier jusqu'à ce que tu tombes sur un END:EVENT (ton eventpos n'a pas d'info sur la fin du dernier enregistrement); un boucle while ou do..while serait je pense plus judicieux que de passer par plusieurs tests; et écrire systématiquement "manuellement" le END:VEVENT à la fin en plus du END:VCALENDAR

  5. #5
    Membre du Club
    Inscrit en
    Mars 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Mars 2009
    Messages : 7
    Par défaut
    Merci umfred pour ta réponse.
    Je n'ai pas retouché depuis, mais je vais essayer ce que tu me dis

    Edit : c'est pourtant ce qu'il me semble faire :
    dans la partie event, je démarre la boucle en k à partir de j, cad la dernière ligne lue et je teste si ça contient le endEvent

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 540
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 540
    Par défaut
    ligne 34: for(j=0; j<fichierSRC.Length;j++) + 1er bloc if; alors qu'un for(j=eventpos[i];j<enventpos[i+1];j++) on commence direct sur la bonne ligne (il faut juste rajouter à la fin de eventpos, la position de fin (on peut aussi laisser la limite haute que tu utilises)

    Tu n'as pas besoin de la boucle k, elle se gère directement avec le j.

  7. #7
    Membre Expert
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 941
    Par défaut
    Petit conseil d'architecture, tu peux extraire la fonction servant à faire la répartition pour isoler la sémantique et la rendre plus testable. Par ailleurs, il vaut mieux qu'elle soit async afin de ne pas bloquer l'interface pendant le traitement.

    Sinon je pense aussi comme umfred que tu te poses trop de questions, le format est prévisible et tu peux te servir des marqueurs de fin pour interrompre le traitement d'un event. Une autre options, plus robuste, consiste à parser le calendrier pour en extraire les événements et les sérialiser indépendamment ; en effet, tu charges un fichier, mais le programme n'a aucune certitude que le contenu est bien ce qu'il en attend.

    Exemple pour les deux approches (pour le parsing j'ai utilisé la library Ical.Net) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    using Ical.Net;
    using Ical.Net.Serialization;
     
    namespace D2175184;
     
    public static class CalendarHelper
    {
    	#region String control based splitting
     
    	private const string BeginVCalendar = "BEGIN:VCALENDAR";
    	private const string BeginVEvent = "BEGIN:VEVENT";
    	private const string EndVEvent = "END:VEVENT";
    	private const string EndVCalendar = "END:VCALENDAR";
     
    	public static async Task<bool> SplitEventListWhenMany(string filename)
    	{
    		var lines = await File.ReadAllLinesAsync(filename);
    		bool eventFound = false;
     
    		foreach (var line in lines)
    		{
    			if (line.Contains(BeginVCalendar))
    			{
    				if (eventFound)
    				{
    					await SplitEventList(lines);
    					return true;
    				}
    				else
    				{
    					eventFound = true;
    				}
    			}
    		}
     
    		return false;
    	}
     
    	public static async Task SplitEventList(string[] lines)
    	{
    		int count = 0;
    		StreamWriter? writer = null;
    		try
    		{
    			foreach (string line in lines)
    			{
    				switch (line.ToUpper())
    				{
    					case BeginVEvent:
    						if (writer != null)
    							throw new FormatException("Invalid VEVENT list format: a new event was opened while the previous one has not been closed");
     
    						writer = new StreamWriter($"calendar_{count}.txt");
    						count++;
    						await writer.WriteLineAsync(BeginVCalendar);
    						await writer.WriteLineAsync(BeginVEvent);
    						break;
    					case EndVEvent:
    						if (writer == null)
    							throw new FormatException("Invalid VEVENT list format: end of event mark has been met before event opening");
     
    						await writer.WriteLineAsync(EndVEvent);
    						await writer.WriteLineAsync(EndVCalendar);
    						await writer.FlushAsync();
    						writer.Close();
    						writer = null;
    						break;
    					default:
    						if (writer != null) // Ignore lines outside of a VEVENT
    							await writer.WriteLineAsync(line);
    						break;
    				}
    			}
    		}
    		finally
    		{
    			writer?.Dispose();
    		}
    	}
     
    	#endregion
     
     
    	#region Parser based splitting
     
    	public static async Task<bool> SafeSplitCalendar(string filename)
    	{
    		var content = await File.ReadAllTextAsync(filename);
    		return await SafeSplitCalendar(content, i => new StreamWriter($"calendar_{i}.txt"));
    	}
     
    	public static async Task<bool> SafeSplitCalendar(string content, Func<int, TextWriter> createWriter)
    	{
    		var calendar = Calendar.Load(content);
    		if (calendar.Events.Count < 2) return false;
     
    		var serializer = new CalendarSerializer();
    		for (int i = 0; i < calendar.Events.Count; ++i)
    		{
    			var @event = calendar.Events[i];
    			var tmpCalendar = new Calendar
    			{
    				ProductId = calendar.ProductId // If keeping the original calendar producer is expected behavior
    			};
    			tmpCalendar.Events.Add(@event);
    			var writer = createWriter(i);
    			await writer.WriteAsync(serializer.SerializeToString(tmpCalendar));
    			await writer.FlushAsync();
    			writer.Close();
    		}
     
    		return true;
    	}
     
    	#endregion
    }

  8. #8
    Membre du Club
    Inscrit en
    Mars 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Mars 2009
    Messages : 7
    Par défaut
    Merci Noxen pour ta réponse.
    En fait, je n'ai pas tout précisé ; je suis de niveau débutant, avec VS2010, .Net Framework 4, et je ne voulais pas trop importer de classe par Nuget ou autre.

    En fait j'ai trouvé la solution grâce au site https://brokenvectors.github.io/ que je remercie.
    Je me suis inspiré du script que j'ai adapté en C#, et ça donne ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
            public string[] splitICS(string data, int splitInterval, Boolean removeUIDs)
            {
                string[] portions;
                string header = "";
                string[] lines = data.Split(Environment.NewLine.ToCharArray());
                int portionIndex = 0;
                Boolean headerPassed = false;
                Boolean insideEvent = false;
                int eventEndIndex = 0;
                int nbEvents = 0;
     
                // loop to count the nb of events
                for (int i = 0; i < lines.Length; i++)
                {
                    string ln = lines[i];
                    if (ln == "BEGIN:VEVENT")
                    {
                        nbEvents++;
                    }
     
                }
     
                portions = new string[nbEvents];
     
                for (int i = 0; i < lines.Length; i++)
                {
                    string ln = lines[i];
     
                    // Skip UID lines if removeUIDs is true
                    if (removeUIDs && ln.StartsWith("UID:"))
                    {
                        continue;
                    }
     
                    if (ln == "BEGIN:VEVENT")
                    {
                        insideEvent = true;
                        headerPassed = true;
                        if (portions[portionIndex] == null)
                        {
                            portions[portionIndex] = header;
                        }
                    }
                    if (!headerPassed)
                    {
                        header += ln + "\n";
                    }
                    if (headerPassed && insideEvent)
                    {
                        portions[portionIndex] += ln + "\n";
                    }
     
                    if (ln == "END:VEVENT")
                    {
                        insideEvent = false;
                        eventEndIndex += 1;
                        if ((eventEndIndex % splitInterval == 0) || (lines[i + 1] == "END:VCALENDAR"))
                        {
                            portions[portionIndex] += "END:VCALENDAR" + "\n";
                            portionIndex += 1;
                        }
                    }
                }
                return portions;
            }
    Ca fonctionne super bien, même si je n'ai pas implémenté la possibilité de faire un zip, mais je n'en ai pas besoin.

    Merci encore à vous 2 pour m'avoir répondu.
    Cordialement.
    windo

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 10
    Dernier message: 27/02/2009, 17h19
  2. [VB.NET] plusieurs checkbox plusieurs events
    Par b_lob dans le forum Windows Forms
    Réponses: 5
    Dernier message: 02/05/2006, 12h23
  3. Réponses: 2
    Dernier message: 03/03/2006, 20h24
  4. Réponses: 2
    Dernier message: 02/02/2006, 18h21
  5. [XSLT] Diviser un fichiers xml en plusieurs pages html
    Par thibaut06 dans le forum XSL/XSLT/XPATH
    Réponses: 8
    Dernier message: 07/04/2005, 16h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo