how to truncate first few characters char array c++ - c++

I have a char array called names[50]
Basically, I use
strncpy(this->names, names, sizeof(names))
however this will only truncate characters at the end.
How do I truncate characters from the start?
For example, BillSteveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge should be teveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge

If I have understood correctly then using the string you showed as an example you have to write
strncpy( this->names, names + 5, sizeof(names) - 5 );

You can change the source address for strncpy:
strncpy(this->names, &(names[10]), num_of_chars_to_copy);
Notice that no null-character is implicitly appended at the end of the destination string if the source string is longer than num.

You need to be clear what you want to do... is names[] variable in length from call to call? Is this->names a fixed length? Note that the length for the number of bytes to copy should be the number of bytes available in this->names... Otherwise you run the risk of overflowing the memory.

I designed for you this simple function, You can use it as reference code for more complex issue:
void BackStrCopy(char* src, char* dest, int srcsize, int destsize)
{
if(srcsize >= destsize )
{
do
dest[destsize--] = src[srcsize--];
while( destsize + 1 );
}
}
int main()
{
char* src = "BillSteveLinusMikeGeorgeBillSteveLinusMikeGeorgeGeorge";
char dest[50];
BackStrCopy(src, dest, strlen(src), 25);
}
I tested it end work.
I thing that the function code does not require any comment:)
If my solution help you, please remember to check it as answered.
Ciao

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)

Need to find the number of contents in the array

I have a char array and I want to find out the number of contents in it.
For example, my array is:
char myArray[10];
And after input it's content is:
ABC
Now I want to store in a variable 'size', the size of the area related to content. So, in this case:
size = 3
How do I find that?
A naive way of doing this would be to look for the null-terminating character \0, this is already implemented for you in the C-function strlen, so there are two ways of doing this:
int StringLength( const char* str, int maxLength )
{
for( int i = 0; i < maxLength; ++i )
{
if( str[i] == '\0' )
return i;
}
return -1;
}
Or you could just call strlen as follows:
int iLength = strlen( myArray );
However, as you have tagged this c++, the best way to do this would be to not deal with C-style character arrays and instead use the extremely useful std::string class.
strlen(myArray) is what you want.
Try this:
int len = strlen(myArray).
strlen is a part of stdlib.h library. Don't forget to declare it in your program.
defining array as char myArray[10]; will not always initialize it's content to zeros, so depending on how you fill it with ABC you either can or cannot find the corect lenght. In worst case regular strlen() will always report numbers >10, or even result in read access vialation. I try initialize it like char myArray[10] = {}; first

Difference between char* and strncpy values in the argument

I've got function, that saves values that takes in the argument. And I have to implement two ways, to accept the input - as a char* and then via strncpy.
Ie: a . Add("123456/7890", "John", "Doe", "2000-01-01", "Main street", "Seattle");
It works allright, untill I use strncpy:
bool status;
char lID[12], lDate[12], lName[50], lSurname[50], lStreet[50], lCity[50];
strncpy(lID, "123456/7890", sizeof ( lID));
strncpy(lName, "John", sizeof ( lName));
strncpy(lSurname, "Doe", sizeof ( lSurname));
strncpy(lDate, "2000-01-01", sizeof ( lDate));
strncpy(lStreet, "Main street", sizeof ( lStreet));
strncpy(lCity, "Seattle", sizeof ( lCity));
status = c . Add(lID, lName, lSurname, lDate, lStreet, lCity);
//is true
strncpy(lID, "987654/3210", sizeof ( lID));
strncpy(lName, "Freddy", sizeof ( lName));
strncpy(lSurname, "Kruger", sizeof ( lSurname));
strncpy(lDate, "2001-02-03", sizeof ( lDate));
strncpy(lStreet, "Elm street", sizeof ( lStreet));
strncpy(lCity, "Sacramento", sizeof ( lCity));
// notice, that I don't even save it at this point
strncpy(lID, "123456/7890", sizeof ( lID));
strncpy(lDate, "2002-12-05", sizeof ( lDate));
strncpy(lStreet, "Sunset boulevard", sizeof ( lStreet));
strncpy(lCity, "Los Angeles", sizeof ( lCity));
status = c . Resettle(lID, lDate, lStreet, lCity);
status = c . Print(cout, "123456/7890");
//is true
At this point I want to print out values for ID 123456/7890... so Name:John, Surname:Doe etc.
Neverthless It prints out values, that were saved as the last ones:
123456/7890 Freddy Kruger
2002-12-05 Sunset boulevard Los Angeles
2002-12-05 Sunset boulevard Los Angeles
My Add is declared as:
bool Add(const char * id,
const char * name,
const char * surname,
const char * date,
const char * street,
const char * city);
Resettle function is delared similar to Add, it just doesn't take name and surname arguments.
All values are saved to char ** arrays.
Could you please advice me, how to handle this situation, to be able to accept properly both inputs?
Ps: for char* input whole program works allright, so I don't expect any bug there..
Pps: pls don't advice me to use strings or any other constructions I don't use here - I'm very limited on imports, thus I use char* and other stuff...
I don't think you should be using sizeof here. strncpy needs the length of the string(number of chars to be copy from destination string to source string).
sizeof is the size of the pointer (e.g sizrof(IID) = address size, om my system its 4).
I think you need strlen(). Also, this needs to be called on the source pointer, not the destination pointer.
strncpy(lID, "987654/3210", strlen ("987654/3210"));
Be sure that IID is enough long to copy string, otherwise buffer overflow can be problem
read char * strncpy ( char * destination, const char * source, size_t num );, and
Copy characters from string
Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
Thanks to #JonathanLeffler: (The strlen() means the data won't be null-terminated. That's bad. At least copy strlen() + 1 bytes to get the null-terminator)
Note: No null-character is implicitly appended at the end of destination if source is longer than num (thus, in this case, destination may not be a null terminated C string).
Also read about: size_t strlen ( const char * str );
The problem is not in the strncpy() operations. It is in the material you've not shown us:
c.Add()
c.Resettle()
c.Print()
One or more of those has problems, but since we can't yet see them, we can't help you debug them.
Demonstration of strncpy()
There's a discussion of the behaviour of strncpy() in the comments to voodoogiant's answer. Here's a demonstration that:
strncpy(target, "string", strlen("string"));
does not null-terminate the output:
#include <string.h>
#include <stdio.h>
int main(void)
{
char buffer[32];
memset(buffer, 'X', sizeof(buffer));
printf("Before: %.*s\n", (int)sizeof(buffer), buffer);
strncpy(buffer, "123456/7890", strlen("123456/7890"));
printf("After: %.*s\n", (int)sizeof(buffer), buffer);
return(0);
}
Output:
Before: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
After: 123456/7890XXXXXXXXXXXXXXXXXXXXX
(Yes, I'm quite aware that the memset() does not null-terminate the buffer; it doesn't need to for this example because the printing is done carefully.)
As we do not know the datatype of lID et al. and hence the sizeof it is difficult to comment. Besides why not use the string container from STL

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.

