2008-11-13 10 views
1

J'ai une chaîne à marquer. Sa forme est HHmmssffH, m, s, f sont des chiffres.Tokensizer offset inversé

Il est censé être segmenté en quatre nombres à deux chiffres, mais j'en ai besoin pour accepter également les formes abrégées, comme sff, pour qu'il l'interprète comme 00000sff. Je voulais utiliser boost::tokenizeroffset_separator, mais il semble fonctionner uniquement avec des décalages positifs et je voudrais le faire travailler en arrière.

Ok, une idée est de remplir la chaîne avec des zéros de la gauche, mais peut-être que la communauté trouve quelque chose uber -smart. ;)

Modifier:Des exigences supplémentaires viennent d'entrer en jeu.

Le besoin fondamental d'une solution était de traiter tous les cas plus intelligents, comme f, ssff, mssff, etc., mais aussi accepter une notation de temps plus complet, comme HH:mm:ss:ff avec ses formes sténographie, par exemple s:ff ou même s: (celui-ci est censé être interprété comme s:00).

Dans le cas où la chaîne se termine par :, je peux évidemment aussi la tamponner avec deux zéros, puis enlever tous les séparateurs en ne laissant que les chiffres et analyser la ficelle résultante avec de l'alcool.

Mais il semblerait que ce serait un peu plus simple s'il y avait un moyen de faire revenir le tokenizer offset à la fin de la chaîne (décalages -2, -4, -6, -8) et de lancer le numéro lexical à int s.

+0

"une idée est de remplir la chaîne avec des zéros de la gauche," Simple, rapide, fonctionne. Pourquoi chercher autre chose? –

+0

Ne pas être un monstre des performances, mais cette solution implique une copie de chaîne (entrée est une chaîne const & std :: string). – macbirdie

+0

ne devrait pas être un problème, std :: string est généralement assez intelligent pour être copié à l'écriture, donc il n'y aurait un coût de performance que si vous ajoutez réellement. –

Répondre

1

Je continue de prêcher la notation BNF. Si vous pouvez écrire la grammaire qui définit votre problème, vous pouvez facilement le convertir en un analyseur Boost.Spirit, qui le fera pour vous.

TimeString := LongNotation | ShortNotation 

LongNotation := Hours Minutes Seconds Fractions 

Hours := digit digit 
Minutes := digit digit 
Seconds := digit digit 
Fraction := digit digit 

ShortNotation := ShortSeconds Fraction 
ShortSeconds := digit 

Edit: contrainte supplémentaire

VerboseNotation = [ [ [ Hours ':' ] Minutes ':' ] Seconds ':' ] Fraction 
+0

Mais cela gère seulement celui-ci 'sff', pas' ssff' ou 'mssff' ou même 'f'. Pour l'instant, je remballe la chaîne et l'analyse en fait avec l'esprit. – macbirdie

+0

Vous pouvez exiger des exigences du produit qu'ils spécifient entièrement leurs besoins sous forme BNF. S'ils ne peuvent pas, vous pouvez les aider, s'ils le peuvent, vous avez terminé. – xtofl

0

expressions régulières viennent à l'esprit. Quelque chose comme "^0*?(\\d?\\d?)(\\d?\\d?)(\\d?\\d?)(\\d?\\d?)$" avec boost::regex. Les sous-échantillons vous fourniront les valeurs numériques. Ne devrait pas être difficile à adopter dans votre autre format avec des deux-points entre les numéros (voir la réponse de sep61.myopenid.com). boost::regex est parmi les parseurs regex les plus rapides sur le marché.

0

En réponse au commentaire "Ne pas être un monstre de performance, mais cette solution implique une copie de chaîne (entrée est un const & std :: string)". Si vous vous souciez tellement des performances que vous ne pouvez pas utiliser une grande bibliothèque comme regex, vous ne risquez pas un analyseur BNF, vous ne voulez pas supposer que std :: string :: substr évitera copier avec l'allocation (et ne peut donc pas utiliser les fonctions de chaîne STL), et ne peut même copier les caractères de chaîne dans un tampon et gauche de la tablette avec des caractères « 0 »:

void parse(const string &s) { 
    string::const_iterator current = s.begin(); 
    int HH = 0; 
    int mm = 0; 
    int ss = 0; 
    int ff = 0; 
    switch(s.size()) { 
     case 8: 
      HH = (*(current++) - '0') * 10; 
     case 7: 
      HH += (*(current++) - '0'); 
     case 6: 
      mm = (*(current++) - '0') * 10; 
     // ... you get the idea. 
     case 1: 
      ff += (*current - '0'); 
     case 0: break; 
     default: throw logic_error("invalid date"); 
     // except that this code goes so badly wrong if the input isn't 
     // valid that there's not much point objecting to the length... 
    } 
} 

Mais fondamentalement, juste 0- l'initialisation de ces variables int est presque autant de travail que la copie de la chaîne dans un tampon char avec remplissage, donc je ne m'attendrais pas à voir une différence de performance significative.Je ne recommande donc pas réellement cette solution dans la vie réelle, juste comme un exercice d'optimisation prématurée.

+0

En fait, j'utilise un analyseur BNF et std :: string pour que la performance ne soit pas si énorme qu'un problème (l'esprit est surtout une bibliothèque de modèles après tout) et je suis en train de créer un remplacement pour un code similaire à votre exemple plus générique et, euh, gentil. ;) – macbirdie

+0

Je voulais simplement voir s'il y avait une solution vraiment propre possible. – macbirdie

+0

Assez juste - la question implicite par ce commentaire est un tout autre sujet de la question que vous avez vraiment posée, sur le code simple plutôt que sur l'horrible byte-fiddling. Je suis friand de ce type de switch hack, parce qu'il flippe les cases qui ont peur de tomber ;-) –

Questions connexes