9
votes

Quelle est la meilleure façon de l'analyser en C ++?

Dans mon programme, j'ai une liste de "Adresse du serveur" dans le format suivant:

::1:80 // Invalid: Is this the IPv6 address ::1:80 and a default port, or the IPv6 address ::1 and the port 80 ?
::1:http // This is not ambigous, but for simplicity sake, let's consider this is forbidden as well.


4 commentaires

Je ne suis pas sûr de comprendre ce que vous entendez par "choisir". J'ai conçu le format moi-même (si c'est ce que vous demandez) mais je crois que c'est un très courant. J'aurais pu utiliser un autre séparateur, mais je ne pense pas que cela aurait été élégant. Exemple: "Connexion à localhost $ http" semble moins intuitif que "Connexion à localhost: http".


Si vous ne voulez pas changer le séparateur (localhost-http vous semble bien), vous pouvez forcer tous les hôtes à être joints, sinon regex le ferait


- est un caractère légitime pour les noms d'hôte; Je ne veux pas vraiment remplacer sur l'ambiguïté par un autre;)


Bonne question, je pense à la même tâche dans C.


7 Réponses :


8
votes

Avez-vous regardé Boost :: Spirit ? Cela pourrait être surchargé pour votre tâche, cependant.


12 commentaires

Je ne savais pas qu'il existait. Merci. Cependant, comme vous venez de le dire, semble une sorte de surpuissance pour ma tâche. Je ne monte personne d'autre avec quelque chose de plus simple, je vais sûrement prendre un aspect profond.


Depuis que la communauté semble aimer cette solution, une personne pourrait-elle me donner quelques directives pour commencer avec boost :: esprit dans mon cas particulier?


@ cerneon: Malheureusement, je n'ai jamais eu de raison de travailler avec l'esprit et je ne l'ai jamais rendue au sommet de ma liste de choses que je voudrais jouer avec non plus, je ne peux donc pas vous donner des conseils. AFAIK C'est une machine importante et lourde-méta-méta et pourrait bien être en surpoids pour votre objectif. Regex ne serait-il pas aider ici? Sinon, je n'écrirais probablement pas simplement un analyseur simple à l'aide de flux de chaîne. Cela dit, la façon dont ce tutoriel d'esprit commence semble tellement intéressant ...


@Matthieu: J'ai bien peur que l'on se passe sur ma tête. Quel est le Serviette pour?


Tu ne sais pas H2G2? Vous devriez toujours avoir une serviette avec vous.


@sbi: Urbaydictionary définit "N'oubliez pas d'apporter une serviette" comme une chose incroyable pour dire au hasard. Le 25 mai était la journée de la serviette en l'honneur de Douglas Adams.


@Matthieu & @brian: Je suis bien conscient de la racine de cette phrase (voir le lien dans mon commentaire), je ne sais tout simplement pas comment l'appliquer ici. C'était probablement stupide de moi de m'attendre à une cause raisonnable ...


Oh, c'est juste parce que Ereon a demandé aux directives de démarrer avec Boost :: Spirit et je me suis souvenu de mes premiers essais chaotiques. Il faut un peu pour aller au-delà du "bonjour, monde!" (au moins il a pris un peu pour moi). Vous devez vraiment exploiter un certain nombre de bibliothèques de boost pour passer au-delà des exemples les plus triviaux: Phoenix pour exprimer les foncteurs, variante chaque fois que vous obtenez des types non liés dans une collection, etc ... et je vous épargne les messages d'erreur griettes quand quelque chose est désactivé ... J'admire Néanmoins le pouvoir / l'intelligence :)


Vous ne m'avez jamais parlé de cela, @sbi :) a ajouté ma prise en X3. Il gère également les cas «ambigu» de manière gracieuse. Et jette dans la résolution de validation / DNS: Stackoverflow.com/a/66827620/85371


@sehe: Je ne suis pas sûr que vous étiez même déjà dans la discussion lorsque j'ai écrit cette réponse. Je ne savais certainement pas à quel point vous êtes mal à l'esprit à l'époque. :)


@sbi tu es absolument raison. La façon dont je me souviens que j'ai commencé à-être - mon compte ~ 2011, et probablement seulement dans le salon en 2012 ou plus


@sehe: Fwiw, j'ai longtemps évoqué toutes les réponses à l'esprit qui entrent plus en détail que le mien, y compris la vôtre.



-4
votes

Si vous obtenez le port et l'hôte via une chaîne ou en C ++, un tableau de caractères; Vous pouvez avoir la longueur de la chaîne. Faites une boucle jusqu'à la fin de la chaîne et allez jusqu'à ce que vous trouviez un seul point de côlon seul et la fendre la chaîne en deux parties à cet endroit.

