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 :

Parser Srt (subrip)


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations forums :
    Inscription : Mars 2009
    Messages : 22
    Par défaut Parser Srt (subrip)
    Bonjour a vous tous !
    Je développe pour mon amusement un petit projet de synchro de srt avec l' activeX de vlc sous VS2010, et j' import donc des fichiers SRT

    Une première version lie le fichier ligne par ligne et utilise des boucles while suivie de String.Split ...
    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
    StreamReader reader = new StreamReader(fichier, System.Text.Encoding.Default);
                while ((ligne = reader.ReadLine()) != null)
                {
                    try
                    {
     
                        if (utils.IsNumeric(ligne) == true && item != string.Empty)
                        {
     
                            string[] splititem = item.Split(new string[] { "\n", "-->", "\n", }, System.StringSplitOptions.None);
     
                            int item_numero = Convert.ToInt32(splititem[0]);
                            string item_debut = splititem[1];
                            string item_fin = splititem[2];
     
                            string item_texte = null;
                            for (int i = 3; i < splititem.Count(); i++)
                            {
                                item_texte += splititem[i] + "\n";
                            }
                            item_texte = utils.RemoveEmptyLines(item_texte);
     
                            table.Rows.Add(item_numero, Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(item_debut)) + append_time)), Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(item_fin)) + append_time)), utils.milliseconds_from_hours(item_debut) + append_time, utils.milliseconds_from_hours(item_fin) + append_time, item_texte);
                            item = null;
                            item += ligne + "\n";
     
                            table.Rows.Add(match.Groups["sequence"].Value,
                                Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(match.Groups["start"].Value)) + append_time)),
                                Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(match.Groups["end"].Value)) + append_time)),
                                utils.milliseconds_from_hours(match.Groups["start"].Value) + append_time,
                                utils.milliseconds_from_hours(match.Groups["end"].Value) + append_time,
                                match.Groups["text"].Value);
                        }
                        else
                        {
                            item += ligne + "\n";
                        }
                    }
     
                } // FIN while ..

    J' ai changé en utilisant un regex qui lie tout le fichier d' un coup
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private static Regex input = new Regex(@"(?<numero>\d*)\r\n(?<debut>\d{2}:\d{2}:\d{2},\d{3}) --> (?<fin>\d{2}:\d{2}:\d{2},\d{3})\r\n(?<texte>([^\n]+)\n(([^\n]+)))", RegexOptions.Compiled );
    , et accède aux groupe
    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
     StreamReader reader = new StreamReader(fichier, System.Text.Encoding.Default);
                string fich_ = reader.ReadToEnd();
                MatchCollection matches = input.Matches(fich_);
     
     
                foreach (Match match in matches)
                {
     
                    table.Rows.Add(
                                   match.Groups["numero"].Value,
                                  Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(match.Groups["debut"].Value)) + append_time)),
                                  Convert.ToString(utils.hours_from_milliseconds(Convert.ToDouble(utils.milliseconds_from_hours(match.Groups["fin"].Value)) + append_time)),
                                  utils.milliseconds_from_hours(match.Groups["debut"].Value) + append_time,
                                  utils.milliseconds_from_hours(match.Groups["fin"].Value) + append_time,
                                  utils.RemoveEmptyLines(match.Groups["texte"].Value)
                                  );
     
                }
    Les deux fonctionnent, mais en rapidité sont ex-arquo .. en lenteur...

    Mes fonctions perso de la classe utils reformattent les données
    "hh:mm:ss:mmm" en ms ou inversement ...
    J' ai essayé de voir si elles sont en cause en les enlevant juste pour voir mais c' est toujours aussi lent ...

    Pour indication, je compte environ 10 sec. pour ouvrir un fichier de 2000 sous-titres,

    Je suis simple amateur, peut -être suis-je à coté de la plaque niveau stratégie ...

    Sinon, ca existe des parseurs pareils en "tout fait" ?

    Merci pour vos lumières ...

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Bon, pour le fun, je me suis amusé à implémenter la même chose

    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
    117
    118
    public class SubtitleParser
    {
     
        public IEnumerable<SubtitleItem> Parse(string path)
        {
            return Parse(File.OpenRead(path));
        }
     
        public IEnumerable<SubtitleItem> Parse(Stream stream)
        {
            return Parse(new StreamReader(stream));
        }
     
        enum SubtitleParserStep
        {
            None,
            Id,
            Time,
            Text
        }
     
        public IEnumerable<SubtitleItem> Parse(TextReader reader)
        {
            using(reader)
            {
                SubtitleItem current = null;
                string line;
                SubtitleParserStep step = SubtitleParserStep.None;
                while ((line = reader.ReadLine()) != null)
                {
                    line = line.Trim();
                    if (string.IsNullOrEmpty(line))
                    {
                        if (current != null)
                            yield return current;
                        current = null;
                        step = SubtitleParserStep.None;
                        continue;
                    }
     
                    if (current == null)
                        current = new SubtitleItem();
     
                    if (step != SubtitleParserStep.Text)
                        step++;
     
                    switch (step)
                    {
                        case SubtitleParserStep.Id:
                            if (!ParseId(line, current))
                            {
                                // id is missing, skip to Time
                                step++;
                                goto case SubtitleParserStep.Time;
                            }
                            break;
                        case SubtitleParserStep.Time:
                            if (!ParseTime(line, current))
                            {
                                throw new FormatException("Le format de temps est incorrect");
                            }
                            break;
                        case SubtitleParserStep.Text:
                            if (!string.IsNullOrEmpty(current.Text))
                                current.Text += Environment.NewLine;
                            current.Text += line;
                            break;
                    }
                }
     
                if (current != null)
                    yield return current;
            }
        }
     
        bool ParseId(string line, SubtitleItem current)
        {
            int id;
            if (int.TryParse(line, out id))
            {
                current.Id = id;
                return true;
            }
            return false;
        }
     
        static readonly CultureInfo _subtitleTimeCulture = CultureInfo.GetCultureInfo("fr-FR");
        static readonly Regex _subtitleTimeRegex = new Regex("(?<start>[0-9:,]+) --> (?<end>[0-9:,]+)", RegexOptions.Compiled);
     
        bool ParseTime(string line, SubtitleItem current)
        {
            Match m = _subtitleTimeRegex.Match(line);
            if (m.Success)
            {
                TimeSpan start;
                TimeSpan end;
     
                if (!TimeSpan.TryParse(m.Groups["start"].Value, _subtitleTimeCulture, out start))
                    return false;
     
                if (!TimeSpan.TryParse(m.Groups["end"].Value, _subtitleTimeCulture, out end))
                    return false;
     
                current.StartOffset = start;
                current.EndOffset = end;
                return true;
            }
            return false;
        }
    }
     
    public class SubtitleItem
    {
        public int Id { get; set; }
        public TimeSpan StartOffset { get; set; }
        public TimeSpan EndOffset { get; set; }
        public string Text { get; set; }
    }
    (non tu ne rêves pas, il y a bien un goto dans ce code )

    Utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    static void Main()
    {
        string path = @"E:\tmp\test.srt";
        var parser = new SubtitleParser();
        Console.WriteLine(parser.Parse(path).ToList().Count);
    }
    Pour un fichier de 2000 sous-titres, j'obtiens un temps entre 30 et 60 ms

  3. #3
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut
    Il a l'air marrant votre jeu

    Vais essayer en utilisant Linq
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  4. #4
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut
    Verdict, entre 40 et 60ms (ça dépend des exécutions) avec le code suivant (avec un fichier de 2000 sous-titres également) :

    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
     
    public static class SubTitleParser
    {
        private static IEnumerable<string> GetSubTitleEnumerator(TextReader reader)
        {
            string line;
            StringBuilder sb = new StringBuilder();
     
            using (reader)
            {
                while ((line = reader.ReadLine()) != null)
                {
                    if (string.IsNullOrEmpty(line.Trim()))
                    {
                        yield return sb.ToString().TrimEnd();
                        sb = new StringBuilder();
                    }
                    else
                    {
                        sb.AppendLine(line);
                    }
                }
     
                if (sb.Length > 0)
                    yield return sb.ToString();
            }
        }
     
        private static TextReader OpenSrt(string path)
        {
            return new StreamReader(File.OpenRead(path));
        }
     
        public static IEnumerable<SubTitleItem> Parse(string path)
        {
            TextReader reader = OpenSrt(path);
            string[] delimiter = new string[1] { "-->" };
     
            return (from subtitle in
                        (from title in GetSubTitleEnumerator(reader)
                         select title)
                    where string.IsNullOrEmpty(subtitle) == false
                    select new SubTitleItem(
                        int.Parse(subtitle.GetLine(0)),
                        ParseTime(subtitle.GetLine(1).Split(delimiter, StringSplitOptions.None)[0]),
                        ParseTime(subtitle.GetLine(1).Split(delimiter, StringSplitOptions.None)[1]),
                        subtitle.FromLineToEnd(2)));
        }
     
        private static TimeSpan ParseTime(string s)
        {
            TimeSpan result;
     
            TimeSpan.TryParse(s.Replace(',', '.'), out result);
            return result;
        }
    }
     
    public class SubTitleItem
    {
        public SubTitleItem()
        { }
     
        public SubTitleItem(int id, TimeSpan beginTime, TimeSpan endTime, string text)
        {
            Id = id;
            BeginTime = beginTime;
            EndTime = endTime;
            Text = text;
        }
     
        public int Id { get; set; }
        public TimeSpan BeginTime { get; set; }
        public TimeSpan EndTime { get; set; }
        public string Text { get; set; }
    }
     
    public static class stringExtension
    {
        public static string[] endLine = new string[1] { Environment.NewLine };
     
        public static string GetLine(this string s, int index)
        {
            return s.Split(endLine, StringSplitOptions.None)[index];
        }
     
        public static string FromLineToEnd(this string s, int index)
        {
            return s.Split(endLine, StringSplitOptions.None)
                .SkipWhile((a, b) => b < index).Aggregate((a, b) => a + b);
        }
    }
    Et le code qui lance :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public partial class Form1 : Form
    {
        public Form1()
        {
            DateTime ts = DateTime.Now;
            Console.WriteLine(SubTitleParser.Parse(@"C:\test.srt").ToList().Count);
            Console.WriteLine(string.Format("Temps mis : {0}", DateTime.Now.Subtract(ts).TotalMilliseconds));
     
            InitializeComponent();
        }
    }

    Tomlev

    [EDIT] Passage par la classe Parser
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations forums :
    Inscription : Mars 2009
    Messages : 22
    Par défaut
    WOuaa ....
    J' ai réussit a remplir ma datatable avec le code suivant, mais j' avoue que le comment du pourquoi m' échappe ...

    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
    var parser = new SubtitleParser();
                List<SubtitleItem> subtitles = parser.Parse(fichier).ToList();
                Debug.WriteLine(".............."+System.DateTime.Now.Second);
                foreach (SubtitleItem item in subtitles)
                {
     
                    //Debug.WriteLine(item.Id);
                    //Debug.WriteLine(item.StartOffset.ToString());
                    //Debug.WriteLine(item.EndOffset.ToString());
                    //Debug.WriteLine(item.Text);
     
                    table.Rows.Add(
                         Convert.ToInt32(item.Id),
                         Convert.ToString(item.StartOffset.ToString()),
                         Convert.ToString(item.EndOffset.ToString()),
                         utils.milliseconds_from_hours(item.StartOffset.ToString()),
                         utils.milliseconds_from_hours(item.EndOffset.ToString()),
                         utils.RemoveEmptyLines(item.Text)
                         );
     
     
                }
                Debug.WriteLine(".............." + System.DateTime.Now.Second);
    Malgé tout, je mets 7 secondes a remplir la grille ?? Je sais j' y comprends rien ..

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Il faudrait mettre un Stopwatch pour voir ce qui prend du temps, mais a priori ça doit venir de toutes tes conversions... Il y a quoi dans ta méthode milliseconds_from_hours ? Pourquoi tu passes tout le temps par des string au lieu de garder les valeurs dans leur type d'origine ?

    D'autre part, si tu affiches les données dans une interface graphique, ça prend forcément plus de temps...

  7. #7
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut
    Re,

    je ne connais pas la structure de votre table mais le code suivant :
    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
    DataTable table = new DataTable();
     
    table.Columns.AddRange(new DataColumn[] {
        new DataColumn("Id", typeof(int)),
        new DataColumn("BeginTime", typeof(string)),
        new DataColumn("EndTime", typeof(string)),
        new DataColumn("Text", typeof(string)),
    });
     
    ts = DateTime.Now;
     
    foreach (SubTitleItem item in SubTitleParser.Parse(@"C:\test.srt").ToArray())
    {
        table.Rows.Add(new object[] {
            item.Id,
            item.BeginTime.ToString(),
            item.EndTime.ToString(),
            item.Text
        });
    }
     
    Console.WriteLine(string.Format("Temps mis : {0}", DateTime.Now.Subtract(ts).TotalMilliseconds));
    ne prend que ~130ms pour plus de 4000 sous-titres...
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  8. #8
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par Arnaud F. Voir le message
    ne prend que ~130ms pour plus de 4000 sous-titres...
    Et ça serait sans doute encore plus rapide en gardant les timestamp sous forme de timespan

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

Discussions similaires

  1. [JAXP] com.sun.xml.parser.ValidatingParser
    Par yolepro dans le forum Format d'échange (XML, JSON...)
    Réponses: 7
    Dernier message: 05/11/2008, 15h36
  2. parser xml
    Par billout dans le forum C++Builder
    Réponses: 4
    Dernier message: 20/11/2003, 11h08
  3. [Servlet] parser la requete
    Par jaimepasteevy dans le forum Servlets/JSP
    Réponses: 3
    Dernier message: 15/10/2003, 16h43
  4. Parser XML
    Par miloux32 dans le forum XML/XSL et SOAP
    Réponses: 4
    Dernier message: 18/07/2003, 03h17
  5. [langage] Continuer a parser une ligne
    Par D[r]eadLock dans le forum Langage
    Réponses: 5
    Dernier message: 30/09/2002, 18h49

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