Forcing format_to_n to use terminating zero - c++

Beside most common (format) function C++20 also comes with format_to_n that takes output iterator and count.
What I am looking for is the way to make sure that in case I ran out of space that my string is still zero terminated.
For example I want the following program to output 4 instead of 42.
#include<string>
#include<iostream>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
void f(char* in){
fmt::format_to_n(in, 2,"{}{}", 42,'\0');
std::cout << in;
}
int main(){
char arr[]= "ABI";
f(arr);
}
Is this possible without me manually doing the comparison of number of written chars and max len I provided to function?
If you are wondering why I use '\0' as an argument:
I have no idea how to put terminating char in format string.
note: I know that for one argument I can specify max len with :. but I would like a solution that works for multiple arguments.

format_to_n returns a result. You can use that struct:
void f(char* in){
auto [out, size] = fmt::format_to_n(in, 2, "{}", 42);
*out = '\0';
std::cout << in;
}
Note that this might write "42\0" into in, so adjust your capacity as appropriate (2 for a buffer of size 3 is correct).

format_to_n returns a struct containing, among other things, the iterator past the last character written. So it's quite easy to simply check the difference between that iterator and the original iterator against the maximum number of characters, and insert a \0 where appropriate:
void f(char* in)
{
const max_chars = 2;
auto fmt_ret = fmt::format_to_n(in, max_chars,"{}", 42);
char *last = fmt_ret.out;
if(last - in == max_chars)
--last;
*last = '\0';
std::cout << in;
}
Note that this assumes that the array only holds exactly the number of characters (including the NUL terminator) as the number you attempted to pass to format_to_n. The above code will therefore overwrite the last character written with a NUL terminator, essentially doing further truncation.
If instead you pass to format_to_n the number of characters in the array - 1, then you can simply always write the NUL terminator to fmt_ret.out itself.

Related

How to convert a std::string which contains '\0' to a char* array?

I have a string like,
string str="aaa\0bbb";
and I want to copy the value of this string to a char* variable. I tried the following methods but none of them worked.
char *c=new char[7];
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
strcpy(c,str.data()); // c="aaa"
str.copy(c,7); // c="aaa"
How can I copy that string to a char* variable without loosing any data?.
You can do it the following way
#include <iostream>
#include <string>
#include <cstring>
int main()
{
std::string s( "aaa\0bbb", 7 );
char *p = new char[s.size() + 1];
std::memcpy( p, s.c_str(), s.size() );
p[s.size()] = '\0';
size_t n = std::strlen( p );
std::cout << p << std::endl;
std::cout << p + n + 1 << std::endl;
}
The program output is
aaa
bbb
You need to keep somewhere in the program the allocated memory size for the character array equal to s.size() + 1.
If there is no need to keep the "second part" of the object as a string then you may allocate memory of the size s.size() and not append it with the terminating zero.
In fact these methods used by you
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
str.copy(c,7); // c="aaa"
are correct. They copy exactly 7 characters provided that you are not going to append the resulted array with the terminating zero. The problem is that you are trying to output the resulted character array as a string and the used operators output only the characters before the embedded zero character.
Your string consists of 3 characters. You may try to use
using namespace std::literals;
string str="aaa\0bbb"s;
to create string with \0 inside, it will consist of 7 characters
It's still won't help if you will use it as c-string ((const) char*). c-strings can't contain zero character.
There are two things to consider: (1) make sure that str already contains the complete literal (the constructor taking only a char* parameter might truncate at the string terminator char). (2) Provided that str actually contains the complete literal, statement memcpy(c,str.data(),7) should work. The only thing then is how you "view" the result, because if you pass c to printf or cout, then they will stop printing once the first string terminating character is reached.
So: To make sure that your string literal "aaa\0bbb" gets completely copied into str, use std::string str("aaa\0bbb",7); Then, try to print the contents of c in a loop, for example:
std::string str("aaa\0bbb",7);
const char *c = str.data();
for (int i=0; i<7; i++) {
printf("%c", c[i] ? c[i] : '0');
}
You already did (not really, see edit below). The problem however, is that whatever you are using to print the string (printf?), is using the c string convention of ending strings with a '\0'. So it starts reading your data, but when it gets to the 0 it will assume it is done (because it has no other way).
If you want to simply write the buffer to the output, you will have to do this with something like
write(stdout, c, 7);
Now write has information about where the data ends, so it can write all of it.
Note however that your terminal cannot really show a \0 character, so it might show some weird symbol or nothing at all. If you are on linux you can pipe into hexdump to see what the binary output is.
EDIT:
Just realized, that your string also initalizes from const char* by reading until the zero. So you will also have to use a constructor to tell it to read past the zero:
std::string("data\0afterzero", 14);
(there are prettier solutions probably)

