2015-11-17 1 views
0

J'utilise la fonction getline() dans le programme ac pour lire les lignes d'un fichier, placer les informations lues dans une structure, créer un thread avec cette structure comme argument, et répéter . Cependant, il getline() renvoie -1 avant l'EOF, après le deuxième ensemble d'arguments sous "id: test2" dans le fichier fourni.
Depuis print errno dans gdb renvoie 0, je suppose qu'il n'y a pas d'erreur et getline() semble croire qu'il a atteint EOF.
La partie create thread a été mise en commentaire car elle n'a aucun rapport avec le problème rencontré.C getline() renvoie -1 avant EOF

Le code:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <signal.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <pthread.h> 
#include <sys/time.h> 

#define BUFFER_SIZE 256 

time_t cur; 
char* keypath = "home/chy/.ssh/id_rsa"; 
int logfreq = 1; 
int hashfreq = 180; 

struct getArgs 
{ 
    const char* id; 
    const char* hostaddr; 
    uint16_t port; 
    const char* uname; 
    const char* path; 
}; 

int main(int argc, char **argv) 
{ 
    int th_max = 5; 
    int th_count = 0; 
    pthread_t* ths = (pthread_t*) malloc(th_max * sizeof(pthread_t)); 

    FILE* fp; 
    fp = fopen("config.txt", "r"); 

    char* line = NULL; 
    char buf[128]; 
    int r = 0; 
    size_t len = 0; 
    ssize_t read; 

    system("mkdir logs && chmod -R a+r logs"); 

    while(1) 
    { 
    pthread_t th; 
//Get ID 
    while((read = getline(&line, &l, fp)) != -1) 
    { 
     if(!(read > 0)) 
     continue; 
     if(sscanf(line, "id: %[^\n]\n", buf) == 1) 
     break; 
     if(sscanf(line, "keypath: %[^\n]\n", buf) == 1) 
     keypath = strdup(buf); 
     else if(sscanf(line, "logfreq: %d\n", &r) == 1) 
     logfreq = r; 
     else if(sscanf(line, "hashfreq: %d\n", &r) == 1) 
     hashfreq = r; 
    } 
    if(read == -1) 
     break; 
    struct getArgs* args = (struct getArgs*)malloc(sizeof(struct getArgs)); 
    args->id = strdup(buf); 
//Get Host Address 
    if(getline(&line, &len, fp) == -1) 
    { 
     free_args(args); 
     break; 
    } 
    if(sscanf(line, "hostaddr: %[^\n]\n", buf) != 1) 
    { 
     free_args(args); 
     continue; 
    } 
    args->hostaddr = strdup(buf); 
//Get Port Number 
    if(getline(&line, &len, fp) == -1) 
    { 
     free_args(args); 
     break; 
    } 
    if(sscanf(line, "port: %d\n", &r) != 1) 
    { 
     free_args(args); 
     continue; 
    } 
    args->port = r; 
//Get Username 
    if(getline(&line, &len, fp) == -1) 
    { 
     free_args(args); 
     break; 
    } 
    if(sscanf(line, "username: %[^\n]\n", buf) != 1) 
    { 
     free_args(args); 
     continue; 
    } 
args->uname = strdup(buf); 
//def Path 
    if(getline(&line, &len, fp) == -1) 
    { 
     free_args(args); 
     break; 
    } 
    if(sscanf(line, "path: %[^\n]\n", buf) != 1) 
    { 
     free_args(args); 
     continue; 
    } 
    args->path = strdup(buf); 

// int err = pthread_create(&th, NULL, &getFiles, args); 

    if(th_count > th_max) 
    { 
     th_max *= 2; 
     ths = (pthread_t*)realloc(ths, th_max * sizeof(pthread_t)); 
    } 
    *(ths+th_count * sizeof(pthread_t)) = th; 
    th_count++; 
    } 

    fclose(fp); 

    while(1); 
} 

Le fichier:

