1

I'm trying to read 3 different strings of max 15 characters each. When I try to read them with scanf("%s %s %s", a, b, c), only the last one is picked up (I'm asuming the spaces between every string has something to do with this).

#include <iostream>
#include <string.h>

using namespace std;

#define DIM 15

int main()
{
    char a[DIM], b[DIM], c[DIM];

    scanf("%s %s %s", a,b,c);
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    cout << "Cadenes introduides: " << a << " " << b << " " << c << endl;
}

the input is CadenaDe15chars CadenaDe15chars CadenaDe15chars

And I'm only seeing



CadenaDe15chars
Cadenes introduides:   CadenaDe15chars

when the actual output should be

CadenaDe15chars
CadenaDe15chars
CadenaDe15chars
Cadenes introduides: CadenaDe15chars CadenaDe15chars CadenaDe15chars

I'm kinda new to c++ so I don't really know how to make scanf ignore the whitespace, I've found examples with strings delimited by a new line \n, but not with a space.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
quiquelhappy
  • 396
  • 2
  • 6
  • 16
  • 2
    `#define DIM 15` is not large enough to hold 15 characters plus the end of string `\0`. – 001 Jan 31 '22 at 21:17
  • *Too-Short-By-1* failure. Also note `"%s %s %s"` is equivalent to `"%s%s%s"`. The `"%s"` format specifier discards leading whitespace on its own. (the bitter issue is why are you using `scanf()` in C++ when `std::cin` or `std::getline()` are preferred. – David C. Rankin Jan 31 '22 at 21:18
  • @JohnnyMopp is there any way I can ignore \0 so I can safely store the 15 characters with scanf? or I should code a function to read the input until I get a \0? – quiquelhappy Jan 31 '22 at 21:19
  • 1
    No. C-style strings require it. Why not use the C++ `std::string`? – 001 Jan 31 '22 at 21:20
  • @JohnnyMopp sadly it is an uni assignment and I have to use char[x] – quiquelhappy Jan 31 '22 at 21:22
  • Then use a sufficiently sized buffer. `#define DIM 256`. Never skimp on buffer size. And if using `scanf()` you must use the *field-width* modifier to prevent against buffer overrun `"%255s %255s %255s"`. Otherwise, `scanf()` with `"%s"` is no safer than `gets()` -- See [Why gets() is so dangerous it should never be used!](https://stackoverflow.com/q/1694036/3422102) – David C. Rankin Jan 31 '22 at 21:23
  • @DavidC.Rankin I can't increase the size of the array in this context – quiquelhappy Jan 31 '22 at 21:23
  • 1
    You can try `scanf("%14s %14s %14s", a,b,c);` but data can get lost. – 001 Jan 31 '22 at 21:24
  • 1
    Don't use unnecessary macros. Use variable for this. – eerorika Jan 31 '22 at 21:25
  • 1
    Then `"%14s %14s %14s"` will protect an array of 15 chars by limiting the number of characters written to 14 plus the nul-terminating character. You can also use [std::basic_istream::getline](https://en.cppreference.com/w/cpp/io/basic_istream/getline) but note the conditions where `failbit` will be set if `count` is set. – David C. Rankin Jan 31 '22 at 21:26
  • 1
    Give the teacher what they want so you can pass the class, but note this isn't how real-world C++ programmers will solve the problem unless facing significant resource constraints. – user4581301 Jan 31 '22 at 21:32
  • @user4581301 yeah I know, honestly programming classes on an scenario like this usually differ from actual real world code. I'm trying to read char by char, since %14s is actually making me lose characters for some reason – quiquelhappy Jan 31 '22 at 21:38
  • That's what `%14s` does. It prevents a user from ing up the program by trying to stick too many characters in too small a space by stopping and dumping excess characters. Never trust a user. The ones that aren't trying to hack your program for fun and profit are dropping typos. – user4581301 Jan 31 '22 at 21:42
  • 1
    You can read char-by-char but it won't help when it comes to printing the strings. `cout` will print chars until it finds a `\0`. That's how it knows to stop. If your array does not have that you may get unexpected results. You _must_ either increase the buffer size or lose characters. Or, I guess, print the strings char-by-char as well. – 001 Jan 31 '22 at 21:46
  • 1
    @JohnnyMopp "*`cout` will print chars until it finds a `\0`. That's how it knows to stop*" - more accurately, `cout` itself doesn't care about `'\0'` at all, it is the overloaded `operator<<` that takes in a `const char*` which needs `'\0'`. You can use `cout.write()` instead to work around that. – Remy Lebeau Jan 31 '22 at 21:55

2 Answers2

1

This call

scanf("%s %s %s", a,b,c);

invokes undefined behavior because at least this input "CadenaDe15chars" contains 15 characters. So the appended terminating zero character '\0' will be written by the function outside the corresponding array used as an argument.

You should at least declare the macro constant like

#define DIM 16

to reserve space in the arrays for potentially appended zero character.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Given your constraints (no 0-termination), I would read the input char-by-char, adding them to an appropriate array and processing white-space character to go to the next array.

Note that the array char a[DIM] does NOT have to be 0-terminated. However, you won't be able to use it and a C-string APIs, including cin << ...

Code example. You can read a line into the std::string variable first.

#include <iostream>
#include <string>

const int DIM = 15;

int main()
{
    char a[DIM], b[DIM], c[DIM];
    int a_len(0), b_len(0), c_len(0);

    std::string s = "CadenaDe15chars CadenaDe15chars CadenaDe15chars";

    int ind = 0;
    for (; a_len < 15; ++a_len, ++ind) {
        if (std::isspace(s[ind]))
            break;
        a[a_len] = s[ind];
    }
    while (std::isspace(s[ind]))
        ++ind;

    for (int i = 0; i < a_len; ++i)
        std::cout << a[i];
    std::cout << std::endl;
}

Repeat for b and c, or use two-dimensional array.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27