En examinant mon code, mon professeur a dit que mon utilisation de strstr et strchr entraînait beaucoup de gaspillage de ressources car chacun d'entre eux analysait la chaîne.
Puis-je réduire le nombre de fonctions d'une bonne manière?
Ce code scanne une chaîne et en fonction des paramètres définis, décide si l'entrée est valide ou non.
ch1 est '@' et ch2 est '.' , ( email [i] code>) est la chaîne.
int at_count = 0, dot_count = 0, error1 = 0, error2 = 0;
int i;
size_t length = strlen(email);
int ch1 = '@', ch2 = '.';
for ( i = 0; email[i] != '\0'; i++) /* for loop to count the occurance of the character '@' */
{
if ( email[i] == ch1)
at_count++;
}
for ( i = 0; email[i] != '\0'; i++) /* for loop to count the occurance of the character '.' */
{
if ( email[i] == ch2)
dot_count++;
}
if ( email[0] == ch1 || email[0] == ch2 || email[length-1] == ch1 || email[length-1] == ch2 )
{
error1++;
}
else
{
error1 = 0;
}
if ( strstr(email,".@") || strstr(email, "@.") || strstr(email, "..") || strstr(email, "@@"))
{
error2++;
}
else
{
error2 = 0;
}
if ( (at_count != 1) || (dot_count < 1) || (error1 == 1) || (error2 == 1))
{
printf("The user entered email address '%s' is invalid\n", email);
}
else
{
printf("'%s' is a valid email address\n", email);
}
Voici l'extrait dont je parle.
Dois-je écrire mon propre code qui effectue la vérification une fois? si oui, pouvez-vous me donner quelques indications à ce sujet? Je vous remercie.
EDIT: Merci beaucoup pour vos réponses, j'ai appris les erreurs dans mon code et j'espère en tirer des leçons.
Merci encore!
EDIT: 2: Je tiens à vous remercier encore une fois pour vos réponses, elles m'ont énormément aidé, et je crois que j'ai écrit un meilleur code
for (i = 0; email[i] != 0; i++) {
{
if (strstr(email, "@.") ||
strstr(email, ".@") ||
strstr(email, "..") ||
strstr(email, "@@") ||
email[i] == ch1 ||
email[i] == ch2 ||
email[strlen(email) - 1] == ch1 ||
email[strlen(email) - 1] == ch2) {
printf("The entered e-mail '%s' does not pass the required parameters, Thus it is invalid\n", email);
} else {
printf("The email '%s' is a valid e-mail address\n",email);
}
break;
}
}
Je pense que c'est un code plus élégant et plus simple, aussi plus efficace.
Ma principale inspiration était @chqrlie, car je trouvais que son code était très agréable et facile à lire.
Puis-je m'améliorer de toute façon?
(Les vérifications des e-mails sont uniquement destinées à la pratique, ne les dérangez pas!)
Merci beaucoup à tous!
3 Réponses :
Considérez strpbrk . Toutes les conditions peuvent éventuellement être évaluées en un seul passage par e-mail.
#include <stdio.h>
#include <string.h>
int main( void) {
char email[1000] = "";
char at = '@';
char dot = '.';
char *find = NULL;
char *atfind = NULL;
char *dotfind = NULL;
int atfound = 0;
if ( fgets ( email, sizeof email, stdin)) {
email[strcspn ( email, "\n")] = 0;//remove trailing newline
find = email;
while ( ( find = strpbrk ( find, "@."))) {//find a . or @
if ( find == email) {
printf ( "first character cannot be %c\n", *find);
return 0;
}
if ( 0 == *( find + 1)) {
printf ( "email must not end after %c\n", *find);
return 0;
}
//captures .. @@ .@ @.
if ( dot == *( find + 1)) {
printf ( ". cannot follow %c\n", *find);
return 0;
}
if ( at == *( find + 1)) {
printf ( "@ cannot follow %c\n", *find);
return 0;
}
if ( dot == *( find)) {
dotfind = find;
}
if ( at == *( find)) {
atfind = find;
atfound++;
if ( atfound > 1) {
printf ( "multiple @\n");
return 0;
}
}
find++;
}
if ( !atfind) {
printf ( "no @\n");
return 0;
}
if ( !dotfind) {
printf ( "no .\n");
return 0;
}
if ( atfind > dotfind) {
printf ( "subsequent to @, there must be a .\n");
return 0;
}
}
else {
printf ( "problem fgets\n");
return 0;
}
printf ( "good email\n");
return 0;
}
Votre code a en effet plusieurs problèmes:
int at_count = 0;
int has_error = 0;
size_t i, len = strlen(email);
if (len == 0 || email[0] == ch1 || email[0] == ch2 ||
email[len - 1] == ch1 || email[len - 1] == ch2) {
has_error = 1;
}
for (i = 0; !has_error && i < len; i++) {
if (email[i] == '.') {
if (email[i + 1] == '.' || email[i + 1] == '@') {
has_error = 1;
}
} else if (email[i] == '@') {
at_count++;
if (i == 0 || i == len - 1 || email[i + 1] == '.' || email[i + 1] == '@') {
has_error = 1;
}
}
// should also test for allowed characters
}
if (has_error || at_count != 1) {
printf("The entered e-mail '%s' does not pass the required tests, Thus it is invalid\n", email);
} else {
printf("The email '%s' is a valid e-mail address\n", email);
}
Vous scannez la chaîne 4 fois pour tester les différents modèles et encore 2 fois pour strlen () . Il devrait être possible d'effectuer les mêmes tests au cours d'une seule analyse.
Notez également que d'autres problèmes passent inaperçus:
@ présent Certains tests semblent exagérés: pourquoi refuser .. avant le @ , pourquoi refuser un final. avant le @?
Voici une version plus efficace:
for (i = 0; email[i] != 0; i++) { // you iterate for each character in the string.
{ //this is a redundant block, remove the extra curly braces
if (strstr(email, "@.") || // this test only needs to be performed once
strstr(email, ".@") || // so does this one
strstr(email, "..") || // so does this one
strstr(email, "@@") || // again...
email[i] == ch1 || // this test is only performed once
email[i] == ch2 || // so is this one
email[strlen(email) - 1] == ch1 || // this test is global
email[strlen(email) - 1] == ch2) { // so is this one
printf("The entered e-mail '%s' does not pass the required parameters, Thus it is invalid\n", email);
} else {
printf("The email '%s' is a valid e-mail address\n", email);
}
break; // you always break from the loop, why have a loop at all?
}
}
Ces commentaires m'ont aidé à réaliser la nécessité de comprendre quand un test doit être effectué une ou plusieurs fois, également ces tests comme; ".." sont obligatoires pour l'affectation, et certains d'entre eux n'ont pas beaucoup de sens comme vous l'avez dit. J'essaierai de comprendre la méthode que vous proposez et j'essaierai de trouver une solution par moi-même. Merci!
Pouvez-vous expliquer ce qu'est un test global? celui que vous avez mentionné dans les commentaires ?.
@Math_Seeker: un test qui ne dépend pas de i , donc n'a pas besoin d'être répété pour chaque caractère dans email .
Votre professeur a un bon point sur l'inefficacité de l'analyse répétitive des caractères dans email . Idéalement, chaque caractère ne doit être scanné qu'une seule fois. Que vous utilisiez une boucle for et une indexation de chaîne (par exemple, email [i] ) ou simplement un pointeur vers le bas, la chaîne email est en haut pour vous, mais vous ne devriez localiser chaque personnage qu'une seule fois. Au lieu de cela, dans votre code actuel, vous faites
pour chaque caractère dans email , vous p >
e-mail 4 fois avec strstr pour localiser une sous-chaîne donnée, et e-mail 2 fois avec strlen Pensez-y. Pour chaque caractère dans email , vous appelez strlen deux fois, ce qui permet de parcourir l'intégralité du contenu de email à la recherche le caractère nul-fin . Les quatre de vos appels strstr localisent deux caractères dans des combinaisons différentes. Vous pouvez au minimum rechercher l'un ou l'autre, puis vérifier le caractère précédent et celui qui suit.
@ chqrlie indique des combinaisons de caractères et des conditions supplémentaires qui devraient être vérifiées, mais comme je suppose qu'il s'agit d'un exercice d'apprentissage plutôt que de quelque chose destiné au code de production, il suffit d'être conscient que des critères supplémentaires sont nécessaires pour faire un e -mail de validation.
Bien qu'il n'y ait rien de mal à inclure string.h et pour des chaînes plus longues (généralement plus grandes que 32 caractères), les optimisations dans la chaîne La fonction .h fournira divers degrés d'efficacité améliorée, mais il n'est pas nécessaire d'engager une surcharge d'appel de fonction. Indépendamment de ce que vous recherchez dans votre saisie, vous pouvez toujours parcourir votre chaîne avec un pointeur vérifiant chaque caractère et prenant les mesures appropriées si nécessaire.
Un petit exemple supplémentaire de cette approche de votre problème, en utilisant le modeste goto au lieu d'un indicateur d'erreur, pourrait ressembler à ce qui suit:
#include <stdio.h>
#define MAXC 1024
int main (void) {
char buf[MAXC] = "", /* buffer to hold email */
*p = buf; /* pointer to buf */
short at = 0; /* counter for '@' */
fputs ("enter e-mail address: ", stdout);
if (fgets (buf, MAXC, stdin) == NULL) { /* read/validate e-mail */
fputs ("(user canceled input)\n", stderr);
return 1;
}
while (*p && *p != '\n') { /* check each character in e-mail */
if (*p == '@') /* count '@' - exactly 1 or fail */
at++;
if (p == buf && (*p == '@' || *p == '.')) /* 1st char '@ or .' */
goto emailerr;
/* '@' followed or preceded by '.' */
if (*p == '@' && (*(p+1) == '.' || (p > buf && *(p-1) == '.')))
goto emailerr;
/* sequential '.' */
if (*p == '.' && (*(p+1) == '.' || (p > buf && *(p-1) == '.')))
goto emailerr;
p++;
} /* last char '@' or '.' */
if (*(p-1) == '@' || *(p-1) == '.' || at != 1)
goto emailerr;
if (*p == '\n') /* trim trailing '\n' (valid case) */
*p = 0;
printf ("The email '%s' is a valid e-mail address\n", buf);
return 0;
emailerr:;
while (*p && *p != '\n') /* locate/trim '\n' (invalid case) */
p++;
if (*p == '\n')
*p = 0;
printf ("The email '%s' is an invalid e-mail address\n", buf);
return 1;
}
Comme mentionné, il existe de nombreuses façons de gérer le e -validation de messagerie, et dans une large mesure, vous ne devez pas vous concentrer sur les "micro-optimisations", mais plutôt sur l'écriture de code logique avec une validation sonore. Cependant, comme votre professeur l'a souligné, en même temps, votre logique ne devrait pas être inutilement répétitive en injectant des inefficacités dans le code. L'écriture d'un code efficace nécessite une pratique continue. Un bon moyen d'obtenir cette pratique est d'écrire plusieurs versions de votre code, puis de vider votre code dans l'assembly et de comparer ou de chronométrer / profiler votre code en fonctionnement pour avoir une idée de l'endroit où les inefficacités peuvent être. Amusez-vous bien.
Regardez les choses et faites-moi savoir si vous avez d'autres questions.
Je pense avoir compris l'essentiel de votre commentaire, mais une question: comment faire pour transférer leur code de code dans l'assembly?
Vous faites cela avec le compilateur, par exemple pour que gcc videra avec la syntaxe intel, vous utiliseriez gcc -S -masm = intel -o yourcode.asm yourcode.c (vous pouvez ajouter votre optimisation, par exemple -O [0-3 ] ou -Ofast pour voir la façon dont le compilateur optimise votre code en fonction du niveau d'optimisation).
Tout d'abord, je ne sais pas pourquoi vous avez besoin de la boucle
forcar elle se rompt à la1èreitération elle-même.Votre professeur a-t-il réellement profilé le code et constaté que les «ressources gaspillées» sont à la fois mesurables et importantes? Parce qu'il semble que votre professeur a perdu la trace de la raison pour laquelle nous utilisons les ordinateurs en premier lieu: faire les choses pour que nous n'ayons pas à le faire. Le code fonctionne et il semble facile à comprendre. Jusqu'à ce que quelqu'un démontre un problème de performance avec un peu de code, il n'y a AUCUN problème de performance ou de "gaspillage de ressources".
@AndrewHenle Je pense qu'il voulait dire en performance, alias que j'utilise trop strstr, ou quelque chose comme ça.
@Math_Seeker Et alors? C'est la réponse que votre professeur mérite. Et alors? Le plus dur dans l'écriture de code est de le faire fonctionner. Pour que cela fonctionne, il doit être compréhensible et maintenable. Souvent, la réponse la plus efficace à un code fonctionnant trop lentement ou utilisant trop de mémoire est d'acheter un ordinateur plus rapide ou simplement d'ajouter de la RAM. Le matériel est bon marché et fiable. Le travail humain coûte cher et crée des bugs.
@Math_Seeker Non seulement cela, si vous validez la saisie d'une adresse e-mail par l'utilisateur (ce qui ressemble à ce que vous faites ...), l'ordinateur exécutera tous ces appels
strstr ()en quoi? 300 microsecondes au lieu des 200 microsecondes qu'il faudrait pour exécuter le code plus complexe, non maintenable et sujet aux bogues que votre professeur semble pousser? Pensez-vous que l'utilisateur remarquera cette différence? Votre professeur doit consacrer du temps à faire fonctionner le code et être appelé à 3 heures du matin quand ce n'est pas le cas.@AndrewHenle Merci d'avoir pris le temps de répondre, je vais essayer de raisonner avec mon professeur.
@Math_Seeker Bien. Parce que vous ne faites pas d'analyse des performances et des ressources en regardant le code. Non, vous ne le faites pas. Lorsque l'une des façons dont vous gagnez votre vie en faisant fonctionner plus rapidement des systèmes haut de gamme à grande échelle, vous apprenez que regarder du code et essayer de prédire les problèmes de performances est une perte de temps. Vous exécutez le code et profilez - mesure les goulots d’étranglement. Ensuite, vous corrigez ceux . FWIW, l'utilisation répétée de
strstr ()sur la même chaîne est probablement assez efficace, en fait, car cette chaîne sera dans le cache le plus chaud - si un compilateur optimisé ne la réécrit pas totalement de toute façon.@AndrewHenle le professeur a raison, il y a une meilleure façon de faire le contrôle. Le code est un désordre, dans certaines conditions, il imprimera la même erreur plusieurs fois. Bien que je sois d'accord sur certaines parties de vos commentaires, je ne suis pas d'accord sur le résultat général. Vous répondez à un étudiant, laissez-le apprendre les bonnes pratiques.
Votre professeur a un boeuf valide. Vous apprenez à coder en C, si vous dites "meh, les ordinateurs sont assez rapides de toute façon" alors vous pourriez aussi bien apprendre Java. Il est assez facile de faire, au minimum utiliser strlen () une seule fois, stocker sa valeur de retour dans une variable. Vous pouvez également facilement éliminer plusieurs appels strstr () avec un seul strchr () pour trouver «@». C'est seulement l'exercice qui compte, le programme ne sera jamais utilisé.
@HansPassant le problème est que les sous-chaînes "@." et ". @" et ".." ne peuvent pas être dans la chaîne d'entrée, ce sont des paramètres. Donc j'utilise strstr trois fois, une fois pour chaque sous-chaîne, trouver "@" est assez simple, c'est fait dans une autre partie de mon code. Alors, y a-t-il un moyen de réduire la quantité de fonctions strstr ou non? comme une fonction implémentée par l'utilisateur? des conseils si possible ?. Merci pour votre temps!
La façon dont vous avez écrit les appels
strstr ()signifie que la réponse qu'ils donnent ne change pas à chaque itération de la boucle. Dans l'état actuel des choses, ces quatre tests pourraient être effectués avant la boucle. Même si vous utilisezstrstr (& email [i], "@."), vous n'obtiendrez toujours pas vraiment de différence dans les résultats si les chaînes verboten (". @" Code >,"@.","..") étaient absents - vous repéreriez éventuellement la différence s'ils étaient présents. On ne sait pas exactement à quoich1,ch2sont définis. Le test de ces caractères à la fin de la chaîne ne changera pas non plus à chaque itération. 6 tests sur 8 ne changent pas - c'est du gaspillage.@JonathanLeffler donc si je comprends bien, il vaut mieux mettre les tests qui ne changent pas avant la boucle, plutôt que de les mettre dans la boucle, ce qui serait inutile. ai-je bien compris?
C'est ma thèse; cela pourrait même être la thèse de votre professeur.
@JonathanLeffler Merci beaucoup pour la réponse, je vais contacter mon professeur et lui poser des questions à ce sujet plus en détail.
Hmmm; il est également possible de penser que vous ne pouvez pas savoir si l'adresse e-mail est valide tant que la boucle n'est pas terminée. Vous pouvez repérer les adresses invalides lorsque vous repérez le ou les caractères invalides; vous ne pouvez dire que l'adresse dans son ensemble est valide lorsque vous avez tout vérifié. Lorsque vous repérez un problème, vous pouvez imprimer et briser la boucle. Vous pouvez décider de simplement définir une variable (
bool isValid = true;avant la boucle;isValid = false; break;lorsque vous repérez un problème dans la boucle;if (isValid) printf ("OK \ n"); else printf ("Invalid \ n");après la boucle.@AndrewHenle: Peut-être que vous travaillez dans un domaine où les choses que vous dites sont vraies. Mais ceux-ci ne sont pas applicables ici pour deux raisons. Premièrement, il s'agit d'une situation d'enseignement, où des programmes simplifiés sont utilisés pour former des problèmes futurs. Le fait que
strstrne prenne que quelques dizaines de microsecondes n'est pas pertinent pour l'apprentissage des techniques d'optimisation qui s'appliquent lorsque vous travaillez avec du code qui prend des secondes, des heures ou des jours. C'est la technique, les connaissances qui sont précieuses ici, et non le temps de calcul des programmes étudiants. Deuxièmement, d'autres personnes travaillent dans d'autres domaines, où les choses que vous dites ne tiennent pas.@AndrewHenle: Par exemple, il y a des gens qui travaillent sur des logiciels qui sont exécutés un très grand nombre de fois, et qui économisent 200 microsecondes par exécution, plus que le temps qu'il leur faut pour optimiser. De plus, il n'est pas vrai qu'essayer de prévoir les problèmes de performances soit du temps perdu. Encore une fois, cela peut être vrai dans un domaine dans lequel vous travaillez, mais ce n'est généralement pas le cas. Il est certainement utile pour les programmeurs de comprendre les effets du code qu'ils écrivent et de concevoir dans un souci d'optimisation.