Very strange char array behaviour

.
unsigned int fname_length = 0;
//fname length equals 30
file.read((char*)&fname_length,sizeof(unsigned int));
//fname contains random data as you would expect
char *fname = new char[fname_length];
//fname contains all the data 30 bytes long as you would expect, plus 18 bytes of random data on the end (intellisense display)
file.read((char*)fname,fname_length);
//m_material_file (std:string) contains all 48 characters
m_material_file = fname;
// count = 48
int count = m_material_file.length();
now when trying this way, intellisense still shows the 18 bytes of data after setting the char array to all ' ' and I get exactly the same results. even without the file read
char name[30];
for(int i = 0; i < 30; ++i)
{
name[i] = ' ';
}
file.read((char*)fname,30);
m_material_file = name;
int count = m_material_file.length();
any idea whats going wrong here, its probably something completely obvious but im stumped!
thanks
Sounds like the string in the file isn't null-terminated, and intellisense is assuming that it is. Or perhaps when you wrote the length of the string (30) into the file, you didn't include the null character in that count. Try adding:
fname[fname_length] = '\0';
after the file.read(). Oh yeah, you'll need to allocate an extra character too:
char * fname = new char[fname_length + 1];
I guess that intellisense is trying to interpret char* as C string and is looking for a '\0' byte.
fname is a char* so both the debugger display and m_material_file = fname will be expecting it to be terminated with a '\0'. You're never explicitly doing that, but it just happens that whatever data follows that memory buffer has a zero byte at some point, so instead of crashing (which is a likely scenario at some point), you get a string that's longer than you expect.
Use
m_material_file.assign(fname, fname + fname_length);
which removes the need for the zero terminator. Also, prefer std::vector to raw arrays.
std::string::operator=(char const*) is expecting a sequence of bytes terminated by a '\0'. You can solve this with any of the following:
extend fname by a character and add the '\0' explicitly as others have suggested or
use m_material_file.assign(&fname[0], &fname[fname_length]); instead or
use repeated calls to file.get(ch) and m_material_file.push_back(ch)
Personally, I would use the last option since it eliminates the explicitly allocated buffer altogether. One fewer explicit new is one fewer chance of leaking memory. The following snippet should do the job:
std::string read_name(std::istream& is) {
unsigned int name_length;
std::string file_name;
if (is.read((char*)&name_length, sizeof(name_length))) {
for (unsigned int i=0; i<name_length; ++i) {
char ch;
if (is.get(ch)) {
file_name.push_back(ch);
} else {
break;
}
}
}
return file_name;
}
Note:
You probably don't want to use sizeof(unsigned int) to determine how many bytes to write to a binary file. The number of bytes read/written is dependent on the compiler and platform. If you have a maximum length, then use it to determine the specific byte size to write out. If the length is guaranteed to fewer than 255 bytes, then only write a single byte for the length. Then your code will not depend on the byte size of intrinsic types.