keypath: /home/username/.ssh/id_rsaNOPASSWORD 
id: test1 
hostaddr: XXX.XXX.XXX.XXX 
port: 22 
username: hpc 
path: /home/hpc/ 
id: test2 
hostaddr: XXX.XXX.XXX.XXX 
port: 22 
username: hpc 
path: /home/hpc/ 
id: test3 
hostaddr: XXX.XXX.XXX.XXX 
port: 22 
username: hpc 
path: /home/hpc/ 
id: test4 
hostaddr: XXX.XXX.XXX.XXX 
port: 3844 
username: uname 
path: /home/uname/hpc/ 

Le contenu du pointeur de fichier avant le dernier getline():

{_flags = -72539000, 
    _IO_read_ptr = 0x7ffff7ff80d0 " \nid: test3\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test4\nhostaddr: XXX.XXX.XXX.XXX\nport: 3844\nusername: uname\npath: /home/uname/hpc/\n\n", 
    _IO_read_end = 0x401d10 "H\211l$\330L\211d$\340H\215-\277\006 ", 
    _IO_read_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_buf_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., _IO_buf_end = 0x7ffff7ff9000 "P\220\377\367\377\177", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7bbb880, _fileno = 7, _flags2 = 0, _old_offset = 0, _cur_column = 0, 
    _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x603120, _offset = -1, __pad1 = 0x0, __pad2 = 0x603130, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>} 

Après:

{_flags = -72538984, 
    _IO_read_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_read_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_read_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_write_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., 
    _IO_buf_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., _IO_buf_end = 0x7ffff7ff9000 "P\220\377\367\377\177", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7bbb880, _fileno = 7, _flags2 = 0, _old_offset = 0, _cur_column = 0, 
    _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x603120, _offset = -1, __pad1 = 0x0, __pad2 = 0x603130, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>} 

J'ai également tenté d'implémenter la même chose avec fgets() au lieu de getline(), mais avec les mêmes résultats.
Qu'est-ce qui pourrait causer ce problème?

Édition:
J'ai exécuté le code sur le même fichier, sauf sans espaces/lignes vides excessives et en changeant l'ordre pour voir si quelque chose est affecté, mais les résultats sont les mêmes.
Je peux également confirmer que la structure de test1 et test2 est correctement remplie.

{id = 0x6032e0 "test1", hostaddr = 0x603300 "XXX.XXX.XXX.XXX", port = 22, uname = 0x603320 "hpc", path = 0x603340 "/home/hpc/"} 
{id = 0x603390 "test2", hostaddr = 0x6033b0 "XXX.XXX.XXX.XXX", port = 22, uname = 0x6033d0 "hpc", path = 0x6033f0 "/home/hpc/"} 

modifications: changement de version à code et fichier

+0

Où sont déclarées les autres variables? 'keypath',' logfreq', etc. Les globals sont-ils? –

+0

Oui, je les ai édités pour plus de clarté. –

+0

