C++: cin >> *char - c++

So, I'm currently writing a line editor as a learning project on I/O, writing files, and the like. It is written in C++, and I am currently trying to write out to a file of the user's choosing. I have CLI arguments implemented, but I currently have no idea how to implement an in program way of specifying the file to write to.
char *filename;
if (argc >= 2){
filename = argv[1];
} else{
cout << "file>";
cin >> filename;
cin.ignore();
}
This works perfectly well when I use command line arguments; however, whenever I do not, as soon as I start the program, it Segmentation Faults. The place where I use the actual filename is in the save command:
void save(char filename[], int textlen, string file[]){
ofstream out(filename);
out << filestring(textlen, file);
out.close();
}
Which also works perfectly well. Is there any way you can help me? Full source code, for review, is up on https://github.com/GBGamer/SLED

The problem is that char* filename is just a pointer to some memory containing characters. It does not own any memory itself.
When you use the command line argument, the program handles storing that string somewhere, and you get a pointer to it. When you try to read using cin >> filename there isn't actually anywhere to store the read data.
Solution: Replace char* filename with std::string filename (and #include <string>).
Then to open the output file, you need a c-style string (null terminated char array). std::string has a function for this. You would write
std::ofstream out(filename.c_str());
^^^^^
Or, in fact, if you can use a recent compiler with c++11 features, you don't even need to use c_str(). A new std::ofstream constructor has been added to accept a std::string.

Your filename variable points to argv[1] when command line argument is provided, it does not need memory to be allocated but when going in else block, you have not allocated memory to filename. Its just a pointer.
Use malloc to assign filename some memory then take user input.
filename = (char *)malloc(sizeof(char)*(FILE_NAME_LENGTH+1))

Related

C++ reading/writing to two different files (need help understanding which syntax to use)

I'm currently working on this program for a class in my university. I've tried multiple approach with no success. I'm pretty sure it's just a conversion problem, but I want to understand the differences.
What the program supposed to do : We're to create a program that ask the user for two filenames. One will be an input and another will be an output. The program is supposed to read the input and write the line to the output while until the end of the input file is not reached.
My Code :
#include <iostream>
#include <fstream> //included for read/writing files
#include <string> //Included this for getline to read the file
using namespace std;
int main() {
ifstream infile; // Stream to read from input file
ofstream outfile; // Stream to write to output file
char inputfilename[80], outputfilename[80]; //declaring two character arrays to save the file names.
string text;
cout << "What is the name of your input file (text.txt)" ; // Prompting user for input file name
cin >> (inputfilename); // Getting input file
infile.open(inputfilename, ios::in); // Opening the input file.
cout << "What is the name of your output file" ; // Prompting user for output file name
cin >> (outputfilename);
outfile.open(outputfilename, ios::out);
if(!infile) { // If cannot open file
cout << "There was an error opening your file!" ;
return 1;
}
if (!outfile) {
cout << "There was an error opening your file!" ;
return 1;
}
while (infile.eof()==0) {
fgets(text, 80, infile);
fprintf(outfile,"%d. %s\n", text);
}
infile.close(); // Closing input file
outfile.close(); // Closing output file
return 0;
}
What I've tried : I didn't know if it was being affected by how I opened the file. I previously tried.
ifstream infile;
ofstream outfile;
char text, inputfilename[80], outputfilename[80]; <----- 1
cout << "What is the name of your input file (text.txt)" ;
gets(inputfilename); <----- 2
infile.open(inputfilename);
cout << "What is the name of your output file" ;
gets(outputfilename); <----- 2
outfile.open(outputfilename);
1) I switched char I previous tried
char text
char text[80]
char *text[80]
2) Would switching how getting the file name change anything in the while loop(I previous tried getline and gets)? Additionally the "f" in front of fgets/fprints/etc are always associated with a file stream?
Note: My teacher gave us the hint.
"Suppose you read a line from the input file into a string variable called str using the following statement: fgets(str, 80, infile);You can add a line number and save the line with the line number to the output file using the same statement using: fprintf(outfile,"%d. %s\n",Next_line_number++, str);"
from this I tried :
while (infile.eof()==0) {
fgets(text, 80, infile);
fprintf(outfile,"%d. %s\n", text);
}
as well as
while (infile.eof()==0) {
fgets(text, 80, infile);
fputs(text, outFile);
}
and
while (infile.eof()==0) {
getline(infile, text);
fprintf(outfile,"%d. %s\n", text);
}
I also tried making a long and using that to increment the line number. I'm fairly new to programming; if any of the methods I'm using our dated please let me know (on some sites they were saying fgets is dated and not supported on cx11 or some version of C++)! I want to understand the concepts vs just get the programming running. Should note Lines 34-35 are where my code is always erroring out and it's
cannot convert 'std::__cxx11::string {aka std::__cxx11::basic_string}' to 'char*' for argument '1' to 'char* fgets(char*, int, FILE*)'
I figured I was getting this because it has a pointer to the file and I'm asking the user vs having the file declared in the program. This is causing a conversion that causing my error.
fgets(text, 80, infile);
fgets() is a C library function, that expects a char * as its first parameter. It knows absolutely nothing about any C++ class, and not just std::string that you are passing as the first parameter. Neither does fgets() has any clue about the C++ std::ifstream class you're attempting to pass to it as its third parameter. And that's exactly what your compiler's error message states.
You are randomly mixing up C and C++ code, which results in repeated confusion.
char inputfilename[80], outputfilename[80];
You should also use std::strings, instead of arbitrary-sized C style arrays, here.
while (infile.eof()==0) {
This is always a bug, and read this linked article for more information.
fprintf(outfile,"%d. %s\n", text);
Again: fprintf is also a C library function, that knows absolutely nothing about C++ classes like std::string and std::ofstream. In either case, this is a bug because this string has placeholders for two parameters, an integer, %d, and C style string, %s; and you're giving just one parameter here, text. In the event that you were writing C instead of C++ code, this would not've worked either, but that's mostly academic. This is a C++ program, and this C library function has no business doing anything here, in the first place.
When you are reading from a std::ifstream:
You can use std::getline to read an entire line of text into a std::string
Alternatively you can use the >> formatted extraction operator
Or you can use various methods of the std::ifstream object to read from the file and into a suitable buffer
These alternatives are not equivalent (otherwise what would be the point?) and they do different things, and the right one to use depends on what the requirements are.
Similarly, to write to a std::ofstream you can use:
The << formatted output operator.
Various methods of the std::ofstream object itself.
And, in some advanced situations you can take advantage of the iterator library, and implement reading and writing using input and output iterators, too.
The correct approach depends on the individual situation. For more information on how to read and write from files in C++ using these approaches, see any good C++ book. Whichever C++ book actually advised you to use fgets() to read from a std::ifstream and into a std::string: throw it away, and get a better book, from the list linked above. If this is just what you found in some program somewhere on the Internet -- you can't learn C++ this way, by piecing together different parts of different programs, and hope that the results work. To fix all of your compilation errors: remove all that C code that knows absolutely nothing about C++, and replace it with proper C++ code, using any of the options I outlined above, using the examples from the C++ books linked above, as a reference.

File loader problems

i have a text file which contains authors and books lists, i need to load it to my program, here is the code of the method which should load it:
void Loader::loadFile(const char* path)
{
FILE* file = fopen(path, "r");
char* bufferString;
while (feof(file) != 1) {
fgets(bufferString, 1000, file);
printf("%s", bufferString);
}
}
I use it in my main file:
int main(int argc, char** argv) {
Loader* loader = new Loader();
loader->loadFile("/home/terayon/prog/parser/data.txt");
return 0;
}
And I get data.txt file is not completely printed.
What I should do to get data completed?
fgets reads into the memory pointed to by the pointer passed as first parameter, bufferString on your case.
But your bufferString is an uninitialised pointer (leading to undefined behaviour):
char * bufferString;
// not initialised,
// and definitely not pointing to valid memory
So you need to provide some memory to read into, e.g by making it an array:
char bufferString[1000];
// that's a bit large to store on the stack
As a side note: Your code is not idiomatic C++. You're using the IO functions provided by the C standard library, which is possible, but using the facilities of the C++ STL would be more appropriate.
You have undefined behavior, you have a pointer bufferString but you never actually make int point anywhere. Since it's not initialized its value will be indeterminate and will seem to be random, meaning you will write to unallocated memory in the fgets call.
It's easy to solve though, declare it as an array, and use the array size when calling fgets:
char bufferString[500];
...
fgets(bufferString, sizeof(bufferString), file);
Besides the problem detailed above, you should not do while(!feof(file)), it will not work as you expect it to. The reason is that the EOF flag is not set until you try to read from beyond the file, leading the loop to iterate once to many.
You should instead do e.g. while (fgets(...) != NULL)
The code you have is not very C++-ish, instead it's using the old C functions for file handling. Instead I suggest you read more about the C++ standard I/O library and std::string which is a auto-expanding string class that won't have the limits of C arrays, and won't suffer from potential buffer overflows in the same way.
The code could then look something like this
std::ifstream input_file(path);
std::string input_buffer;
while (std::getline(input_file, input_buffer))
std::cout << input_buffer << '\n';

How to name text file at a certain directory?

Ok thanks for the answer Wug! I changed my code but now it's complaining about:
no matching function for call to
std::basic_ofstream::basic_ofstream(std::basic_string)
I'm not sure it makes any difference but i'll just post all of my code it's not that much so far.
I'll try to keep it cleaner from now on.
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <fstream>
using namespace std;
int main()
{ /*Introduction*/
SetConsoleTitle("Journal");
string action, prom0, filename, filepath;
filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
cout << "Hi and welcome to Journal! \nHere you can write down your day.\nWrite help for";
cout << "more \nType command to start: ";
/*Choose Action*/
cin >> action;
if (action == "new")
{system("cls");
/*Make new Journal file*/
cout << "Filename: ";
getline(cin, filename);
mkdir("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs");
ofstream journallogs(filepath + filename);
journallogs.close();
}
else {
cout << "Wrong command\n";
};
return 0;}
There are 2 things wrong. The first is what the compiler's complaining about:
ofstream journallogs("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\" + getline(cin, filename), ios::out);
std::getline(istream&, string&) returns istream&, and you can't add char * to istream. I recommend taking a look at the documentation for getline(), which might help you understand better how you're supposed to use it. Here's an example anyway:
string filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
string filename;
getline(cin, filename);
ofstream journallogs(filepath + filename);
The second problem is that you're reading from cin into filename before calling getline(). When you call getline(), any contents of filename are dropped, so you'll effectively trim the first word off of your filename, which probably isn't what you want. To fix that, remove the extraneous cin >> filename;
Note: indentation is important and helps you read your own code. Put forth the effort to keep your code looking nice.
First, learn this:
Start small and simple.
Add complexity a little at a time.
Test at every step.
develop new functionality in isolation.
Never add to code that doesn't work.
For the rest, I don't use Windows, so I can't be certain my code will work there, but the approach will.
You are trying to 1) get a filename from the user, 2) modify it and then 3) use it to open a file; we will develop these three things in isolation.
Getting a filename from the user. Civilized filenames do not contain whitespace, so they can be read with cin, but if you want to allow whitespace you can use getline instead. Either way, test it.
Modifying the filename. Write code that assigns a value to the filename, just as it does to the path-- do not get the filename from the user, it slows down your testing and is not proper isolation. Now try to append them. If you try filepath + filename, you may get a compiler error. Here's where you must understand the difference between std::string and char[]. A char[] is an array of char, and it (usually) contains a null-terminated sequence of characters; you must read up on arrays and pointers. It is a primitive type, and you cannot simply concatenate two of them with '+', you must use something like strcat, which is dangerous if you haven't done your homework on arrays. On the other hand, std::string is more sophisticated, and can handle '+' and many other operations. If you have a std::string x and you decide you want a char[] after all, you can get one like so: x.c_str().
Opening the file. If I remember right, the ofstream constructor can take a char[], but not a std::string. Test this with a hard-coded string (isolation!).
Once you have these three components working independently, you can hook them together.

