2

how to scan IP Adress with scanf in c?

I already user:

scanf(" %d ", &ip_adress)

Should i use

%d.%d.%d

or there is a easier way.

This is my function for reading information from a file:

void read_tree_from(FILE *in, PC **root){
    char name[MAX_NAME];
    int ip_adress;
    int speed;
    while(fscanf(in, "%s %d %d", name, &ip_adress, &speed) != EOF){
        PC *new = create_new_item(name, ip_adress, speed);
        add_to_tree(new, root);
    }
}

But when i run the program it works fine but in output file its mess...

Alex Bravo
  • 1,601
  • 2
  • 24
  • 40
Stefan
  • 375
  • 1
  • 9
  • 24
  • `fgets` and then parse. – Eugene Sh. Jan 10 '18 at 21:50
  • 2
    `scanf("%d.%d.%d.%d", &a, &b, &c, &d);` would work, but that would also accept entries like `999.99.999.99999` which is **not** an IP address. If you are using a unix like system, you could also use [`inet_pton`](http://man7.org/linux/man-pages/man3/inet_pton.3.html) instead. – Pablo Jan 10 '18 at 21:55
  • Show us the content of your file, at least one line to see the input. – Pablo Jan 10 '18 at 22:03
  • 1
    RACUNAR1 52.123.213.11 100. This is input the name of a pc , its ip adress and his internet speed – Stefan Jan 10 '18 at 22:06
  • Rather than compare the output with `fscanf(in, "%s %d %d", ....) != EOF` as it is 1 value code does not want, compare to the number of desired successful conversions with `fscanf(in, "%s %d %d", name, &ip_adress, &speed) == 3`. – chux - Reinstate Monica Jan 10 '18 at 22:55

3 Answers3

5

You could use format specifier %hhu.%hhu.%hhu.%hhu, which reads in unsigned 8 bit values, and you could directly read into the 4 bytes of your int-representation of an ip-address, respectively:

int main () {

    const char* testLine = "aName 192.168.112.54 100";

    char name[100];
    int speed;

    volatile uint32_t ipAddress=0;
    unsigned char* ipAddressBytes = (unsigned char*)&ipAddress;


    if (sscanf(testLine, "%s %hhu.%hhu.%hhu.%hhu %d", name, ipAddressBytes+3, ipAddressBytes+2,ipAddressBytes+1,ipAddressBytes+0, &speed) == 6) {
        printf("%s %08X %d\n",name, ipAddress, speed);
    }

    return 0;   
}

Output:

aName C0A87036 100

Format %hhu reads in an integral value and maps it to 8 bits; hence, it does not check if the input concerning the IP-address is valid in the sense that 1025.168.112.54 would not be valid. However, it avoids technical overflows in that any part exceeding 8 bits is simply ignored. So before mentioned input would yield 01A87036.

Note the volatile-specifier, which tells the compiler that it shall not optimize access to this variable; otherwise, the compiler might assume that - since the value is not obviously changed - the variable is unchanged / not initialized.

Note further that you have to take care whether your environment is of big endian or little endian architecture.

Hope it helps.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • `ipAsArray+3` incurs an endian dependent solution. Recommend `uint8_t ipAddress[4];` instead. – chux - Reinstate Monica Jan 10 '18 at 22:58
  • @chux: agree in general; yet it seemed to me that OP wants to use an `int` type, probably due to a given interface `create_new_item(name, ip_adress, speed)`; But maybe my assumption is wrong; then `ipAddress[4]` is more independent, of course. – Stephan Lechner Jan 10 '18 at 23:03
  • 1
    I suspect this will work for OP, yet that pesky anti-aliasing rule comes into play. Code does not need to assume `ipAddress` changed due to `sscanf(testLine, "%s %hhu....", name, ipAsArray+3, ...` and could attempt to print uninitialized data. – chux - Reinstate Monica Jan 10 '18 at 23:20
  • Formally, the [`scanf()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html) spacification says that using `%hhu` to convert a value too big to fit in an `unsigned char` leads to undefined behaviour. (_If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the space provided, the behavior is undefined._) Also, for whatever it is worth, `%hhu.%hhu.%hhu.%hhu` will convert `" -234. +379. 456. 9"` because the `%hhu` will skip blanks and handle signs, etc. – Jonathan Leffler Jan 11 '18 at 05:55
  • @chux: good point concerning uninitialized data, thanks. Addressed the issue by making `ipAddress` volatile. – Stephan Lechner Jan 12 '18 at 21:00
  • @Jonathan Leffler: is `%hhu`-specifier together with an input like `1025` really UB? I thought that the input sequence *"shall be converted to a type appropriate to the conversion character."*, and the properly converted value is written to the respective object. Corresponding to `%hhu`, `1025` would be converted to an 8 bit unsigned value first, so no overflow. Otherwise any use of `scanf`with `%d, %u, %hhu, ...` could possibly lead to UB purely depending on the input provided, and the programmer would have to check input first before using `scanf` in order to be safe. Are you sure? – Stephan Lechner Jan 12 '18 at 21:08
  • Concerning "Corresponding to %hhu, 1025 would be converted to an 8 bit unsigned value first, so no overflow." I do not think so. `"%hhu"` corresponds to a pointer `unsigned char *`. Why would 1025 attempt to convert to an`unsigned` first and not directly to `unsigned char`? (which fails due to outside `unsigned char` range - might take on the value 255) – chux - Reinstate Monica Jan 12 '18 at 21:26
  • `volatile` --- interesting way to solve anti-aliasing (AA). – chux - Reinstate Monica Jan 12 '18 at 21:28
  • @chux: maybe I expressed it wrong; I don't think that `1025` will ever be converted to an `unsigned` value, but always to an `unsigned char`-value (i.e. `1025` for `%hhu` never yields `0x401`, but always `0x01`); Right? – Stephan Lechner Jan 12 '18 at 22:00
  • @StephanLechner "1025 for %hhu ... yields ...always 0x01); Right?" "Always" is a long time. I would not agree with "always 0x01". %u --> says "Matches ... decimal integer, whose format is the same as expected for the subject sequence of the `strtoul` function ..." That `strtoul` looks like a mistake in the C spec. as `%llu` certainly means to use `strtoull()`. So it is reasonable to extend that to mean could %hhu use a theoretical `strtouchar()` when `"1025"` --> 255. Perhaps post as a question? By extension, I doubt `"0x1234567890...12345678900000042"` and `"%hhu"` must go to 0x42. – chux - Reinstate Monica Jan 12 '18 at 22:15
  • A common implementation would be to define that over-large values presented to `%hhu` are treated modulo 256, ignoring any high-order bits. That's a perfectly valid response to undefined behaviour. I'd be hard-pressed to think of an implementation that wouldn't behave like that, but the standard doesn't say that an implementation must do that. It simply says that it (the standard) doesn't define what happens. It's one of the many messy areas in standard C. The standard avoids over-prescribing what implementations must do, which means the programmers must be careful with what they assume. – Jonathan Leffler Jan 12 '18 at 23:24
1

How to scan IP Adress with fscanf

With line data, fgets() has many advantages, including better error recovery than fscanf(), so a fgets/sscanf() solution

void read_tree_from(FILE *in, PC **root){
  char buffer [MAX_NAME + 50];
  // Read a line of input
  if (fgets(buffer, sizeof buffer, in)) {
    int ip_address;
    int speed;
    unsigned char octet[4];
    int n = 0;
    int namelen; 

    // This approach uses %n to record the scan offset - if it got that far 
    // %*s%n to scan over the name, all tht is needed is how long it is
    // %hhu to scan an `unsigned char`
    // " ." to scan spaces and then a '.'
    sscanf(buffer, "%*s%n %hhu .%hhu .%hhu .%hhu %d %n",
      &namelen, &octet[0], &octet[1], &octet[2], &octet[3], &speed, &n);

    // When `n > 0`, the complete scan occurred
    // buffer[n] != '\0' detects extra junk after the speed
    if (n == 0 || buffer[n] != '\0') {
      // failed scan
      return;
    }

    // Check data for validity 
    if (namelen >= MAX_NAME) {
      // name too long
      return;
    }

    buffer[namelen] = '\0';  // \0 terminate the name

    // form IP address.  I'd recommend `uint32_t` rather than `int`
    // The `1u*` insures code deals with unsigned promotions and shifts.  No sign extend
    ip_address = 1u*octet[0] << 24 | 1u*octet[1] << 16 | 1u*octet[2] << 8 | octet[3];

    PC *new = create_new_item(buffer, ip_adress, speed);
    add_to_tree(new, root);
    }
  }
}

or there is a easier way (?)

Yes, if code does not care about detecting ill-form data, many short-cuts are available. Production code checks for bad data. Robust checking would take the buffer and look for numeric values out of range, (maybe with strtoul), etc.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

I tried this the first time but it didnt work probably bcs i had some error somewhere else in the code and i tought this was the part that making the eror.But it worked when i fixed some mistakes in my code.

while(fscanf(in, "%d %s %s %d", tmp_number, tmp_name, tmp_ip, &tmp_speed) != EOF){ RACUNAR *new = create_new_item(tmp_number, tmp_name, tmp_ip, tmp_speed); add_to_tree(new, root);

Stefan
  • 375
  • 1
  • 9
  • 24