User-input file path - c++

The program is supposed to open a text file whose path is user-input. Next, it counts the lines contained in the file and outputs them. Here's what I tried:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string path = NULL;
string garbage = NULL;
int cnt = 0;
cout << "Enter file path: ";
cin >> path;
ifstream inFile(path.c_str());
if (inFile)
{
while (!inFile.eof())
{
getline(inFile, garbage);
++cnt;
}
}
inFile.close();
cout << endl;
cout << path << " has " << cnt << " lines";
cin.ignore();
cin.get();
return 0;
}
This is what I get:
Program: C:\Windows\SYSTEM32\MSVCP120D.dll
File: c:\program files (x86)\microsoft visual studio 12.0\vc\include\xstring
Line: 1168
Expression: invalid null pointer
Note: The course I'm following has only shown me the basics of the methods used by ifstream and ofstream, like open, close and eof. So I would appreciate a solution with only these, as I'm sure you know many ways of doing this.

The class std::string is a reasonable container class, like std::vector but with an API that has a number of extra string-oriented functions.
In a particular, its use does not resemble old-fashioned C-style string handling in terms of char*, which is where I assume you got the idea of trying to use NULL as an initializer. (in modern C++ you should use the C++ keyword nullptr to create a null pointer, rather than the old C style macro NULL)
what string path = NULL; actually does is interpret NULL as a const char*, and then tries to read the C-style string at the location NULL points to so as to copy it into path. Since NULL is null rather than actually pointing to a string, you get the error message you cite.
What you really want to do is to simply use the default constructor via string path; which initializes path to be an empty string.

Don't use the meaningless NULL they're not pointers:
string path;
string garbage;

Related

Users Manually Enter Input and Output Paths C++