Read .part files and concatenate them all

So I am writing my own custom FTP client for a school project. I managed to get everything to work with the swarming FTP client and am down to one last small part...reading the .part files into the main file. I need to do two things. (1) Get this to read each file and write to the final file properly (2) The command to delete the part files after I am done with each one.
Can someone please help me to fix my concatenate function I wrote below? I thought I had it right to read each file until the EOF and then go on to the next.
In this case *numOfThreads is 17. Ended up with a file of 4742442 bytes instead of 594542592 bytes. Thanks and I am happy to provide any other useful information.
EDIT: Modified code for comment below.
std::string s = "Fedora-15-x86_64-Live-Desktop.iso";
std::ofstream out;
out.open(s.c_str(), std::ios::out);
for (int i = 0; i < 17; ++i)
{
std::ifstream in;
std::ostringstream convert;
convert << i;
std::string t = s + ".part" + convert.str();
in.open(t.c_str(), std::ios::in | std::ios::binary);
int size = 32*1024;
char *tempBuffer = new char[size];
if (in.good())
{
while (in.read(tempBuffer, size))
out.write(tempBuffer, in.gcount());
}
delete [] tempBuffer;
in.close();
}
out.close();
return 0;
Almost everything in your copying loop has problems.
while (!in.eof())
This is broken. Not much more to say than that.
bzero(tempBuffer, size);
This is fairly harmless, but utterly pointless.
in.read(tempBuffer, size);
This the "almost" part -- i.e., the one piece that isn't obviously broken.
out.write(tempBuffer, strlen(tempBuffer));
You don't want to use strlen to determine the length -- it's intended only for NUL-terminated (C-style) strings. If (as is apparently the case) the data you read may contain zero-bytes (rather than using zero-bytes only to signal the end of a string), this will simply produce the wrong size.
What you normally want to do is a loop something like:
while (read(some_amount) == succeeded)
write(amount that was read);
In C++ that will typically be something like:
while (infile.read(buffer, buffer_size))
outfile.write(buffer, infile.gcount());
It's probably also worth noting that since you're allocating memory for the buffer using new, but never using delete, your function is leaking memory. Probably better to do without new for this -- an array or vector would be obvious alternatives here.
Edit: as for why while (infile.read(...)) works, the read returns a reference to the stream. The stream in turn provides a conversion to bool (in C++11) or void * (in C++03) that can be interpreted as a Boolean. That conversion operator returns the state of the stream, so if reading failed, it will be interpreted as false, but as long as it succeeded, it will be interpreted as true.