Compilez-vous avec * Avertissements * activé? (c'est-à-dire avec "-Wall -Wextra" dans votre chaîne de compilation?) La raison pour laquelle je vous demande est que vous avez des discordances 'type' signées/unsinged dans vos chaînes' sscanf'). Votre problème avec 'EOF' est très probablement de' sscanf', par ex. 'EOF' est retourné si la fin de l'entrée est atteinte avant que la * première conversion réussie * ou une * erreur de correspondance * se produise. Pour confirmer, placez 'getline' et' sscanf' sur des lignes différentes dans différents tests 'if' à isoler. –

Répondre

1

En plus du non signé/signétype discordances discutés dans les commentaires, la question principale que vous aviez était la logique contradictoire utilisé dans les tests, et de multiples appels vaguement organisés à getline et sscanf où vous testerait if ((read = getline ... || sscanf ...)getline ou sscanf (en cas d'erreur d'entrée ou de correspondance) pourrait renvoyer EOF (-1) sans indiquer le problème. Pour nettoyer la logique, vous voulez approcher la lecture du fichier de configuration où vous (1) lire la ligne - une fois, (2) parse tag et la valeur, puis (3) tester tag et value et prendre le nécessaire actes. En m'appuyant sur cette approche, et en ignorant le code de threads non pertinent à votre problème de lecture, j'ai débogué/réécrit votre routine d'entrée pour vous donner un exemple d'approche de la lecture et de la séparation. Ce faisant, j'ai rassemblé tous les paramètres dans un tableau de pointeurs à struct (par ex.struct getArgs **args;). Vous n'avez peut-être pas besoin de collecter chacun dans un tableau de structures avec votre schéma pthread, mais à des fins d'exemple, ce changement a été fait.

J'ai également inclus un deuxième exemple ci-dessous qui montre une approche supplémentaire avec un peu plus de vérification pour s'assurer que vos valeurs sont lues dans id, hostaddr, port, nom d'utilisateur et chemin groupes. Le premier exemple simple traitera de sauter des lignes vides dans le fichier d'entrée:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdint.h> 

#define NARGS 32 
#define TSIZE 16 
#define VSIZE 256 

char *keypath = "home/chy/.ssh/id_rsa"; 
int logfreq = 1; 
int hashfreq = 180; 

struct getArgs { 
    const char *id; 
    const char *hostaddr; 
    uint16_t port; 
    const char *uname; 
    const char *path; 
}; 

void *xcalloc (size_t n, size_t s); 
void *xrealloc_dp (void *ptr, size_t *n); 

int main (int argc, char **argv) 
{ 
    struct getArgs **args = NULL; 
    char *line = NULL; 
    size_t len = 0; 
    size_t idx = 0; 
    size_t nargs = NARGS; 
    size_t i; 
    ssize_t read; 
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : fopen ("config.txt", "r"); 

    if (!fp) { fprintf (stderr, "file open failed.\n"); return 1; } 

    /* allocate NARGS pointer to struct getArgs */ 
    args = xcalloc (NARGS, sizeof *args); 

    /* read each line in file */ 
    while ((read = getline (&line, &len, fp)) != -1) { 

     if (read == 1) continue; /* skip blank lines */ 

     char tag[TSIZE] = {0}; 
     char val[VSIZE] = {0}; 

     /* separate tag and value (val) */ 
     if (sscanf (line, "%s %[^\n]%*c", tag, val) != 2) { 
      fprintf (stderr, "error: sscanf conversion failed.\n"); 
      break; 
     } 

     /* handle keypath, logfreq, hashfreq */ 
     if (strcmp (tag, "keypath:") == 0) { 
      keypath = strdup (val); 
      continue; 
     } 

     if (strcmp (tag, "logfreq:") == 0) { 
      logfreq = (int)strtol (val, NULL, 10); 
      continue; 
     } 

     if (strcmp (tag, "hashfreq:") == 0) { 
      hashfreq = (int)strtol (val, NULL, 10); 
      continue; 
     } 

     /* allocate space for args[idx] if "id:", then 
     handle id, hostaddr, port, username, path */ 
     if (strcmp (tag, "id:") == 0) { 
      args[idx] = calloc (1, sizeof **args); 
      args[idx]->id = strdup (val); 
      continue; 
     } 

     if (strcmp (tag, "hostaddr:") == 0) { 
      args[idx]->hostaddr = strdup (val); 
      continue; 
     } 

     if (strcmp (tag, "port:") == 0) { 
      args[idx]->port = (uint16_t)strtoul (val, NULL, 10); 
      continue; 
     } 

     if (strcmp (tag, "username:") == 0) { 
      args[idx]->uname = strdup (val); 
      continue; 
     } 

     /* increment idx on path */ 
     if (strcmp (tag, "path:") == 0) 
      args[idx++]->path = strdup (val); 

     if (idx == nargs) /* check idx, realloc */ 
      args = xrealloc_dp (args, &nargs); 
    } 
    fclose (fp); 

    printf ("\n keypath : %s\n logfreq : %d\n hashfreq : %d\n", 
      keypath, logfreq, hashfreq); 

    for (i = 0; i < idx; i++) 
     printf ("\n id  : %s\n hostaddr : %s\n port  : %hu\n" 
       " username : %s\n path  : %s\n", args[i]->id, args[i]->hostaddr, 
       args[i]->port, args[i]->uname, args[i]->path); 

    for (i = 0; i < idx; i++) 
     free (args[i]); 
    free (args); 

    return 0; 
} 

/* calloc with error check, exit on failure */ 
void *xcalloc (size_t n, size_t s) 
{ 
    register void *memptr = calloc (n, s); 
    if (memptr == 0) { 
     fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n"); 
     exit (EXIT_FAILURE); 
    } 

    return memptr; 
} 

/* reallocate memory for a double-pointer from 'n' to 2 * 'n' 
* returns pointer to reallocated block on success, exit on 
* failure 
*/ 
void *xrealloc_dp (void *ptr, size_t *n) 
{ 
    void **p = ptr; 
    void *tmp = realloc (p, 2 * *n * sizeof tmp); 
    if (!tmp) { 
     fprintf (stderr, "xrealloc_dp() error: virtual memory exhausted.\n"); 
     exit (EXIT_FAILURE); 
    } 
    p = tmp; 
    memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */ 
    *n *= 2; 

    return p; 
} 

Les xcalloc et xrealloc_dp fonctions ne sont que des fonctions qui font la vérification des erreurs pour calloc et realloc (pour double pointeur) pour maintenir le corps principal et logique du code clair. J'ai couru le code sur l'entrée de test suivant et reçu le résultat suivant:

Fichier de test d'entrée (avec blanc lignes)

$ cat ../dat/idhostaddrport.txt 
keypath: /home/username/.ssh/id_rsaNOPASSWORD 
logfreq: 2 
hashfreq: 250 

id: test1 
hostaddr: XXX.XXX.XXX.XXX 
port: 221 
username: hpc1 
path: /home/hpc1/ 

id: test2 
hostaddr: XXX.XXX.XXX.XXX 
port: 222 
username: hpc2 
path: /home/hpc2/ 

id: test3 
hostaddr: XXX.XXX.XXX.XXX 
port: 223 
username: hpc3 
path: /home/hpc3/ 

id: test4 
hostaddr: XXX.XXX.XXX.XXX 
port: 3844 
username: uname 
path: /home/uname/hpc/ 

Sortie

$ ./bin/getline_sscanf_dbg ../dat/idhostaddrport.txt 

keypath : /home/username/.ssh/id_rsaNOPASSWORD 
logfreq : 2 
hashfreq : 250 

id  : test1 
hostaddr : XXX.XXX.XXX.XXX 
port  : 221 
username : hpc1 
path  : /home/hpc1/ 

id  : test2 
hostaddr : XXX.XXX.XXX.XXX 
port  : 222 
username : hpc2 
path  : /home/hpc2/ 

id  : test3 
hostaddr : XXX.XXX.XXX.XXX 
port  : 223 
username : hpc3 
path  : /home/hpc3/ 

id  : test4 
hostaddr : XXX.XXX.XXX.XXX 
port  : 3844 
username : uname 

Une chose que vous devrait travailler vers des moyens de valider davantage votre contribution. Assurez-vous que si vous lisez un id, vous lisez également les valeurs restantes hostaddr à path pour chaque id. Une variante simple qui ajoute un contrôle minimal serait la modification suivante à la boucle de lecture:

/* read each line in file */ 
    while ((read = getline (&line, &len, fp)) != -1) { 

     if (read == 1) continue; /* skip blank lines */ 

     char tag[TSIZE] = {0}; 
     char val[VSIZE] = {0}; 

     /* separate tag and value (val) */ 
     if (sscanf (line, "%s %[^\n]%*c", tag, val) != 2) { 
      fprintf (stderr, "error: sscanf conversion failed.\n"); 
      break; 
     } 

     /* handle keypath, logfreq, hashfreq */ 
     if (strcmp (tag, "keypath:") == 0) { 
      keypath = strdup (val); 
      continue; 
     } 

     if (strcmp (tag, "logfreq:") == 0) { 
      logfreq = (int)strtol (val, NULL, 10); 
      continue; 
     } 

     if (strcmp (tag, "hashfreq:") == 0) { 
      hashfreq = (int)strtol (val, NULL, 10); 
      continue; 
     } 

     /* allocate space for args[idx] if "id:", then 
     handle id, hostaddr, port, username, path */ 
     if (strcmp (tag, "id:") == 0) { 

      args[idx] = calloc (1, sizeof **args); 
      args[idx]->id = strdup (val); 
      size_t tagseq = 0; 

      while ((read = getline (&line, &len, fp)) != -1) { 

       if (read == 1) continue; /* skip blank lines */ 

       /* separate tag and value (val) */ 
       if (sscanf (line, "%s %[^\n]%*c", tag, val) != 2) { 
        fprintf (stderr, "error: sscanf conversion failed.\n"); 
        break; 
       } 

       if (strcmp (tag, "hostaddr:") == 0) { 
        if (tagseq != 0) { 
         fprintf (stderr, "error: tagseq failed for hostaddr.\n"); 
         exit (EXIT_FAILURE); 
        } 
        args[idx]->hostaddr = strdup (val); 
        tagseq++; 
        continue; 
       } 

       if (strcmp (tag, "port:") == 0) { 
        if (tagseq != 1) { 
         fprintf (stderr, "error: tagseq failed for port.\n"); 
         exit (EXIT_FAILURE); 
        } 
        args[idx]->port = (uint16_t)strtoul (val, NULL, 10); 
        tagseq++; 
        continue; 
       } 

       if (strcmp (tag, "username:") == 0) { 
        if (tagseq != 2) { 
         fprintf (stderr, "error: tagseq failed for username.\n"); 
         exit (EXIT_FAILURE); 
        } 
        args[idx]->uname = strdup (val); 
        tagseq++; 
        continue; 
       } 

       /* increment idx on path */ 
       if (strcmp (tag, "path:") == 0) { 
        if (tagseq != 3) { 
         fprintf (stderr, "error: tagseq failed for path.\n"); 
         exit (EXIT_FAILURE); 
        } 
        args[idx++]->path = strdup (val); 
        break; 
       } 
      } 
     } 

     if (idx == nargs) /* check idx, realloc */ 
      args = xrealloc_dp (args, &nargs); 
    } 
    fclose (fp); 

Jetez un oeil à la fois et laissez-moi savoir si vous avez des questions supplémentaires.

3

La ligne

sscanf(line, "id: %[^\n]\n", buf) != 1 

ne correspondra pas à une ligne vide, ce qui provoque votre boucle de lecture pour sortir de la synchronisation à ce moment-là. D'autres problèmes tombent en cascade à partir de là lorsque l'ID: test2 est englouti, et finalement vous frappez EOF.

[  Matches a nonempty sequence of characters ... 
+0

Merci, mais j'ai essayé d'exécuter le même code sur un fichier de configuration sans espaces excessifs/newlines, et les résultats sont les mêmes. Je vais clarifier en tant que tel dans le premier poste. –

+1

Que voulez-vous dire par * pas d'espaces excessifs/de nouvelles lignes *? Tout ce qu'il faut est '1 ligne vide' (ou deux' newlines' d'affilée), et le problème se produit. Vous devez probablement déplacer 'sscanf' * dans * votre boucle de lecture et tester' if (read> 0) 'avant d'appeler' sscanf'. –

+0

@ DavidC.Rankin Je ne veux pas dire qu'il n'y a pas de double-ligne double.Juste pour m'assurer, j'ai fait comme vous l'avez suggéré, et les résultats étaient les mêmes. –