#include <iostream>
#include <cstring>
#include <cctype>
void go_lower(char *str);
void fill_str(char *mass);
int main()
{
char str_1[80];
char str_2[80];
char *pointer_1;
char *pointer_2;
pointer_1 = str_1;
pointer_2 = str_2;
fill_str(pointer_1);
fill_str(pointer_2);
if(strcmp(str_1, str_2))
{
std::cout << "This strings are equal\n";
}
else
{
std::cout << "This strings are not equal\n";
}
return 0;
}
void fill_str(char *mass)
{
std::cout << "Insert string to compare\n";
while(*mass)
{
std::cin >> *mass;
mass++;
}
std::cout << '\n';
}
void go_lower(char *str)
{
while(*str)
{
if(isupper(*str))
{
*str = tolower(*str);
}
str++;
}
}
Not so long ago I started studying programming and I try to understand the test tasks. In this program, I need to compare strings regardless of the case of the characters. when the program starts, it goes straight to filling in str_2 and the comparison block concludes that the strings are equal. What is the error?
While in C++, you should really use std::basic_string, you still need to know how to handle plain-old-arrays. When passing an plain-old-array to a function to be filled, you must pass an additional parameter to provide the array size to the function. Within the function you can only read size - 2 characters into the array (saving index size - 1 for the nul-terminating character '\0' (ASCII 0).
You should also avoid using MagicNumbers in your code. Instead, if you need a constant (such as for 80), #define a constant or use a const int in C++. Since you have a choice for your return type, you should return the number of characters read into each string. The information will be available when you leave your fill_str() function and there is no reason to have to calling function re-scan for end-of-string.
You cannot use std::cin effectively to read multi-word input in a function as you are attempting. std::cin discards whitespace, so you will never read the '\n' generated by the user pressing [Enter]. So you have know way to know when the user is done typing. Instead you must use std::cin.get(); which will read every character.
With those changes, you can write your function to use plain-old-arrays as follows:
#define MAXC 80 /* if you need a constant, #define one (or more) */
...
/* using plain-old-arrays, you must pass size of array */
size_t fill_str (char *mass, size_t size)
{
size_t n = 0; /* length counter */
std::cout << "\nInsert string to compare\n";
while (n < size - 1) /* read while array not full */
{
if ((*mass = std::cin.get()) && *mass != '\n') /* use get() to read whitespace */
n++; /* increment length counter */
if (*mass == '\n') /* if \n user pressed [Enter], input complete */
break;
mass++; /* increment pointer */
}
*mass = 0; /* nul-terminate when you leave read-loop */
return n; /* return length */
}
Your example, omitting your unused go_lower() function, could then be:
#include <iostream>
#include <cstring>
#define MAXC 80 /* if you need a constant, #define one (or more) */
size_t fill_str (char *mass, size_t size);
int main (void)
{
char str_1[MAXC];
char str_2[MAXC];
char *pointer_1;
char *pointer_2;
size_t len_1, len_2;
pointer_1 = str_1;
pointer_2 = str_2;
len_1 = fill_str (pointer_1, MAXC);
len_2 = fill_str (pointer_2, MAXC);
if (len_1 != len_2) {
std::cout << "\nstrings differ in length (" <<
len_1 << " != " << len_2 << ").\n";
return 0;
}
if (strcmp (str_1, str_2) == 0) /* only equal if return is 0 */
{
std::cout << "\nThis strings are equal\n";
}
else
{
std::cout << "\nThis strings are not equal\n";
}
return 0;
}
/* using plain-old-arrays, you must pass size of array */
size_t fill_str (char *mass, size_t size)
{
size_t n = 0; /* length counter */
std::cout << "\nInsert string to compare\n";
while (n < size - 1) /* read while array not full */
{
if ((*mass = std::cin.get()) && *mass != '\n') /* use get() to read whitespace */
n++; /* increment length counter */
if (*mass == '\n') /* if \n user pressed [Enter], input complete */
break;
mass++; /* increment pointer */
}
*mass = 0; /* nul-terminate when you leave read-loop */
return n; /* return length */
}
Example Use/Output
$ ./bin/golower
Insert string to compare
apples and bananas
Insert string to compare
apples and bananas
This strings are equal
$ ./bin/golower
Insert string to compare
pickle
Insert string to compare
pickles
strings differ in length (6 != 7).
As mentioned at the beginning, you should really use std::string and getline() to read the user input, but you should also be familiar with plain-old-arrays. Look things over and let me know if you have questions.
The while (*mass) in your fill_str function will not loop even once because * mass may be initialized to 0 here (it is actually undefined behavior and up to your compiler). Because of this none of your strings will ever get filled with anything.
strcmp evaluates your strings equal because they are initialized in such a way they are equal from the beginning and are never changed because of the reason stated above.
To read a string consider using getline or std::cin if you know that your string will not contain spaces, eg.
std::cin >> mass;
See more here https://www.cplusplus.com/doc/tutorial/basic_io. Also consider using std::string instead of char arrays.
Related
I need to split a list of student like this into ID, Name and score. This is an exercise so no string is allowed, only char
0001 William Bob 8.5
0034 Howard Stark 9.5
0069 Natalia Long Young 8
Here's the code
int readFile(list& a) {
char str[MAX];
short i = 0;
ifstream fi(IN);
if (!fi)
return 0;
while (!fi.eof()) {
fi.getline(str, MAX - 1);
char* temp = strtok(str, " ");
if (temp == NULL)
continue;
strcpy(a.sv[i].id, temp);
temp = strtok(NULL, "0123456789");
strcpy(a.sv[i].name, temp);
temp = strtok(NULL, "\n");
a.sv[i].grade = atof(temp);
i++;
}
a.size = i;
fi.close();
return 1;
}
Using strtok() I have splitted ID and Name successfully, but the score are
0.5
0.5
0
I know the problem is because of temp = strtok(NULL, "0123456789"); but I don't know how to fix it, are there any delimiters beside "0123456789", or can I move the pointer back?
This is my attempt to fix the while(!file.eof()) and solve my problem.
Here's my heading and structs:
#include<iostream>
#include<fstream>
#include<string>
#define IN "D:\\Input.txt"
#define OUT "D:\\Output.txt"
#define MAX 40
using namespace std;
struct sv{
char id[MAX], name[MAX] , sex[MAX];
float grade;
};
struct dssv {
sv sv[MAX];
short size;
};
And here's my function:
int readFile(dssv& a) {
char str[MAX];
short i = 0;
ifstream fi(IN);
if (!fi)
return 0;
while (fi>>a.sv[i].id && fi.getline(str, MAX)) {
char* name = strchr(str, ' ');
int pos = strrchr(name, ' ') - name;
char* score = str + pos;
strcpy(name + pos, "\0"); \\null-terminate to remove the score.
strcpy(a.sv[i].name, name + 1);
a.sv[i].grade = atof(score + 1);
i++;
}
a.size = i;
fi.close();
return 1;
}
Still figuring out how to fix the eof() and why do I need two pointers char* name and char* score instead of one and reuse it.
You have started off on the wrong foot. See Why is while ( !feof (file) ) always wrong?. While there are a number of ways to separate the information into id, name, score, probably the most basic is to simply read an entire line of data into a temporary buffer (character array), and then to use sscanf to separate id, name & score.
The parsing with sscanf is not difficult, the only caveat being that your name can contain whitespace, so you cannot simply use "%s" as the format specifier to extract the name. This is mitigated by your score field always starting with a digit and digits do not occur in names (there are always exceptions to the rule -- and it can be handled with a simple parse with a pair of pointers, but for the basic example we will make this formatting assumption)
To make data handling simpler and be able to coordinate all the information for one student as a single object (allowing you to create an array of them to hold all student information) you can use a simple stuct. Declaring a few constants to set the sizes for everything avoids using Magic-Numbers throughout your code. (though for the sscanf field-width modifiers, actual numbers must be used as you cannot use constants or variables for the width modifier) For example, your struct could be:
#define MAXID 8 /* if you need a constant, #define one (or more) */
#define MAXNM 64
#define MAXSTD 128
#define MAXLN MAXSTD
typedef struct { /* simple struct to hold student data */
char id[MAXID];
char name[MAXNM];
double score;
} student_t;
(and POSIX reserves the "_t" suffix for extension of types, but there won't be a "student_t" type -- but in general be aware of the restriction though you will see the "_t" suffix frequently)
The basic approach is to read a line from your file into a buffer (with either fgets or POSIX getline) and then pass the line to sscanf. You condition your read loop on the successful read of each line so your read stops when EOF is reached. For separating the values with sscanf, it is convenient to use a temporary struct to hold the separated values. That way if the separation is successful, you simply add the temporary struct to your array. To read the students into an array of student_t you could do:
size_t readstudents (FILE *fp, student_t *s)
{
char buf[MAXLN]; /* temporary array (buffer) to hold line */
size_t n = 0; /* number of students read from file */
/* read each line in file until file read or array full */
while (n < MAXSTD && fgets (buf, MAXLN, fp)) {
student_t tmp = { .id = "" }; /* temporary stuct to fill */
/* extract id, name and score from line, validate */
if (sscanf (buf, "%7s %63[^0-9] %lf", tmp.id, tmp.name, &tmp.score) == 3) {
char *p = strrchr (tmp.name, 0); /* pointer to end of name */
/* backup overwriting trailing spaces with nul-terminating char */
while (p && --p >= tmp.name && *p == ' ')
*p = 0;
s[n++] = tmp; /* add temp struct to array, increment count */
}
}
return n; /* return number of students read from file */
}
Now let's take a minute and look at the sscanf format string used:
sscanf (buf, "%7s %63[^0-9] %lf", tmp.id, tmp.name, &tmp.score)
Above, with the line in buf, the format string used is "%7s %63[^0-9] %lf". Each character array type uses a field-width modifier to limit the number of characters stored in the associated array to one-less-than the number of characters available. This protects the array bounds and ensures that each string stored is nul-terminated. The "%7s" is self-explanatory - read at most 7-characters into what will be the id.
The next conversion specifier for the name is "%63[^0-9]" which is a bit more involved as it uses the "%[...] character class conversion specifier with the match inverted by use of '^' as the first character. The characters in the class being digits 0-9, the conversion specifier reads up to 63 character that do Not include digits. This will have the side-effect of including the spaces between name and score in name. Thankfully they are simple enough to remove by getting a pointer to the end of the string with strrchr (tmp.name, 0); and then backing up checking if the character is a ' ' (space) and overwriting it with a nul-terminating character (e.g. '\0' or numeric equivalent 0).
The last part of the sscanf conversion, "%lf" is simply the conversion specifier for the double value for score.
Note: most importantly, the conversion is validated by checking the return of the call to sscanf is 3 -- the number of conversions requested. If all conversions succeed into the temporary struct tmp, then tmp is simply added to your array of struct.
To call the function from main() and read the student information, you simply declare an array of student_t to hold the information, open and validate your data file is open for reading, and make a call to readstudents capturing the return to validate that student information was actually read from the file. Then you can make use of the data as you wish (it is simply output below):
int main (int argc, char **argv) {
student_t students[MAXSTD] = {{ .id = "" }}; /* array of students */
size_t nstudents = 0; /* count of students */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* read students from file, validate return, if zero, handle error */
if ((nstudents = readstudents (fp, students)) == 0) {
fputs ("error: no students read from file.\n", stderr);
return 1;
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < nstudents; i++) /* output each student data */
printf ("%-8s %-24s %g\n",
students[i].id, students[i].name, students[i].score);
return 0;
}
All that remains is including the required headers, stdio.h and string.h and testing:
Example Use/Output
$ ./bin/read_stud_id_name_score dat/stud_id_name_no.txt
0001 William Bob 8.5
0034 Howard Stark 9.5
0069 Natalia Long Young 8
It works as needed.
Note, this is the most basic way of separating the values and only works based on the assumption that your score field starts with a digit.
You can eliminate that assumption by manually parsing the information you need by reading each line in the same manner, but instead of using sscanf, simply declare a pair of pointers to isolate id, name & score manually. The basic approach being to advance a pointer to the first whitespace and read id, skip the following whitespace and position the pointer at the beginning of name. Start from the end of the line with the other and backup to the first whitespace at the end and read score, then continue backing up positioning the pointer in the first space after name. Then just copy the characters between your start and end pointer to name and nul-terminate. It is more involved from a pointer-arithmetic standpoint, but just as simple. (that is left to you)
Look things over and let me know if you have further questions. Normally, you would dynamically declare your array of students and allocate/reallocate as needed to handle any number of students from the file. (or from an actual C++ standpoint use the vector and string types that the standard template library provides and let the containers handle the memory allocation for you) That too is just one additional layer that you can add to add flexibility to your code.
C++ Implementation
I apologize for glossing over a C++ solution, but given your use of C string functions in your posted code, I provided a C solution in return. A C++ solution making using the std::string and std::vector is not that much different other than from a storage standpoint. The parsing of the three values is slightly different, where the entire line is read into id and name and then the score is obtained from the portion of the line held in name and then those characters erased from name.
Changing the C FILE* to std::ifstream and the array of student_t to a std::vector<student_t>, your readstudents() function could be written as:
void readstudents (std::ifstream& fp, std::vector<student_t>& s)
{
std::string buf; /* temporary array (buffer) to hold line */
student_t tmp; /* temporary stuct to fill */
/* read each line in file until file read or array full */
while (fp >> tmp.id && getline(fp, tmp.name)) {
/* get offset to beginning digit within tmp.name */
size_t offset = tmp.name.find_first_of("0123456789"),
nchr; /* no. of chars converted with stod */
if (offset == std::string::npos) /* validate digit found */
continue;
/* convert to double, save in tmp.score */
tmp.score = std::stod(tmp.name.substr(offset), &nchr);
if (!nchr) /* validate digits converted */
continue;
/* backup using offset to erase spaces after name */
while (tmp.name.at(--offset) == ' ')
tmp.name.erase(offset);
s.push_back(tmp); /* add temporary struct to vector */
}
}
(note: the return type is changed to void as the .size() of the student vector can be validated on return).
The complete example would be:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <vector>
struct student_t { /* simple struct to hold student data */
std::string id;
std::string name;
double score;
};
void readstudents (std::ifstream& fp, std::vector<student_t>& s)
{
std::string buf; /* temporary array (buffer) to hold line */
student_t tmp; /* temporary stuct to fill */
/* read each line in file until file read or array full */
while (fp >> tmp.id && getline(fp, tmp.name)) {
/* get offset to beginning digit within tmp.name */
size_t offset = tmp.name.find_first_of("0123456789"),
nchr; /* no. of chars converted with stod */
if (offset == std::string::npos) /* validate digit found */
continue;
/* convert to double, save in tmp.score */
tmp.score = std::stod(tmp.name.substr(offset), &nchr);
if (!nchr) /* validate digits converted */
continue;
/* backup using offset to erase spaces after name */
while (tmp.name.at(--offset) == ' ')
tmp.name.erase(offset);
s.push_back(tmp); /* add temporary struct to vector */
}
}
int main (int argc, char **argv) {
std::vector<student_t> students {}; /* array of students */
if (argc < 2) { /* validate one argument given for filename */
std::cerr << "error: filename required as 1st argument.\n";
return 1;
}
std::ifstream fp (argv[1]); /* use filename provided as 1st argument */
if (!fp.good()) { /* validate file open for reading */
std::cerr << "file open failed";
return 1;
}
/* read students from file, validate return, if zero, handle error */
readstudents (fp, students);
if (students.size() == 0) {
std::cerr << "error: no students read from file.\n";
return 1;
}
for (auto s : students) /* output each student data */
std::cout << std::left << std::setw(8) << s.id
<< std::left << std::setw(24) << s.name
<< s.score << '\n';
}
(the output is the same -- aside from 2-spaces omitted between the values)
Look things over and let me know if you have questions.
I'm learning C++, and I have no idea how I'm supposed to do the following.
In an assignment, we need to write a function that checks if the first two characters of a string are the same as the last two.
These are the limitations:
You cannot use std::string class or string function such as strlen. You must use either array or pointer for this function.
I tried this:
bool haveTheSameTwoChars(string str) {
char arr[] = str;
if (sizeof(arr) < 3) {
return false;
}
else if (arr[0] == arr[sizeof(arr) - 3] && arr[1] == arr[sizeof(arr) - 2]) {
return true;
}
else {
return false;
}
}
But it won't accept str into the array.
However, if I were to put something in quotes in the place of str, it accepts it just fine, despite them both being strings.
What am I doing wrong?
Well, here's the breakdown of your problem:
You need to take input as an array or a pointer. For example:
bool isMatched( const char* str )
{
// ...
}
Now, you need to calculate the length of your string yourself. Revise the loops and devise something that gives you the length of a null terminated string. C-strings are null-terminated i.e. '\0' so you can end your loop when you encounter null character. For example:
int len = 0;
while ( str[len] != '\0' ) len++;
That's just an idea. Do your own research and calculate the string length correctly.
The rest is just a comparison of first and last two characters using if. :)
I'm sure you can put things together and revise your study material a bit to solve this.
Best of luck!
Happy coding!
When you're not allowed strlen, it is a strong hint that the problem can be solved without caring about the length of the string.
Let's do that:
First, you should have the prototype (no strings allowed, right?)
bool haveTheSameTwoChars(const char* str)
Then, verify that the string has at least two characters
if (!str || !str[0] || !str[1])
return false;
Then you find the end of the string:
const char* end = str;
while (*end)
end++;
Then move back two characters, so end points to the first of the last two characters:
end -= 2;
(This is safe since we first checked that there are at least two characters.)
Then compare
return str[0] == end[0] && str[1] == end[1];
Your professor wants you to use a const char* as the function parameter, that is, model a string as a pointer to the first character in the string, where the string finishes with a 0. (We call this NUL-termination).
So your function is
bool haveTheSameTwoChars(const char* s)
You then need to roll your own version of strlen1 to calculate the number of characters in the string up to and not including the NUL:
#include <cstddef> // for std::size_t
std::size_t strlen(const char *s) {
const char *p = s;
while (*s) ++s;
return s - p;
}
After which, it's a simple case, given the length l say, of dealing with a couple of edge cases and the general case:
If l is 0 or 1, return false.
If l is 2, return true.
If l is greater than 2 then something similar to how you have it in the question will suffice, remembering that my l is one less than yours.
Note that C++ does not support variable length arrays, so char arr[] = str; is not valid C++, and sizeof is a compile-time operator so would only give you the size of an actual array type, not an array that's decayed to a pointer type.
1 No professional programmer would dream of doing that. A compiler might optimise strlen down to a machine word-based algorithm, i.e. consider multiple bytes simultaneously.
Here is some sample code for your purpose
bool haveTheSameTwoChars(const char* p) {
if(!p || !*p)
return false;
int len = -1;
while(p[++len]);
if(p[0] == p[len-2] &&
p[1] == p[len-1])
return true;
return false;
}
This will return true if the string is "ABxxxAB" and false if "ABxxxBA". Rest you can tweak according to your desire.Thanks
Another approach uses a loop and a single pointer to iterate over each line saving the first and second characters to compare against the next to last (penultimate) and last (ultimate) characters in the string.
By using simple iteration over the character array, as long as it is nul-terminated, you don't have to worry about a separate loop to find the length, you simply grab/save the first two characters, and then iterate to the end of your string saving the prev/last characters as you go. When you hit the end of your string, all you need to do is compare the first against the last and the second against the next to last, e.g.
/* function iterates pointer through chars in 'line' saving
* the 1st & 2nd chara in 'beg` and 'next' and the penultimate and
* ultimate characters in 'prev' & 'last' and compares. returns 1 if
* 1st matches ultimate AND 2nd matches penultimate, 0 otherwise.
*/
int begmatchend (const char *line)
{
const char *p = line; /* pointer to line */
char beg = *p++, /* save for 1st char */
next = *p++, /* save for 2nd char */
prev = *p++, /* save for next to last char */
last = *p++; /* save for last char */
while (*p) { /* iterate over line setting prev/last as you go */
prev = last;
last = *p++;
}
if (beg == last && next == prev) /* test whether equal */
return 1;
return 0;
}
Then for a simple test, you can just feed your programs lines from a file calling begmatchend on each line and then output indication of which lines matched. Something simple like the following is one way:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstddef>
#define MAXC 1024
/* function iterates pointer through chars in 'line' saving
* the 1st & 2nd chara in 'beg` and 'next' and the penultimate and
* ultimate characters in 'prev' & 'last' and compares. returns 1 if
* 1st matches ultimate AND 2nd matches penultimate, 0 otherwise.
*/
int begmatchend (const char *line)
{
const char *p = line; /* pointer to line */
char beg = *p++, /* save for 1st char */
next = *p++, /* save for 2nd char */
prev = *p++, /* save for next to last char */
last = *p++; /* save for last char */
while (*p) { /* iterate over line setting prev/last as you go */
prev = last;
last = *p++;
}
if (beg == last && next == prev) /* test whether equal */
return 1;
return 0;
}
int main (int argc, char **argv) {
char line[MAXC] = "";
size_t n = 0;
std::ifstream f (argc > 1 ? argv[1] : "/dev/stdin");
if (!f.is_open()) {
std::cerr << "error: file open failed.\n";
return 1;
}
while (f.getline (line, MAXC, '\n')) {
if (begmatchend ((const char *)line))
std::cout << "line[" << std::setw(3) << n <<
"] 1st/ultimate matched, 2nd/penultimate matched.\n";
n++;
}
return 0;
}
Example Input
$ cat dat/linesbegend.txt
all good vikings go to valhalla
be a tide that will flow and eb
a quick brown fox jumps over the lazy dog
we can find the begin and end - eew
Example Use/Output
$ ./bin/beg_end_match dat/linesbegend.txt
line[ 0] 1st/ultimate matched, 2nd/penultimate matched.
line[ 1] 1st/ultimate matched, 2nd/penultimate matched.
line[ 3] 1st/ultimate matched, 2nd/penultimate matched.
Look things over and let me know if you have any questions.
My stringNAdd function will duplicate strncat (original). I cannot accept arrays as parameters, but pointers. I wonder if my code right?
Here is the fixed code:
#include <string>
#include <iostream>
using namespace std;
char *stringNAdd(char str1[], char str2[],size_t num);
int main()
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout<< strncat(dest, src, 20) << endl;
cout << stringNAdd(dest, src, 20) << endl;
cin.get();
return 0;
}
char *stringNAdd(char str1[], char str2[],size_t num){
size_t str1_len = strlen(str1);
size_t i;
for (i=0; i < num && str2[i] != '\0'; i++)
i==num;
str1[str1_len+i] = str2[i];
str1[str1_len+i] = '\0';
return str1;
}
Output:
Using strncat function, this part is added
Using strncat function, this part is added
The problem is that you don't do the test of both functions in the same conditions: once you've executed strncat(), the dest already contains the longer concatenated version.
The second problem is that dest was already enlarged by 15 chars. It has therefore an initial length of 38 chars + the null terminator before calling stringNAdd(). Adding 15 more chars result in a string of 53 chars plus a null terminator, which is 4 chars longer than your array. So you'll get a buffer overflow, hence memory corruption and undefined behavior.
But all this is related to the testing conditions: your clone works fine.
Suggestions:
Run your functions in distinct blocks, and define your testing variables local to that block:
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout<< strncat(dest, src, 15) << endl;
cout << strlen(dest)<<endl;
}
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout << stringNAdd(dest, src, 15) << endl;
}
Think of a more secure version of your function, in which you would have an additional argument with the total length of the destination array to prevent these errors. This would increase the security of your code. By the way, this is what Microsoft does with strncat_s().
Finally, you could ask your teacher why he/she still lets you work with cstrings, when there are the so much more convenient and secure std::string, and that he certainly could find more modern exercises with the same pedagogical benefits.
Here is equivalent based on https://opensource.apple.com/source/Libc/Libc-167/gen.subproj/i386.subproj/strncat.c
#include <iostream>
char *strnadd(char *dst, const char *src, size_t n)
{
// abort if source is empty
if (n != 0)
{
// copy pointers
char *d = dst;
const char *s = src;
// find end of destination str
while (*d != 0)
d++;
// start copying chars from source str to the end of destination str
// until either source string ends or number of chars copied
// destination string has to be long enough to accommodate source
do
{
if ((*d = *s++) == 0)
break;
d++;
}
while (--n != 0);
// add null termination
*d = 0;
}
// return the resulting string
return dst;
}
int main()
{
char strCat[50];
char strAdd[50];
strcpy(strCat, "string1");
strcpy(strAdd, "string1");
char const *str2 = "string2";
std::cout << strncat(strCat, str2, 6) << std::endl;
std::cout << strnadd(strAdd, str2, 6) << std::endl;
return 0;
}
Prints:
string1string
string1string
I tried to write a script that removes extra white spaces but I didn't manage to finish it.
Basically I want to transform abc sssd g g sdg gg gf into abc sssd g g sdg gg gf.
In languages like PHP or C#, it would be very easy, but not in C++, I see. This is my code:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>
#include <string.h>
char* trim3(char* s) {
int l = strlen(s);
while(isspace(s[l - 1])) --l;
while(* s && isspace(* s)) ++s, --l;
return strndup(s, l);
}
char *str_replace(char * t1, char * t2, char * t6)
{
char*t4;
char*t5=(char *)malloc(10);
memset(t5, 0, 10);
while(strstr(t6,t1))
{
t4=strstr(t6,t1);
strncpy(t5+strlen(t5),t6,t4-t6);
strcat(t5,t2);
t4+=strlen(t1);
t6=t4;
}
return strcat(t5,t4);
}
void remove_extra_whitespaces(char* input,char* output)
{
char* inputPtr = input; // init inputPtr always at the last moment.
int spacecount = 0;
while(*inputPtr != '\0')
{
char* substr;
strncpy(substr, inputPtr+0, 1);
if(substr == " ")
{
spacecount++;
}
else
{
spacecount = 0;
}
printf("[%p] -> %d\n",*substr,spacecount);
// Assume the string last with \0
// some code
inputPtr++; // After "some code" (instead of what you wrote).
}
}
int main(int argc, char **argv)
{
printf("testing 2 ..\n");
char input[0x255] = "asfa sas f f dgdgd dg ggg";
char output[0x255] = "NO_OUTPUT_YET";
remove_extra_whitespaces(input,output);
return 1;
}
It doesn't work. I tried several methods. What I am trying to do is to iterate the string letter by letter and dump it in another string as long as there is only one space in a row; if there are two spaces, don't write the second character to the new string.
How can I solve this?
There are already plenty of nice solutions. I propose you an alternative based on a dedicated <algorithm> meant to avoid consecutive duplicates: unique_copy():
void remove_extra_whitespaces(const string &input, string &output)
{
output.clear(); // unless you want to add at the end of existing sring...
unique_copy (input.begin(), input.end(), back_insert_iterator<string>(output),
[](char a,char b){ return isspace(a) && isspace(b);});
cout << output<<endl;
}
Here is a live demo. Note that I changed from c style strings to the safer and more powerful C++ strings.
Edit: if keeping c-style strings is required in your code, you could use almost the same code but with pointers instead of iterators. That's the magic of C++. Here is another live demo.
Here's a simple, non-C++11 solution, using the same remove_extra_whitespace() signature as in the question:
#include <cstdio>
void remove_extra_whitespaces(char* input, char* output)
{
int inputIndex = 0;
int outputIndex = 0;
while(input[inputIndex] != '\0')
{
output[outputIndex] = input[inputIndex];
if(input[inputIndex] == ' ')
{
while(input[inputIndex + 1] == ' ')
{
// skip over any extra spaces
inputIndex++;
}
}
outputIndex++;
inputIndex++;
}
// null-terminate output
output[outputIndex] = '\0';
}
int main(int argc, char **argv)
{
char input[0x255] = "asfa sas f f dgdgd dg ggg";
char output[0x255] = "NO_OUTPUT_YET";
remove_extra_whitespaces(input,output);
printf("input: %s\noutput: %s\n", input, output);
return 1;
}
Output:
input: asfa sas f f dgdgd dg ggg
output: asfa sas f f dgdgd dg ggg
Since you use C++, you can take advantage of standard-library features designed for that sort of work. You could use std::string (instead of char[0x255]) and std::istringstream, which will replace most of the pointer arithmetic.
First, make a string stream:
std::istringstream stream(input);
Then, read strings from it. It will remove the whitespace delimiters automatically:
std::string word;
while (stream >> word)
{
...
}
Inside the loop, build your output string:
if (!output.empty()) // special case: no space before first word
output += ' ';
output += word;
A disadvantage of this method is that it allocates memory dynamically (including several reallocations, performed when the output string grows).
There are plenty of ways of doing this (e.g., using regular expressions), but one way you could do this is using std::copy_if with a stateful functor remembering whether the last character was a space:
#include <algorithm>
#include <string>
#include <iostream>
struct if_not_prev_space
{
// Is last encountered character space.
bool m_is = false;
bool operator()(const char c)
{
// Copy if last was not space, or current is not space.
const bool ret = !m_is || c != ' ';
m_is = c == ' ';
return ret;
}
};
int main()
{
const std::string s("abc sssd g g sdg gg gf into abc sssd g g sdg gg gf");
std::string o;
std::copy_if(std::begin(s), std::end(s), std::back_inserter(o), if_not_prev_space());
std::cout << o << std::endl;
}
You can use std::unique which reduces adjacent duplicates to a single instance according to how you define what makes two elements equal is.
Here I have defined elements as equal if they are both whitespace characters:
inline std::string& remove_extra_ws_mute(std::string& s)
{
s.erase(std::unique(std::begin(s), std::end(s), [](unsigned char a, unsigned char b){
return std::isspace(a) && std::isspace(b);
}), std::end(s));
return s;
}
inline std::string remove_extra_ws_copy(std::string s)
{
return remove_extra_ws_mute(s);
}
std::unique moves the duplicates to the end of the string and returns an iterator to the beginning of them so they can be erased.
Additionally, if you must work with low level strings then you can still use std::unique on the pointers:
char* remove_extra_ws(char const* s)
{
std::size_t len = std::strlen(s);
char* buf = new char[len + 1];
std::strcpy(buf, s);
// Note that std::unique will also retain the null terminator
// in its correct position at the end of the valid portion
// of the string
std::unique(buf, buf + len + 1, [](unsigned char a, unsigned char b){
return (a && std::isspace(a)) && (b && std::isspace(b));
});
return buf;
}
for in-place modification you can apply erase-remove technic:
#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>
int main()
{
std::string input {"asfa sas f f dgdgd dg ggg"};
bool prev_is_space = true;
input.erase(std::remove_if(input.begin(), input.end(), [&prev_is_space](unsigned char curr) {
bool r = std::isspace(curr) && prev_is_space;
prev_is_space = std::isspace(curr);
return r;
}), input.end());
std::cout << input << "\n";
}
So you first move all extra spaces to the end of the string and then truncate it.
The great advantage of C++ is that is universal enough to port your code to plain-c-static strings with only few modifications:
void erase(char * p) {
// note that this ony works good when initial array is allocated in the static array
// so we do not need to rearrange memory
*p = 0;
}
int main()
{
char input [] {"asfa sas f f dgdgd dg ggg"};
bool prev_is_space = true;
erase(std::remove_if(std::begin(input), std::end(input), [&prev_is_space](unsigned char curr) {
bool r = std::isspace(curr) && prev_is_space;
prev_is_space = std::isspace(curr);
return r;
}));
std::cout << input << "\n";
}
Interesting enough remove step here is string-representation independent. It will work with std::string without modifications at all.
I have the sinking feeling that good ol' scanf will do (in fact, this is the C school equivalent to Anatoly's C++ solution):
void remove_extra_whitespaces(char* input, char* output)
{
int srcOffs = 0, destOffs = 0, numRead = 0;
while(sscanf(input + srcOffs, "%s%n", output + destOffs, &numRead) > 0)
{
srcOffs += numRead;
destOffs += strlen(output + destOffs);
output[destOffs++] = ' '; // overwrite 0, advance past that
}
output[destOffs > 0 ? destOffs-1 : 0] = '\0';
}
We exploit the fact that scanf has magical built-in space skipping capabilities. We then use the perhaps less known %n "conversion" specification which gives us the amount of chars consumed by scanf. This feature frequently comes in handy when reading from strings, like here. The bitter drop which makes this solution less-than-perfect is the strlen call on the output (there is no "how many bytes have I actually just written" conversion specifier, unfortunately).
Last not least use of scanf is easy here because sufficient memory is guaranteed to exist at output; if that were not the case, the code would become more complex due to buffering and overflow handling.
Since you are writing c-style, here's a way to do what you want.
Note that you can remove '\r' and '\n' which are line breaks (but of course that's up to you if you consider those whitespaces or not).
This function should be as fast or faster than any other alternative and no memory allocation takes place even when it's called with std::strings (I've overloaded it).
char temp[] = " alsdasdl gasdasd ee";
remove_whitesaces(temp);
printf("%s\n", temp);
int remove_whitesaces(char *p)
{
int len = strlen(p);
int new_len = 0;
bool space = false;
for (int i = 0; i < len; i++)
{
switch (p[i])
{
case ' ': space = true; break;
case '\t': space = true; break;
case '\n': break; // you could set space true for \r and \n
case '\r': break; // if you consider them spaces, I just ignore them.
default:
if (space && new_len > 0)
p[new_len++] = ' ';
p[new_len++] = p[i];
space = false;
}
}
p[new_len] = '\0';
return new_len;
}
// and you can use it with strings too,
inline int remove_whitesaces(std::string &str)
{
int len = remove_whitesaces(&str[0]);
str.resize(len);
return len; // returning len for consistency with the primary function
// but u can return std::string instead.
}
// again no memory allocation is gonna take place,
// since resize does not not free memory because the length is either equal or lower
If you take a brief look at the C++ Standard library, you will notice that a lot C++ functions that return std::string, or other std::objects are basically a wrapper to a well written extern "C" function. So don't be afraid to use C functions in C++ applications, if they are well written and you can overload them to support std::strings and such.
For example, in Visual Studio 2015, std::to_string is written exactly like this:
inline string to_string(int _Val)
{ // convert int to string
return (_Integral_to_string("%d", _Val));
}
inline string to_string(unsigned int _Val)
{ // convert unsigned int to string
return (_Integral_to_string("%u", _Val));
}
and _Integral_to_string is a wrapper to a C function sprintf_s
template<class _Ty> inline
string _Integral_to_string(const char *_Fmt, _Ty _Val)
{ // convert _Ty to string
static_assert(is_integral<_Ty>::value,
"_Ty must be integral");
char _Buf[_TO_STRING_BUF_SIZE];
int _Len = _CSTD sprintf_s(_Buf, _TO_STRING_BUF_SIZE, _Fmt, _Val);
return (string(_Buf, _Len));
}
Well here is a longish(but easy) solution that does not use pointers.
It can be optimized further but hey it works.
#include <iostream>
#include <string>
using namespace std;
void removeExtraSpace(string str);
int main(){
string s;
cout << "Enter a string with extra spaces: ";
getline(cin, s);
removeExtraSpace(s);
return 0;
}
void removeExtraSpace(string str){
int len = str.size();
if(len==0){
cout << "Simplified String: " << endl;
cout << "I would appreciate it if you could enter more than 0 characters. " << endl;
return;
}
char ch1[len];
char ch2[len];
//Placing characters of str in ch1[]
for(int i=0; i<len; i++){
ch1[i]=str[i];
}
//Computing index of 1st non-space character
int pos=0;
for(int i=0; i<len; i++){
if(ch1[i] != ' '){
pos = i;
break;
}
}
int cons_arr = 1;
ch2[0] = ch1[pos];
for(int i=(pos+1); i<len; i++){
char x = ch1[i];
if(x==char(32)){
//Checking whether character at ch2[i]==' '
if(ch2[cons_arr-1] == ' '){
continue;
}
else{
ch2[cons_arr] = ' ';
cons_arr++;
continue;
}
}
ch2[cons_arr] = x;
cons_arr++;
}
//Printing the char array
cout << "Simplified string: " << endl;
for(int i=0; i<cons_arr; i++){
cout << ch2[i];
}
cout << endl;
}
I don't know if this helps but this is how I did it on my homework. The only case where it might break a bit is when there is spaces at the beginning of the string EX " wor ds " In that case, it will change it to " wor ds"
void ShortenSpace(string &usrStr){
char cha1;
char cha2;
for (int i = 0; i < usrStr.size() - 1; ++i) {
cha1 = usrStr.at(i);
cha2 = usrStr.at(i + 1);
if ((cha1 == ' ') && (cha2 == ' ')) {
usrStr.erase(usrStr.begin() + 1 + i);
--i;//edit: was ++i instead of --i, made code not work properly
}
}
}
I ended up here for a slighly different problem. Since I don't know where else to put it, and I found out what was wrong, I share it here. Don't be cross with me, please.
I had some strings that would print additional spaces at their ends, while showing up without spaces in debugging. The strings where formed in windows calls like VerQueryValue(), which besides other stuff outputs a string length, as e.g. iProductNameLen in the following line converting the result to a string named strProductName:
strProductName = string((LPCSTR)pvProductName, iProductNameLen)
then produced a string with a \0 byte at the end, which did not show easily in de debugger, but printed on screen as a space. I'll leave the solution of this as an excercise, since it is not hard at all, once you are aware of this.
I wrote this code to reverse strings. It works well, but when I enter short strings like "american beauty," it actually prints "ytuaeb nacirema2." This is my code. I would like to know what is wrong with my code that prints a random 2 at the end of the string. Thanks
// This program prompts the user to enter a string and displays it backwards.
#include <iostream>
#include <cstdlib>
using namespace std;
void printBackwards(char *strPtr); // Function prototype
int main() {
const int SIZE = 50;
char userString[SIZE];
char *strPtr;
cout << "Please enter a string (up to 49 characters)";
cin.getline(userString, SIZE);
printBackwards(userString);
}
//**************************************************************
// Definition of printBackwards. This function receives a *
// pointer to character and inverts the order of the characters*
// within it. *
//**************************************************************
void printBackwards(char *strPtr) {
const int SIZE = 50;
int length = 0;
char stringInverted[SIZE];
int count = 0;
char *strPtr1 = 0;
int stringSize;
int i = 0;
int sum = 0;
while (*strPtr != '\0') {
strPtr++; // Set the pointer at the end of the string.
sum++; // Add to sum.
}
strPtr--;
// Save the contents of strPtr on stringInverted on inverted order
while (count < sum) {
stringInverted[count] = *strPtr;
strPtr--;
count++;
}
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
cout << stringInverted << endl;
}
Thanks.
Your null termination is wrong. You're using == instead of =. You need to change:
stringInverted[count] == '\0';
into
stringInverted[count] = '\0';
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
Should use = here.
What is wrong with your code is that you do not even use strlen for counting the length of the string and you use fixed size strings (no malloc, or, gasp new[]), or the std::string (this is C++)! Even in plain C, not using strlen is always wrong because it is hand-optimized for the processor. What is worst, you have allocated the string to be returned (stringInverted) from the stack frame, which means when the function exits, the pointer is invalid and any time the code "works" is purely accidental.
To reverse a string on c++ you do this:
#include <iostream>
#include <string>
int main() {
std::string s = "asdfasdf";
std::string reversed (s.rbegin(), s.rend());
std::cout << reversed << std::endl;
}
To reverse a string in C99 you do this:
char *reverse(const char *string) {
int length = strlen(string);
char *rv = (char*)malloc(length + 1);
char *end = rv + length;
*end-- = 0;
for ( ; end >= rv; end --, string ++) {
*end = *string;
}
return rv;
}
and remember to free the returned pointer after use. All other answers so far are blatantly wrong :)