binary read/write runtime failure

I've looked at binary reading and writing objects in c++ but are having some problems. It "works" but in addition I get a huge output of errors/"info".
What I've done is
Person p2;
std::fstream file;
file.open( filename.c_str(), std::ios::in | std::ios::out | std::ios::binary );
file.seekg(0, std::ios::beg );
file.read ( (char*)&p2, sizeof(p2));
file.close();
std::cout << "Name: " << p2.name;
Person is a simple struct containing string name and int age. When I run the program it outputs "Name: Bob" since I have already made a program to write to a file (so the object is already in filename).
IN ADDITION to outputting the name it also outputs:
* glibc detected * program: double free og corruption (fastttop): ***
Backtrace:
...
Memory map:
...
Abort
Is the name string in the Person struct a character array or a STL string? You can't fill in an STL String by binary reading data over top of it, since the data format is not serializable (contains pointers)
It would be interesting to see how you write the information to file as well, as well as how the Person struct is built.
If you don't have any problem that the file is plain text, my suggestion would be to write to file using string::c_str() (which returns a const char*) as well as using itoa() or itoa_s() to get the integer as a char*.
You can also have one or several constructors in Person:
Person(const std::string& name, int age);
Person(const char* name, int age);
then, when you extract the data from the file you just call the constructor with that data.
Either p2.name is a char* and you are writing and reading the pointer value, not what is pointed by it. Or p2.name is a more complex type such as std::string which is using internaly pointers with the same problem.
Serializing classes often need more work than just dumping the memory representation.
You said you wrote the Person object to a file. Did you tried to use a dump tool to see if what you have inside the file is what you are expecting?
Also did you tried to instead of using string, use a ordinary char (as #bdk pointed out) ?
When you use binary IO, the size must be fixed. If you use STL string here, it would have a problem as the size of a STL string is arbitrary.