My program is giving different output on different machines..!

#include<iostream>
#include<string.h>
#include<stdio.h>
int main()
{
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
char str[10];
gets(str);
strcat(left,str);
puts(left);
return 0;
}
for any input it should concatenate 0000 with that string, but on one pc it's showing a diamond sign between "0000" and the input string...!
You append a possible nine (or more, gets have no bounds checking) character string to a three character string (which contains four character and no string terminator). No string termination at all. So when you print using puts it will continue to print until it finds a string termination character, which may be anywhere in memory. This is, in short, a school-book example of buffer overflow, and buffer overflows usually leads to undefined behavior which is what you're seeing.
In C and C++ all C-style strings must be terminated. They are terminated by a special character: '\0' (or plain ASCII zero). You also need to provide enough space for destination string in your strcat call.
Proper, working program:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
/* Size is 4 + 10 + 1, the last +1 for the string terminator */
char left[15] = "0000";
/* The initialization above sets the four first characters to '0'
* and properly terminates it by adding the (invisible) '\0' terminator
* which is included in the literal string.
*/
/* Space for ten characters, plus terminator */
char str[11];
/* Read string from user, with bounds-checking.
* Also check that something was truly read, as `fgets` returns
* `NULL` on error or other failure to read.
*/
if (fgets(str, sizeof(str), stdin) == NULL)
{
/* There might be an error */
if (ferror(stdin))
printf("Error reading input: %s\n", strerror(errno));
return 1;
}
/* Unfortunately `fgets` may leave the newline in the input string
* so we have to remove it.
* This is done by changing the newline to the string terminator.
*
* First check that the newline really is there though. This is done
* by first making sure there is something in the string (using `strlen`)
* and then to check if the last character is a newline. The use of `-1`
* is because strings like arrays starts their indexing at zero.
*/
if (strlen(str) > 0 && str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = '\0';
/* Here we know that `left` is currently four characters, and that `str`
* is at most ten characters (not including zero terminaton). Since the
* total length allocated for `left` is 15, we know that there is enough
* space in `left` to have `str` added to it.
*/
strcat(left, str);
/* Print the string */
printf("%s\n", left);
return 0;
}
There are two problems in the code.
First, left is not nul-terminated, so strcat will end up looking beyond the end of the array for the appropriate place to append characters. Put a '\0' at the end of the array.
Second, left is not large enough to hold the result of the call to strcat. There has to be enough room for the resulting string, including the nul terminator. So the size of left should at least 4 + 9, to allow for the three characters (plus nul terminator) that left starts out with, and 9 characters coming from str (assuming that gets hasn't caused an overflow).
Each of these errors results in undefined behavior, which accounts for the different results on different platforms.
I do not know why you are bothering to include <iostream> as you aren't using any C++ features in your code. Your entire program would be much shorter if you had:
#include <iostream>
#include <string>
int main()
{
std::string line;
std::cin >> line;
std::cout << "You entered: " << line;
return 0;
}
Since std::string is going to be null-terminated, there is no reason to force it to be 4-null-terminated.
Problem #1 - not a legal string:
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
String must end with a zero char, '\0' not '0'.
This causes what you describe.
Problem #2 - fgets. You use it on a small buffer. Very dangerous.
Problem #3 - strcat. Yet again trying to fill a super small buffer which should have already been full with an extra string.
This code looks an invitation to a buffer overflow attack.
In C what we call a string is a null terminated character array.All the functions in the string.h library are based on this null at the end of the character array.Your character array is not null terminated and thus is not a string , So you can not use the string library function strcat here.

Passing a character array to function | Strange error

Basically I have a buffer in which i am looking for various flags to read certain fields from a binary file format. I have file read into a buffer but as i started to write code to search the buffer for the flags i immediately hit a wall. I am a C++ noob, but here is what i have:
void FileReader::parseBuffer(char * buffer, int length)
{
//start by looking for a vrsn
//Header seek around for a vrns followed by 32 bit size descriptor
//read 32 bits at a time
int cursor = 0;
char vrsn[4] = {'v','r','s','n'};
cursor = this->searchForMarker(cursor, length, vrsn, buffer);
}
int FileReader::searchForMarker(int startPos, int eof, char marker[], char * buffer)
{
int cursor = startPos;
while(cursor < eof) {
//read ahead 4 bytes from the cursor into a tmpbuffer
char tmpbuffer[4] = {buffer[cursor], buffer[cursor+1], buffer[cursor+2], buffer[cursor+3]};
if (strcmp(marker, tmpbuffer)) {
cout << "Found: " << tmpbuffer;
return cursor;
}
else {
cout << "Didn't Find Value: " << marker << " != " << tmpbuffer;
}
cursor = cursor + 4;
}
}
my header looks like this:
#ifndef __FILEREADER_H_INCLUDED__
#define __FILEREADER_H_INCLUDED__
#include <iostream>
#include <fstream>
#include <sys/stat.h>
class FileReader {
public:
FileReader();
~FileReader();
int open(char *);
int getcode();
private:
void parseBuffer(char *, int);
int searchForMarker(int, int, char[], char *);
char *buffer;
};
#endif
I would expect to get back a match for vrsn with strcmp but my result looks like this
Didn't Find Value: vrsn != vrsn
Found:
It looks like it finds it on the second pass after its passed the char array i am looking for.
Relevant hexcode
Your problem is two-fold:
strcmp returns "0" on success, not on failure. Read the documentation.
strcmp expects null-terminated strings. You say that you have chosen non-terminated char arrays because that's what your DB library uses. Well, fine. But still, you are violating the requirements of strcmp. Use strncmp instead (which takes a length argument) or, preferably, actually write C++ and start using std::vector<char> and friends.
Shouldn't that be something like int FileReader::searchForMarker(...) { .... }?
For the second query, I guess the strcmp works when it has two null terminated strings as its arguments. For example str1[]="AAA"; and str2[]="AAA"; then strcmp() would be used as
if(strcmp(str1,str2)==0) which will return 0 to indicate that they are equal. In your case, the tmpbuffer that you have created is not a null terminated string unless you add \0 in the end.So you might want to add \0 in the end of your tmpbuffer to create a string of 'v' 'r' 'n' 's'.
char vrsn[4] = {'v','r','s','n'};
Contains only the 4 characters specified. There is no room for a null character at the end.
char tmpbuffer[4] = {buffer[cursor], buffer[cursor+1], buffer[cursor+2], buffer[cursor+3]};
Contains only the 4 characters from buffer. There is no room for a null character at the end.
Eventually you call:
if (strcmp(marker, tmpbuffer)) {
The strcmp() function expects each of its parameters to end with a null character ('\0'). It wants to work with strings, which are null terminated.
Since your data is not null terminated, you probably want to use memcmp() instead of strcmp().
Also, strcmp() returns zero when its arguments are equal, so the condition in the if statement is inverted. (Zero is false, everything else is true.) The memcmp() function will also return zero when its arguments are equal.

How to work with null pointers in a std::vector

Say I have a vector of null terminates strings some of which may be null pointers. I don't know even if this is legal. It is a learning exercise. Example code
std::vector<char*> c_strings1;
char* p1 = "Stack Over Flow";
c_strings1.push_back(p1);
p1 = NULL; // I am puzzled you can do this and what exactly is stored at this memory location
c_strings1.push_back(p1);
p1 = "Answer";
c_strings1.push_back(p1);
for(std::vector<char*>::size_type i = 0; i < c_strings1.size(); ++i)
{
if( c_strings1[i] != 0 )
{
cout << c_strings1[i] << endl;
}
}
Note that the size of vector is 3 even though I have a NULL at location c_strings1[1]
Question. How can you re-write this code using std::vector<char>
What exactly is stored in the vector when you push a null value?
EDIT
The first part of my question has been thoroughly answered but not the second. Not to my statisfaction at least. I do want to see usage of vector<char>; not some nested variant or std::vector<std::string> Those are familiar. So here is what I tried ( hint: it does not work)
std::vector<char> c_strings2;
string s = "Stack Over Flow";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
// char* p = NULL;
s = ""; // this is not really NULL, But would want a NULL here
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
s = "Answer";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
const char *cs = &c_strings2[0];
while (cs <= &c_strings2[2])
{
std::cout << cs << "\n";
cs += std::strlen(cs) + 1;
}
You don't have a vector of strings -- you have a vector of pointer-to-char. NULL is a perfectly valid pointer-to-char which happens to not point to anything, so it is stored in the vector.
Note that the pointers you are actually storing are pointers to char literals. The strings are not copied.
It doesn't make a lot of sense to mix the C++ style vector with the C-style char pointers. Its not illegal to do so, but mixing paradigms like this often results in confused & busted code.
Instead of using a vector<char*> or a vector<char>, why not use a vector<string> ?
EDIT
Based on your edit, it seems like what your'e trying to do is flatten several strings in to a single vector<char>, with a NULL-terminator between each of the flattened strings.
Here's a simple way to accomplish this:
#include <algorithm>
#include <vector>
#include <string>
#include <iterator>
using namespace std;
int main()
{
// create a vector of strings...
typedef vector<string> Strings;
Strings c_strings;
c_strings.push_back("Stack Over Flow");
c_strings.push_back("");
c_strings.push_back("Answer");
/* Flatten the strings in to a vector of char, with
a NULL terminator between each string
So the vector will end up looking like this:
S t a c k _ O v e r _ F l o w \0 \0 A n s w e r \0
***********************************************************/
vector<char> chars;
for( Strings::const_iterator s = c_strings.begin(); s != c_strings.end(); ++s )
{
// append this string to the vector<char>
copy( s->begin(), s->end(), back_inserter(chars) );
// append a null-terminator
chars.push_back('\0');
}
}
So,
char *p1 = "Stack Over Flow";
char *p2 = NULL;
char *p3 = "Answer";
If you notice, the type of all three of those is exactly the same. They are all char *. Because of this, we would expect them all to have the same size in memory as well.
You may think that it doesn't make sense for them to have the same size in memory, because p3 is shorter than p1. What actually happens, is that the compiler, at compile-time, will find all of the strings in the program. In this case, it would find "Stack Over Flow" and "Answer". It will throw those to some constant place in memory, that it knows about. Then, when you attempt to say that p3 = "Answer", the compiler actually transforms that to something like p3 = 0x123456A0.
Therefore, with either version of the push_back call, you are only pushing into the vector a pointer, not the actual string itself.
The vector itself, doesn't know, or care that a NULL char * is an empty string. So in it's counting, it sees that you have pushed three pointers into it, so it reports a size of 3.
I have a funny feeling that what you would really want is to have the vector contain something like "Stack Over Flow Answer" (possibly without space before "Answer").
In this case, you can use a std::vector<char>, you just have to push the whole arrays, not just pointers to them.
This cannot be accomplished with push_back, however vector have an insert method that accept ranges.
/// Maintain the invariant that the vector shall be null terminated
/// p shall be either null or point to a null terminated string
void push_back(std::vector<char>& v, char const* p) {
if (p) {
v.insert(v.end(), p, p + strlen(p));
}
v.push_back('\0');
} // push_back
int main() {
std::vector<char> v;
push_back(v, "Stack Over Flow");
push_back(v, 0);
push_back(v, "Answer");
for (size_t i = 0, max = v.size(); i < max; i += strlen(&v[i]) + 1) {
std::cout << &v[i] << "\n";
}
}
This uses a single contiguous buffer to store multiple null-terminated strings. Passing a null string to push_back results in an empty string being displayed.
What exactly is stored in the vector when you push a null value?
A NULL. You're storing pointers, and NULL is a possible value for a pointer. Why is this unexpected in any way?
Also, use std::string as the value type (i.e. std::vector<std::string>), char* shouldn't be used unless it's needed for C interop. To replicate your code using std::vector<char>, you'd need std::vector<std::vector<char>>.
You have to be careful when storing pointers in STL containers - copying the containers results in shallow copy and things like that.
With regard to your specific question, the vector will store a pointer of type char* regardless of whether or not that pointer points to something. It's entirely possible you would want to store a null-pointer of type char* within that vector for some reason - for example, what if you decide to delete that character string at a later point from the vector? Vectors only support amortized constant time for push_back and pop_back, so there's a good chance if you were deleting a string inside that vector (but not at the end) that you would prefer to just set it null quickly and save some time.
Moving on - I would suggest making a std::vector > if you want a dynamic array of strings which looks like what you're going for.
A std::vector as you mentioned would be useless compared to your original code because your original code stores a dynamic array of strings and a std::vector would only hold one dynamically changable string (as a string is an array of characters essentially).
NULL is just 0. A pointer with value 0 has a meaning. But a char with value 0 has a different meaning. It is used as a delimiter to show the end of a string. Therefore, if you use std::vector<char> and push_back 0, the vector will contain a character with value 0. vector<char> is a vector of characters, while std::vector<char*> is a vector of C-style strings -- very different things.
Update. As the OP wants, I am giving an idea of how to store (in a vector) null terminated strings some of which are nulls.
Option 1: Suppose we have vector<char> c_strings;. Then, we define a function to store a string pi. A lot of complexity is introduced since we need to distinguish between an empty string and a null char*. We select a delimiting character that does not occur in our usage. Suppose this is the '~' character.
char delimiter = '~';
// push each character in pi into c_strings
void push_into_vec(vector<char>& c_strings, char* pi) {
if(pi != 0) {
for(char* p=pi; *p!='\0'; p++)
c_strings.push_back(*p);
// also add a NUL character to denote end-of-string
c_strings.push_back('\0');
}
c_strings.push_back(deimiter);
// Note that a NULL pointer would be stored as a single '~' character
// while an empty string would be stored as '\0~'.
}
// now a method to retrieve each of the stored strings.
vector<char*> get_stored_strings(const vector<char>& c_strings) {
vector<char*> r;
char* end = &c_strings[0] + c_strings.size();
char* current = 0;
bool nullstring = true;
for(char* c = current = &c_strings[0]; c != end+1; c++) {
if(*c == '\0') {
int size = c - current - 1;
char* nc = new char[size+1];
strncpy(nc, current, size);
r.push_back(nc);
nullstring = false;
}
if(*c == delimiter) {
if(nullstring) r.push_back(0);
nullstring = true; // reset nullstring for the next string
current = c+1; // set the next string
}
}
return r;
}
You still need to call delete[] on the memory allocated by new[] above. All this complexity is taken care of by using the string class. I very rarely use char* in C++.
Option 2: You could use vector<boost::optional<char> > . Then the '~' can be replaced by an empty boost::optional, but other other parts are the same as option 1. But the memory usage in this case would be higher.

How to read in only a particular number of characters

I have a small query regarding reading a set of characters from a structure. For example: A particular variable contains a value "3242C976*32" (char - type). How can I get only the first 8 bits of this variable. Kindly help.
Thanks.
Edit:
I'm trying to read in a signal:
For Ex: $ASWEER,2,X:3242C976*32
into this structure:
struct pg
{
char command[7]; // saves as $ASWEER,2,X:3242C976*32
char comma1[1]; // saves as ,2,X:3242C976*32
char groupID[1]; // saves as 2,X:3242C976*32
char comma2[1]; // etc
char handle[2]; // this is the problem, need it to save specifically each part, buts its not
char canID[8];
char checksum[3];
}m_pg;
...
When memcopying buffer into a structure, it works but because there is no carriage returns it saves the rest of the signal in each char variable. So, there is always garbage at the end.
you could..
convert your hex value in canID to float(depending on how you want to display it), e.g.
float value1 = HexToFloat(m_pg.canID); // find a conversion script for HexToFloat
CString val;
val.Format("0.3f",value1);
the garbage values aren't actually being stored in the structure, it only displays it as so, as there is no carriage return, so format the message however you want to and display it using the CString val;
If "3242C976*3F" is a c-string or std::string, you can just do:
char* str = "3242C976*3F";
char first_byte = str[0];
Or with an arbitrary memory block you can do:
SomeStruct memoryBlock;
char firstByte;
memcpy(&firstByte, &memoryBlock, 1);
Both copy the first 8bits or 1 byte from the string or arbitrary memory block just as well.
After the edit (original answer below)
Just copy by parts. In C, something like this should work (could also work in C++ but may not be idiomatic)
strncpy(m_pg.command, value, 7); // m.pg_command[7] = 0; // oops
strncpy(m_pg.comma, value+7, 1); // m.pg_comma[1] = 0; // oops
strncpy(m_pg.groupID, value+8, 1); // m.pg_groupID[1] = 0; // oops
strncpy(m_pg.comma2, value+9, 1); // m.pg_comma2[1] = 0; // oops
// etc
Also, you don't have space for the string terminator in the members of the structure (therefore the oopses above). They are NOT strings. Do not printf them!
Don't read more than 8 characters. In C, something like
char value[9]; /* 8 characters and a 0 terminator */
int ch;
scanf("%8s", value);
/* optionally ignore further input */
while (((ch = getchar()) != '\n') && (ch != EOF)) /* void */;
/* input terminated with ch (either '\n' or EOF) */
I believe the above code also "works" in C++, but it may not be idiomatic in that language
If you have a char pointer, you can just set str[8] = '\0'; Be careful though, because if the buffer is less than 8 (EDIT: 9) bytes, this could cause problems.
(I'm just assuming that the name of the variable that already is holding the string is called str. Substitute the name of your variable.)
It looks to me like you want to split at the comma, and save up to there. This can be done with strtok(), to split the string into tokens based on the comma, or strchr() to find the comma, and strcpy() to copy the string up to the comma.