for (int i=0; i<string.length; i++) {
     if (string[i] == ':') {
          if (string[i+1] != ':') {
               if (i > 0) {
                    if (string[i-1] != ':') {
                         splitpoint = i;
}    }    }    }    }


7 commentaires

Vous savez que vous pouvez combiner des conditionnels avec && ? si (string [i] == ':' && string [i + 1]! = '&& i> 0 && string [i-1]! =')


Merci pour votre réponse. Mais c'est encore plus hacky que ce que je suis venu à;) Et je ne suis pas sûr que cela gère le ipv6 cas spéciaux.


@Michael - Ouais, je sais que vous pouvez, cependant, si vous essayez de faire une comparaison de chaîne [I-1] en même temps, vous vérifiez si je> 0 puis vous allez jeter des erreurs car vous ne pouvez pas accéder à la chaîne [- 1] Et je viens de le jeter ensemble = p @eon - aucun problème vient de penser que je donnerais la première chose qui a sauté dans mon esprit


Aussi la chaîne [i + 1] lorsque i est est string.length () - 1 (la dernière boucle) se résout à chaîne [string.length ()] que je crois est hors limites.


Oh ... Yup n'a pas vu ça lol, il serait hors de liaison. Vous voyez, vous a dit que cela pourrait être mieux.


@Geschafer ne manque pas de poignée à court-circuit (i> 0) && (string [I-1]! = ':')?


@Geschafer vous pouvez faire si (0 && formatire_harddrive ()); et rien ne se passera; Evaluation s'arrête si le côté gauche d'un && est faux , sans exécuter le côté droit



0
votes

Comme mentionné, boost.spirit.qi pourrait gérer cela.

Comme mentionné, il est trop sucré (vraiment). P>

const std::string line = /**/;

if (line.empty()) return;

std::string host, port;

if (line[0] == '[')           // IP V6 detected
{
  const size_t pos = line.find(']');
  if (pos == std::string::npos) return;  // Error handling ?
  host = line.substr(1, pos-1);
  port = line.substr(pos+2);
}
else if (std::count(line.begin(), line.end(), ':') > 1) // IP V6 without port
{
  host = line;
}
else                          // IP V4
{
  const size_t pos = line.find(':');
  host = line.substr(0, pos);
  if (pos != std::string::npos)
    port = line.substr(pos+1);
}


9 commentaires

Merci ! Probablement pas optimal, mais définitivement lisible. Cependant, ce qui se passe au cas où je fournis la chaîne suivante: "[:: 1:22" ?


:: 1:22 serait considéré comme l'hôte: il n'y a pas de manipulation d'erreur ici, vous pouvez vérifier que dans le premier cas, il y a un support de fermeture assert (pos! = STD :: String :: Nos) ou quoi que ce soit que vous souhaitez :)


STD :: String a-t-il une fonction appelée compte ()? Cela me donne des erreurs dans VC2008. Erreur C2039: 'Compte': n'est pas membre de "std :: basic_string <_elem, _traits, _ax> '


J'ai fait une fonction pour compter les caractères d'une chaîne et cela donne maintenant le mauvais résultat avec "[:: 1:22" . J'ai host = :: 1: 22 et port = :: 1: 22 .


@Vite: Cela devrait avoir très probablement été std :: compter (ligne.begin (), ligne.end (), ':')


Ouais. Mais ça ne marche toujours pas. Principalement parce que cela n'essaie pas d'éliminer les mauvaises versions de l'adresse ".


@Vite : Bonne prise, c'est à cause de là un débordement de POS : std :: string :: npos + 2 == 1 . Amusant. Quoi qu'il en soit, ajouter dans la manipulation des erreurs (c.-à-d. Arrêter si aucun ] est trouvé) supprimer le problème. Il suffit de faire la même chose avec la partie IPv4.


Parce que c'est 2021, j'ai ajouté ma prise en X3. Il gère également les cas «ambigu» de manière gracieuse. Et jette dans la résolution de validation / DNS: Stackoverflow.com/a/66827620/85371


@sehe: Nice. Vous avez un peu une bataille en montée pour obtenir votre réponse vers le sommet; Espérons que les autres lanceront aussi leurs votes.



6
votes

Voici une classe simple qui utilise boost :: Xpressive pour faire le travail de vérification du type d'adresse IP et vous pouvez analyser le reste pour obtenir les résultats

Utilisation:. P>

"^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]+(\\:([0-9]{1,5}|http|ftp|smtp))?$"


5 commentaires

