I have a small assignment which partly requires me to take inputs from a file in the form of strings and place them into char arrays so I can check if the string contains any '*' character at the end of it.
I have been able to extract the strings from the files successfully, however i have failed to find a way in which to place them in char arrays so i can process them.
I would be very grateful if someone would let me know how to place a string into char arrays using cstring library. Please keep in mind that the strings are taken from a file and not as user input.
some of the ways i tried is the following:
//Try 1
char CstringArray[] = LineFromFile;
//Try 2
char CstringArray[100] = LineFromFile;
//Try 3
ifstream Test("Test.txt");
Test>>CstringArray;
//Try 4
ifstream Test("Test.txt");
Test>>CstringArray[0];
Thank you very much
Since this is an assignment, your professor will probably not be happy with you using all of C++'s functionality, particularly if you don't understand it, but since it's a one liner I figured I'd tell you how I'd print all strings ending in an asterisks. Given that you have successfully opened the file to ifstream Test you can do:
copy_if(istream_iterator<string>(Test), istream_iterator<string>(), ostream_iterator<string>(cout, " "), [](const auto& i) { return !empty(i) && i.back() == '*'; })
EDIT:
I'm using an istream_iterator to read in each string in Test and istream_iterator, I'm operating on these values immediately, but if you needed to start by saving all the strings to a vector<string> you could also do this: vector<string> CstringArray{ istream_iterator<string>(Test), istream_iterator<string>() }
I'm using an ostream_iterator to directly stream out my selected strings rather than storing them
I'm using copy_if to iterate over all the strings that are streamed in, selecting only those that meet a given criteria
I'm using the lambda: [](const auto& i) { return !empty(i) && i.back() == '*'; } to conditionally select non-empty strings which end with an asterisks character
Related
I am trying to read data from a text file using C++ & store the strings at each line into wchar_t [] or LPCWSTR.
(These 2 datatypes are the constraints of the application on which I am working. That's why I have to store the data in these datatypes)
The format of data in the .txt file is, for example:
abc\\def\\ghi 10
jkl\\mnopq\\rstq 20
aqq\\sdsds\\qc 30
I am trying to read data line by line & save each line as a map's key-value pair, where key is of type LPCWSTR or wchar_t[] type & value is of int type
There is no issue in extracting int, but the issue comes in reading the strings
Here is my code:
#include<iostream>
#include<fstream>
#include<windows.h>
#include<cstdlib>
using namespace std;
int main()
{
wchar_t test1[260];
const char* s = "Hello\\ABC\\DEF";
mbstowcs(test1, s, strlen(s));
wcout<<test1<<endl;
wchar_t gr[260];
string gr_temp;
int percentage;
ifstream ifs;
ifs.open("data.txt", ifstream::in);
if (ifs.is_open()) {
while (ifs >> gr_temp >> percentage){
const char* source = gr_temp.c_str();
mbstowcs(gr, source, strlen(source));
wcout<<gr<<L" ";
cout<<percentage<<endl;
}
ifs.close();
}
return 0;
}
However, it is giving the following output:
Hello\ABC\DEFa
abc\\def\\ghi 10
jkl\\mnopq\\rstq 20
aqq\\sdsds\\qc 30
I did not understand why that tiny 'a' appeared out of nowhere in the first line of output
I want the code to instead automatically process those double slashes, i.e. I want the output as:
Hello\ABC\DEF
abc\def\ghi 10
jkl\mnopq\rstq 20
aqq\sdsds\qc 30
It would be even best if I could instead write the entries in the .txt file without double slashes & they get automatically processed without checking for any escape sequences. However, since the issue as in point no. 1) above is there, so I am not sure if it is even possible
Even if add cout<<gr_temp<<endl; as the first line in the while loop, even that also outputs the string with double backward slashes.
What am I missing or doing wrong?
Update:
Also, when I add these key-value pairs to a std::map<LPCWSTR,int> m1 using the statement m1[gr] = percentage; at the end of each while loop, then with the print statement, it only shows one single element in the map.
My updated code is:
#include<iostream>
#include<fstream>
#include<windows.h>
#include<cstdlib>
#include<map>
using namespace std;
std::unordered_map<LPCWSTR, int> m1;
int main()
{
wchar_t test1[260];
const char* s = "Hello\\ABC\\DEF";
mbstowcs(test1, s, strlen(s));
wcout<<test1<<endl;
wchar_t gr[260];
string gr_temp;
int percentage;
ifstream ifs;
ifs.open("data.txt", ifstream::in);
if (ifs.is_open()) {
while (ifs >> gr_temp >> percentage){
const char* source = gr_temp.c_str();
mbstowcs(gr, source, strlen(source));
m1[gr] = percentage;
}
ifs.close();
}
for (auto i = m1.begin(); i != m1.end(); i++) {
wcout<< i->first << L" ";
cout<< i->second << endl;
}
return 0;
}
This code is only adding 1 element in the map & that is the most recent added element.
I edited the code to use unordered_map, but still the same issue.
I further tried to print the size() of the map. In both these cases, size of map m1 was displayed as 1.
Miles Budnek already stated your problems.
If you look at the documentation of your function (http://www.cplusplus.com/reference/cstdlib/mbstowcs/), you will see that the third parameter does not expect the number of bytes to translate to wchar_t, but much rather the maximum number of characters the buffer you are pointing to can hold.
It will stop once it finds a \0 (which just happens to be what strlen is also looking for).
So just replace the third parameter of your first mbstowcs call with 260 (or sizeof(test1)/sizeof(wchar_t) and you're good on that stray 'a'.
As has also already been stated, there are no 'escape parameters' while reading from a file.
These only exist in source code and represent ASCII codes you cannot type. (https://www.asciitable.com/)
\n for example represents the codesign for 'new line' 0x0A.
So escaping the backslashes in the file is unnecessary and can be skipped.
If you know that your input file will have 'double backslashes' and need to 'unescape' them, you could look at the std::string functions 'find' and 'replace'.
Find "\\\\" (two backslashes in a row) and replace with "\\".
In response to your updated question (which is basically another question):
The problem is the key you chose for the map.
Each map, unordered or not, requires unique keys and in your scenario, you keep using the same key.
LPCWSTR expands to 'Pointer to Wide Char String', so while you probably think you are using 'abc\def\ghi' as key, you are actually using &gr[0], which remains the same during all iterations.
As an additional result, once the program leaves the scope of gr, its content becomes invalid and accessing the map (which maintains the pointer but not the content), will access freed memory which tends to crash your program.
The solution as such is simple enough though: You need to use the content as key, instead of the pointer, for example by using a container object like std::wstring.
Here is the content of txt file that i've managed read.
X-axis=0-9
y-axis=0-9
location.txt
temp.txt
I'm not sure whether if its possible but after reading the contents of this txt file i'm trying to store just the x and y axis range into 2 variables so that i'll be able to use it for later functions. Any suggestion? And do i need to use vectors? Here is the code for reading of the file.
string configName;
ifstream inFile;
do {
cout << "Please enter config filename: ";
cin >> configName;
inFile.open(configName);
if (inFile.fail()){
cerr << "Error finding file, please re-enter again." << endl;
}
} while (inFile.fail());
string content;
string tempStr;
while (getline(inFile, content)){
if (content[0] && content[1] == '/') continue;
cout << endl << content << endl;
depends on the style of your file, if you are always sure that the style will remain unchanged, u can read the file character by character and implement pattern recognition stuff like
if (tempstr == "y-axis=")
and then convert the appropriate substring to integer using functions like
std::stoi
and store it
I'm going to assume you already have the whole contents of the .txt file in a single string somewhere. In that case, your next task should be to split the string. Personally, yes, I would recommend using vectors. Say you wanted to split that string by newlines. A function like this:
#include <string>
#include <vector>
std::vector<std::string> split(std::string str)
{
std::vector<std::string> ret;
int cur_pos = 0;
int next_delim = str.find("\n");
while (next_delim != -1) {
ret.push_back(str.substr(cur_pos, next_delim - cur_pos));
cur_pos = next_delim + 1;
next_delim = str.find("\n", cur_pos);
}
return ret;
}
Will split an input string by newlines. From there, you can begin parsing the strings in that vector. They key functions you'll want to look at are std::string's substr() and find() methods. A quick google search should get you to the relevant documentation, but here you are, just in case:
http://www.cplusplus.com/reference/string/string/substr/
http://www.cplusplus.com/reference/string/string/find/
Now, say you have the string "X-axis=0-9" in vec[0]. Then, what you can do is do a find for = and then get the substrings before and after that index. The stuff before will be "X-axis" and the stuff after will be "0-9". This will allow you to figure that the "0-9" should be ascribed to whatever "X-axis" is. From there, I think you can figure it out, but I hope this gives you a good idea as to where to start!
std::string::find() can be used to search for a character in a string;
std::string::substr() can be used to extract part of a string into another new sub-string;
std::atoi() can be used to convert a string into an integer.
So then, these three functions will allow you to do some processing on content, specifically: (1) search content for the start/stop delimiters of the first value (= and -) and the second value (- and string::npos), (2) extract them into temporary sub-strings, and then (3) convert the sub-strings to ints. Which is what you want.
I have a file which contains records of students in the following format.
Umar|Ejaz|12345|umar#umar.com
Majid|Hussain|12345|majid#majid.com
Ali|Akbar|12345|ali#geeks-inn.com
Mahtab|Maqsood|12345|mahtab#myself.com
Juanid|Asghar|12345|junaid#junaid.com
The data has been stored according to the following format:
firstName|lastName|contactNumber|email
The total number of lines(records) can not exceed the limit 100. In my program, I've defined the following string variables.
#define MAX_SIZE 100
// other code
string firstName[MAX_SIZE];
string lastName[MAX_SIZE];
string contactNumber[MAX_SIZE];
string email[MAX_SIZE];
Now, I want to pull data from the file, and using the delimiter '|', I want to put data in the corresponding strings. I'm using the following strategy to put back data into string variables.
ifstream readFromFile;
readFromFile.open("output.txt");
// other code
int x = 0;
string temp;
while(getline(readFromFile, temp)) {
int charPosition = 0;
while(temp[charPosition] != '|') {
firstName[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != '|') {
lastName[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != '|') {
contactNumber[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != endl) {
email[x] += temp[charPosition];
charPosition++;
}
x++;
}
Is it necessary to attach null character '\0' at the end of each string? And if I do not attach, will it create problems when I will be actually implementing those string variables in my program. I'm a new to C++, and I've come up with this solution. If anybody has better technique, he is surely welcome.
Edit: Also I can't compare a char(acter) with endl, how can I?
Edit: The code that I've written isn't working. It gives me following error.
Segmentation fault (core dumped)
Note: I can only use .txt file. A .csv file can't be used.
There are many techniques to do this. I suggest searching StackOveflow for "[C++] read file" to see some more methods.
Find and Substring
You could use the std::string::find method to find the delimiter and then use std::string::substr to return a substring between the position and the delimiter.
std::string::size_type position = 0;
positition = temp.find('|');
if (position != std::string::npos)
{
firstName[x] = temp.substr(0, position);
}
If you don't terminate a a C-style string with a null character there is no way to determine where the string ends. Thus, you'll need to terminate the strings.
I would personally read the data into std::string objects:
std::string first, last, etc;
while (std::getline(readFromFile, first, '|')
&& std::getline(readFromFile, last, '|')
&& std::getline(readFromFile, etc)) {
// do something with the input
}
std::endl is a manipulator implemented as a function template. You can't compare a char with that. There is also hardly ever a reason to use std::endl because it flushes the stream after adding a newline which makes writing really slow. You probably meant to compare to a newline character, i.e., to '\n'. However, since you read the string with std::getline() the line break character will already be removed! You need to make sure you don't access more than temp.size() characters otherwise.
Your record also contains arrays of strings rather than arrays of characters and you assign individual chars to them. You either wanted to yse char something[SIZE] or you'd store strings!
vector<string> CategoryWithoutHashTags;
string tester = "#hello junk #world something #cool";
char *pch;
char *str;
str = new char [tester.size()+1];
strcpy(str, tester.c_str());
pch = strtok(str,"#");
while(pch!=NULL)
{
CategoryWithoutHashTags.push_back(pch);
pch=strtok(NULL,"#");
}
cout<<CategoryWithoutHashTags[0]<<endl;
I want to write a program which involves storing all the hash tags words in a vector of strings. The above program stores "hello junk" in the first index rather than "hello". What changes can i make to the program to make it do so?
If you are set on using strtok, you should at the very least use its re-entrant version strtok_r. Then, you should change the code to split at spaces, not at hash marks. This would give you the tokens. Finally, in the loop you would need to look for the first character to be the hash mark, adding the item to the list if it's there, and disregarding the item when the hash mark is not there.
An even better approach would be using a string stream: put your string into it, read tokens one by one, and discard ones with no hash mark.
Here is how you can do it with very little code using C++11's lambdas:
stringstream iss("#hello junk #world something #cool");
istream_iterator<string> eos;
istream_iterator<string> iit(iss);
vector<string> res;
back_insert_iterator< vector<string> > back_ins(res);
copy_if(iit, eos, back_ins, [](const string s) { return s[0] == '#'; } );
Demo on ideone.
I am maintaining a C++ method which one of my clients is hitting an issue with. The method is supposed to write out a series of identifiers to a file delimited by a new line. However on their machine somehow the method is writing a series of NULL's out to the file. Opening the file in a binary editor shows that it contains all zeros.
I can't understand why this is happening. I've tried assigning empty strings and strings with the first character set to 0. There is no problem creating the file, just writing the identifiers to it.
Here is the method:
void writeIdentifiers(std::vector<std::string> IDs, std::string filename)
{
std::ofstream out (filename.c_str(), std::ofstream::binary);
if (out.is_open())
{
for (std::vector<std::string>::iterator it = IDs.begin();
it != IDs.end();
it++)
{
out << *it << "\n";
}
}
out.close();
}
My questions: is there any possible input you can provide that method which will create a file which has NULL values in it?
Yeah, the following code quite clearly writes a series of NULL bytes:
std::vector<std::string> ids;
std::string nullstring;
nullstring.assign("\0\0\0\0\0\0\0\0\0\0", 10);
ids.push_back(nullstring);
writeIdentifiers(ids, "test.dat");
Because the std::string container stores the string length, it can't necessarily be used in the same way as an ordinary C (null-terminated) string. Here, I assign a string containing 10 NULL bytes. Those are then output because the string length is 10.