I have no idea about C++, but I've been assigned to edit this piece of code:
// Setup path information for output file from environmental variables
char * path = new char[100];
path = getenv("MODEL_MERGE");
char * templatePath = new char[100];
char * outputPath = new char[100];
strcpy(templatePath, path);
strcat(templatePath, "infile location");
strcpy(outputPath, path);
strcat(outputPath,"outfile location");
cout << "temp: " << templatePath << endl;
cout << "out: " << outputPath << endl;
//input output file streams for reading/writing to files
ifstream readFile(templatePath);
ofstream outFile(outputPath);
My goal is to replace the "infile location" and "outfile location", which currently point to specific files. I want the user to be able to enter the file names when running from command prompt. Sorry if this is something as simple as <<cin, but I couldn't get that to work, and I have zero experience with this language.
Got it! Everything above was replaced by:
//User inputs paths
string input;
string output;
cout<<"Input path?"<<endl;
cin>> input;
cout<<"output path?"<<endl;
cin>> output;
//input output file streams for reading/writing to files
ifstream readFile(input.c_str());
ofstream outFile(output.c_str());`
Thanks everyone for the help!
There is enough wrong with the code supplied to OP to be worth a quick going over in addition to pointing the OP in a useful direction.
First, no test for NULL on the call to getenv. If MODEL_MERGE doesn't exist, NULL is returned and then used in string copies. BOOM!
Second, newing all those arrays. Dynamically allocate only as a last resort. new must be pared with at least one delete, depending on the code's flow, to return the allocated memory for reuse when no longer needed. Since there seems to no need to dynamically allocate and the sizes of the arrays are known, they should have been defined as char templatePath[100];. Less memory management to be dealt with and effectively no possibility of leakage.
Third renders point two obsolete. Rather than using char arrays, use strings where possible. Not only do they handle all of the memory management, including resizing as needed rather than trampling out of bounds, for you, they also perform routine tasks like copying and appending with much less fuss. This bit I'll demonstrate below.
Proper use of cin and cout is well detailed on a number of sites so I won't go over it here.
Also note I've removed the need for using namespace std; by explicitly stating the namespace at use. Read why using namespace std; is often a bad idea.
#include <fstream>
#include <iostream>
int main()
{
char * Model_MergePath = getenv("MODEL_MERGE");
if (Model_MergePath != NULL)
{ //MODEL_MERGE is defined
std::string path(Model_MergePath); //replace icky and fault-prone char array
std::string templatePath = path; // copy strings with =
std::string outputPath; // not assigning path here so I can demonstrate
//something else later
std::string inFileLoc; // new throw away variables for user input.
std::string outFileLoc; // could use the same var for both. I didn't for clarity
std::cin >> inFileLoc; // get input
templatePath += inFileLoc; // append to strings with +=
std::cin >> outFileLoc;
outputPath = path + outFileLoc; // concatenate strings with +
// validate paths for correctness and possible intrusion attempts here
// this, I'm afraid, is up to the OP as the security requirements are unknown
std::cout << "temp: " << templatePath << std::endl;
std::cout << "out: " << outputPath << std::endl;
//input output file streams for reading/writing to files
std::ifstream readFile(templatePath);
// older C++ compilers may require a c-style string as the file path
std::ofstream outFile(outputPath.c_str());
// do stuff with readFile and outFile
// remove the deletes that should have corresponded to the replaced `new`s
return 0;
}
else
{ //MODEL_MERGE is NOT defined
std::cerr << "Cannot find environment variable MODEL_MERGE. Exiting." << std::endl;
return -1;
}
}

Store individual words from file into an element in string class array (C++)

I am trying to insert several string class arrays (taken from an input file) within a string class array.
The program I am writing consists of a Dictionary class in which the default constructor takes in a filename (eg."words.txt") as a parameter, and thereby stores each individually read word into a single element in a String class array.
The text file would look something like:
example
text
file
here
etc...
The code I have written to test it out so (which is not working at all) is below:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Dictionary(const char *file) {
public:
string wordList;
int numWords;
};
Dictionary::Dictionary(const char *file) {
ifstream inFile;
int SIZE = 50000;
char pass[SIZE+1];
numWords = 0;
inFile.open(file);
if(!inFile)
cout << "File can not be opened" << endl;
else
while(inFile.getline(pass, SIZE)){
wordList[numWords] = pass[SIZE+1];
numWords++;
}
}
inFile.close();
cout << wordList[5] << endl;
cout << numWords << endl;
}
int main(){
Dictionary *foo = new Dictionary("words.txt");
};
The code compiles but prints:
//should print out the 5th word in the file but nothing
0
I am wondering what exactly am I missing here? I've been worked up about this overnight and I feel the solution is so simple but I'm missing the point.
My main problem seems to be the error message that prints out throughout my other trials concerning an "invalid conversion from 'char*' to 'char'. Further, other functions in the program not shown require the letters of each word in the elements of the String class array to be manipulated (preferable as a string class array instead of C-string). I'm at a lost here. Please help?
I found this helpful link but it's for Java. What would be an equivalent for C++?
http://www.mathcs.emory.edu/~cheung/Courses/170/Syllabus/09/String-array.html
A couple places to start:
You're trying to use wordList, which is of type string, like it's an array of strings. wordList[5] will give you the sixth character in wordList... assuming you've assigned it correctly.
SIZE appears to be undefined.

Unusual/Wrong Behavior of C++ Program

My code is intended to tell the user whether the string entered is a keyword in c++.
I am reading the keywords from a file into a set and then checking if the user supplied string is in it.
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <fstream>
using namespace std;
int main()
{
set<string> key;
fstream fs;
string b;
fs.open("keywords.txt",fstream::in);
while(getline(fs,b))
key.insert(b);
b.clear();
for(auto x:key)
cout << x << endl;
cout << "Enter String user\nPress exit to terminate\n";
while(getline(cin,b))
{
if(b == "exit")
break;
if(key.find(b) != key.end())
cout << "This is a keyword\n";
else
cout << "This is a not a keyword\n";
b.clear();
}
fs.close();
}
The keywords.txt file is just a list of keywords and can be obtained from here
The problem is that my program reads all keywords correctly but for some of them such as false,public it cannot find them in the set.
i.e. when I enter false as user input
it says, "This is not a keyword."
Considering your input file, I think you have some keyword names with trailing spaces.
"catch "
"false "
You can trim the strings before inserting in the set to remove spaces, using boost::trim or your own trim (see this question for instance.)
(If you want some advice as for your code:
You can use std::ifstream like this for input file streams:
std::ifstream file( "keywords.txt" );
You do not need to call .close() at then of the scope, it will be done automatically thanks to RAII.
You should not reuse the same std::string objects for every purpose, you can declare new string objects close to their use. You should give them better names like "line" instead of "b". Doing this, you don't need to call ".clear()" for your strings.
Every line has just one word, you could use while(fs>>b) the >> will ignore the spaces (from moldbinlo & wangxf comments)
)

Problems using pointers c++

#include <iostream>
#include <fstream>
#include <cstring>
#include <map>
using namespace std;
int main()
{
cout << "Hello world!" << endl;
//define a bool to determine if you're still in the file's header section or not
bool header = true;
//define the map as a multidimensional string array that stores up to 100 z-levels
//and 50x50 tiles per z level
char* worldmap[100][50][50];
int zLevel=0;
//header declaration map
map<string, char*> declarations;
//create an input file stream object
ifstream file;
//open file
file.open("map1.dmm");
//validate file
if(!file.good()){return 1;}
//begin reading the file
while(!file.eof()){
//create a character array to write to
char line[512];
//read the file line by line; write each line to the character array defined above
file.getline(line, 512);
//header check
if(header){
if(!line[0]){
header = false;
break;
}else{
bool declaringKey=true;
char* key={};
char* element={};
char* token[20]={};
token[0] = strtok(line, "\"()");
for(unsigned int n = 0;n<20;n++){
if(n>0)token[n] = strtok(NULL, "\"()");
//cout << token[0] << endl;
if(!token[n] || (token[n])[1] == '=')continue;
if(declaringKey){
key = token[n];
declaringKey=false;
}else{
//cout << "pow" <<endl;
element = token[n];
cout<<"declarations[" << key << "] = " << element << endl;
declarations.emplace(key,element); //<-------------- problem line, i think
cout << declarations[key] << endl;
}
}declaringKey=true;
}
}else{
if(!line[0]) {
zLevel++;
continue;
}
}
}
//string test = "aa";
return 0;
}
I'm trying to create a map loader that loads a simple map from a text file. I'm aware that there are other map loaders available but most of them do far more than I need them to. So far, this code only reads the header, which basically defines what each set of characters represents as a tile, for example: "aa" = "sand tile"
The problem is, when I'm emplacing the key/element into the declarations map, it seems to use the same element for all keys. I'm assuming that this is because by defining a character pointer it always points to the same data, and only serves the purpose of changing the value contained by that pointer, rather than allocating new data and keeping them separate.
But that raises another question, why does it emplace a different key with the same element, even though both are pointers..? Anyways,
How can I make it so that all keys/elements are independent character arrays, rather than pointers to the exact same space carved out by the array..?
EDIT: You can just assume the code works other than the fact that it stores the same element to all keys. Also, the element it stores is always the last one that's parsed out of the file's header section.
Just use a std::string for the value, too. This should solve your immediate problem.
That said, do not use stream.eof() to control a loop reading values! It does not work. Instead, always try to read from a stream and then verify if the read was successful, e.g.:
while (file.getline(line, sizeof(line))) {
// ...
}
Personally, I wouldn't read into a fixed size buffer and use a std::string instead:
for (std::string line; std::getline(file, line); ) {
// ...
}
From this point I would also not use strtok() but rather either the members of std::string or suitable algorithms. This way I also wouldn't let astray, considering it a good idea to store pointers (not to mention that I can't deal with pointers and, thus, my programs don't use them).

C++ Fstream Only Prints One Word

This is a very strange issue. I'm trying to print a large text file, it's a Wikipedia entry. It happens to be the page on Velocity. So, when I tell it to print the file, it prints "In", when it should print "In physics, velocity is etc, etc etc".
Here's the code I'm using to print out:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream wiki;
wiki.open("./wiki/velocity.txt");
char* wikiRead;
wiki >> wikiRead;
cout << wikiRead << endl;
wiki.close();
}
Please help.
wiki >> wikiRead;
The default delimiter for stream is space, so when the stream encounters a space, it simply stops reading, that is why it reads only one word.
If you want the stream to read all words, the you've to use a loop as:
char* wikiRead = new char[1024]; //must allocate some memory!
while(wiki >> wikiRead)
{
cout << wikiRead << endl;
}
wiki.close();
delete []wikiRead; //must deallocate the memory
This will print all the words in the file, each on a new line. Note if any of the word in the file is more than 1024 character long, then this program would invoke undefined behavior, and the program might crash. In that case, you've to allocate a bigger chunk of memory.
But why use char* in the first place? In C++, you've better choice: Use std::string.
#include<string>
std::string word;
while(wiki >> word)
{
cout << word << endl;
}
wiki.close();
Its better now.
If you want to read line-by-line, instead of word-by-word, then use std::getline as:
std::string line;
while(std::getline(wiki, line))
{
cout << line << endl;
}
wiki.close();
This will read a complete line, even if the line contains spaces between the words, and will print each line a newline.
You ask the stream to read the (binary) value of a pointer (probably 4 bytes, depending on your machine architecture), then you ask it to print the text pointed to by those 4 bytes!
I wonder why you ignored the compiler warning (most of the modern compiler warns you about using uninitialized variables). How about this?
ifstream wiki;
wiki.open("./wiki/velocity.txt");
char wikiRead[255];
wiki >> wikiRead;
cout << wikiRead << endl;
wiki.close();
Alternatively I'd suggest you to use string object with getline to get a single line of text.
string str;
getline(wiki, str);
The >> operator applied to a char * reads only one word. Moreover, you're reading into an uninitialized pointer, which is not valid. Usually std::string, not char *, is used for string processing in C++.
If you only want to print the file's contents, you can hook the file's buffer directly to std::cout:
int main() {
std::ifstream wiki("./wiki/velocity.txt");
std::cout << wiki.rdbuf() << '\n';
}
If you want to put the contents into an automatically-allocated string, use std::getline with the delimiter disabled.
int main() {
std::ifstream wiki("./wiki/velocity.txt");
std::string wiki_contents;
getline( wiki, wiki_contents, '\0' /* do not stop at newline */ );
std::cout << wiki_contents << '\n'; // do something with the string
}
Since you want to read a large file, reading it block by block is a better way.
ifstream wiki;
wiki.open("./wiki/velocity.txt");
const int buf_size = 1024;
char* wikiRead = 0;
int cnt = 1;
do
{
wikiRead = realloc( wikiRead, bufsize*cnt );
wiki.Read( wikiRead + (bufSize*(cnt-1)), buf_size ); //appends to reallocated memory
cnt++;
}while( !wiki.eof())
wikiRead[(bufSize*(cnt-2)) + wiki.gcount() + 1] = '\0'; // null termination.
wiki.Close();
cout << wikiRead;
delete[] wikiRead;
The operator>> is designed to only read one word at a time. If you want to read lines, use getline.
#include <iostream>
#include <fstream>
#include<string>
using namespace std;
int main()
{
ifstream wiki;
wiki.open("./wiki/velocity.txt");
string wikiRead;
while (getline(wiki, wikiRead))
{
cout << wikiRead << endl;
}
wiki.close();
}