Quand les gens ont un problème, ils disent: je sais, je vais utiliser une expression régulière. Maintenant, ils ont 2 problèmes.


MDR. Ce n'est pas trop difficile de comprendre. J'ai appris en moins de 2 heures. Ce n'est pas comme s'il ne connaît pas le format et qu'il n'a pas déjà la solution. Si j'ai raison, il a déjà une solution utilisant STD :: Stringstream et il veut une solution élégante. Je vais ajouter une ventilation de regex dans le poteau.


Gonflé. Les expressions régulières ont toujours travaillé pour moi. Carpée de tous mes collègues paresseux de côté.


Gonflé? Vous voulez dire regex ou performance? Si c'est une performance, cela pourrait changer votre esprit? boost-sandbox.sourceforge.net/libs/ xressive / doc / html / ...


Parce que c'est 2021, j'ai ajouté ma prise en X3. Il gère également les cas «ambigu» de manière gracieuse. Et jette dans la résolution de validation / DNS: Stackoverflow.com/a/66827620/85371



3
votes
std::string host, port;
std::string example("[::1]:22");

if (example[0] == '[')
{
    std::string::iterator splitEnd =
        std::find(example.begin() + 1, example.end(), ']');
    host.assign(example.begin(), splitEnd);
    if (splitEnd != example.end()) splitEnd++;
    if (splitEnd != example.end() && *splitEnd == ':')
        port.assign(splitEnd, example.end());
}
else
{
    std::string::iterator splitPoint =
        std::find(example.rbegin(), example.rend(), ':').base();
    if (splitPoint == example.begin())
        host = example;
    else
    {
        host.assign(example.begin(), splitPoint);
        port.assign(splitPoint, example.end());
    }
}

4 commentaires

Dans la partie IPv6, le && sur splitends La condition semble douteuse. Vous invoquez un comportement indéfini ... Et puisque nous recherchions ] Je ne comprends pas comment l'itérateur pourrait indiquer : pour commencer.


Je ne comprends toujours pas comment cela pourrait être ":", tu ne veux pas dire (* (Splitend ++) == ':')? (Bien qu'il y aurait encore un risque de comportement non défini).


Je peux comparaître ... critique ... mais je crains que vous n'ayez toujours légèrement légèrement éteint. Chaque fois que vous attribuez au port, vous oubliez d'incrémenter l'itérateur, le premier caractère du port sera toujours : le cas échéant. Est-ce intentionnel?


@Matthieu M.: Vous êtes correct dans ce que j'ai complètement battu les adresses IPv6. Mais les IPv4 sont corrects. La base () membre d'inverse itérateurs est un élément en avant de cet itérateur inversé.



5
votes

Je suis en retard à la fête, mais j'étais googling pour la façon de faire ça. Spirit et C ++ ont beaucoup grandi, alors laissez-moi ajouter une prise 2021:

en direct sur Compiler Explorer xxx

impression xxx

bonus

validation / résolution les adresses. L'analyse est de 100% inchangée, il suffit d'utiliser ASIO pour résoudre les résultats, les validant également: xxx

impressions (réseau limité Vivez sur wandbox et coliru http: / /coliru.stacted-crooked.com/a/497d8091b40c9f2d ) xxx


0 commentaires

0
votes
#pragma once
#ifndef ENDPOINT_HPP
#define ENDPOINT_HPP

#include <string>

using std::string;

struct Endpoint {
  string
    Host,
    Port;
  enum : char {
    V4,
    V6
  } Type = V4;
  __inline Endpoint(const string& text) {
    bind(text);
  }
private:
  void __fastcall bind(const string& text) {
    if (text.empty())
      return;
    auto host { text };
    string::size_type bias = 0;
    constexpr auto NONE = string::npos;
    while (true) {
      bias = host.find_first_of(" \n\r\t", bias);
      if (bias == NONE)
        break;
      host.erase(bias, 1);
    }
    if (host.empty())
      return;
    auto port { host };
    bias = host.find(']');
    if (bias != NONE) {
      host.erase(bias);
      const auto skip = text.find('[');
      if (skip == NONE)
        return;
      host.erase(0, skip + 1);
      Type = V6;
      ++bias;
    }
    else {
      bias = host.find(':');
      if (bias == NONE)
        port.clear();
      else {
        const auto next = bias + 1;
        if (host.length() == next)
          return;
        if (host[next] == ':') {
          port.clear();
          Type = V6;
        }
        else if (! bias)
          host.clear();
        else
          host.erase(bias);
      }
    }
    if (! port.empty())
      Port = port.erase(0, bias + 1);
    if (! host.empty())
      Host = host;
  }
};

#endif // ENDPOINT_HPP

0 commentaires