I have a c++ program which reads a text file and then converts that text file to a string. Then, it converts the string to a character array using strncpy. I have already seen the stackoverflow question on strncpy and taken the necessary precautions in order to avoid the issues it causes when creating the array. Could someone please explain why it still causes a stack error.
#include <iostream>
#include <string.h>
#include <random>
#include <fstream>
#include <istream>
#include <sstream>
#include <stdio.h>
using namespace std;
int main()
{
//open a stream reader
ifstream fin;
//opens the text file in the stream reader
fin.open("songlyrics.txt");
//will be used to aggregate all characters in text file
string song;
//used as a pointer when reading each character in text file
char ch;
//while the end of file is not reached
while(!fin.eof())
{
//get the character from the file and add it to the song string
fin.get(ch);
song += ch;
}
//close the file
fin.close();
//make a character array called lyrics_ with a length of the length of song
char lyrics_[song.length()];
//use strncpy to convert song to a char array, lyrics_
strncpy(lyrics_, song.c_str(), sizeof(lyrics_));
//avoid the segmentation fault
lyrics_[sizeof(lyrics_) - 1] = 0;
cout<<lyrics_;
return 0;
}
This:
char lyrics_[song.length()];
// ^^^^^^^^^^^^^
// not a compile-time constant
Is a variable-length array, and is not standard C++.
Also, you don't need to convert a std::string to a character array. It kind of already is:
char* lyrics = &song[0]; // assuming you don't append to song in the future
If you really want a separate character buffer, you'll have to allocate it dynamically:
char* lyrics = new char[song.length() + 1];
memcpy(lyrics, song.c_str(), song.length() + 1); // this will copy the null terminator
delete [] lyrics; // don't forget this
C++ does not support the variable length array feature from C. VLA was a standard feature of C.1999, and an optional feature in C.2011.
If you want to make a copy of the contents of a string into a dynamically sized array of char, you can use a vector:
std::vector<char> lyrics_(song.begin(), song.end());
lyrics_.push_back('\0');
std::cout << &lyrics_[0];
Related
1I am currently working on a project of creating my own game in OpenGL. My problem is right now, that if I read a file, that my function reading that file results in a SIGABRT, because of something inside the std::ifstream deconstructor (more specifically in "std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()"). This function previously worked for me, but suddenly stopped working.
My Goal is simple: A reliable implementation for reading a file to a char*. Multi threading is currently not my concern.
Here is my implementation of the file reading function.
It takes in a path, and should write the content of the file at that path into the out parameter.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
#include <cassert>
#include "Utility.h"
char * Utility::readFile(const char* path,char*& out){
#ifndef NDEBUG
std::cout<<"Getting file: "<<path<<"\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file=std::ifstream(path);
assert(file.good());
if(!file.good())
{
throw std::runtime_error((std::string)"Couldn't open file for loading: "+path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;buffer << file.rdbuf();
std::string fileContentsStr = buffer.str();
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
return out;
}
My code is located at C0D3-M4513R/OpenGlGame.
I already tried a minimal example, which is working and using the same compile flags (except linker flags). test.txt and test1.txt just contain some rubbish text generated by randomly hacking on my keyboard.
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
//This Function is the same as the one above!!!
char *readFile(const char *path, char *&out) {
#ifndef NDEBUG
std::cout << "Getting file: " << path << "\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file = std::ifstream(path);
assert(file.good());
if (!file.good()) {
throw std::runtime_error((std::string) "Couldn't open file for loading: " + path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;
buffer << file.rdbuf();
//convert the stringstream to a string
std::string fileContentsStr = buffer.str();
//copy the contents of the string to a char array
out = new char[fileContentsStr.size()];
strcpy(out, fileContentsStr.c_str());
//return char array address (which should be the same as the start?)
return out;
}
int main() {
//The programm started!
std::cout << "Hello, World!" << std::endl;
//Define a space for the contents of the file to live
char *out;
//Read the contents of a file
out = readFile("test.txt", out);
//Print contents of the file
std::cout << out << std::endl;
char *out1;
//Read the contents of a file
out1 = readFile("test1.txt", out1);
//Print contents of the file
std::cout << out1 << std::endl;
return 0;
}
strcpy:
Copies the character string pointed to by src, including the null terminator, to the character array whose first element is pointed to by dest.
The behavior is undefined if the dest array is not large enough. The behavior is undefined if the strings overlap.
c_str:
Returns a pointer to a null-terminated character array with data equivalent to those stored in the string.
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
You need to be careful when mixing std::string with c-strings, because a std::string is not null-terminated and does not count the nullterminator for its size. However, c_str does return a pointer to a null-terminated character array.
You are asking strcpy to write fileContentsStr.size()+1 (size + null terminator) into a char array with only fileContentsStr.size() elements.
PS: As mentioned in a comment, you should consider to return a std::string instead. You are using a raw owning pointer which is error prone and should be avoided. Either use a smart-pointer or let a std::string manage the char-array (thats what its made for actually ;).
I am so sorry, I know this is a basic problem, but I couldn't find an answer anywhere. Maybe I didn't realize the key word. The problem is:
I want to get inputs from user, which will be stored in the check[] array, then I see through it to get if inputs are valid or invalid (valid if it is like SpaceSpace+0000[i=1-5]).
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <string>
#include <cstring>
void main(){
char check[7];
for(i=0;i<7;i++){
check[i]='1';
};
//int check[4];
cin.getline(check,4);
bool cond=1;
for(i=0;i<7;i++){
cout<<check[i];
};
}
But when I print (cout) the array, I realized this: Input is 33, array[] is "33'Space'1111", my question here is what does the space in output mean and how could I deal with it(ignore, remove or anything).
The problem is that istream::getline reads the input, and then write a null-terminated byte string to your array.
What you see as a "space" is simply the null-terminator '\0'.
If you want to read raw unformated characters, use a loop to read one character at a time instead (checking for eof and newline):
char ch;
// Read up to four character, or until there's an error or end-of-file,
// or until you get a newline
for (unsigned i = 0; i < 4 && cin.get(ch) && ch != '\n'; ++i)
{
check[i] = ch;
}
I have a text format that uses latitude, longitude and name of location
, for example:
41.3333 34.3232 Old Building
I have to read this text file (from the command line), split each line by white space, use stod to convert the lat and long back into a double, then read the whole file into a vector or a list.
This is what I currently I am stuck on:
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
class Distance{
public:
double x;
double y;
string location;
};
int main(int argc, char *argv[]){
// If the user didn't provide a filename command line argument,
// print an error and exit.
if (argc <= 1){
cout << "Usage: " << argv[0] << " <Filename>" << endl;
exit(1);
}
char *pFilename = argv[1];
string buf; // Have a buffer string
stringstream ss(argv[1]); // Insert the string into a stream
vector<string> tokens; // Create vector to hold our words
while (ss >> buf)
tokens.push_back(buf);
}
Question:
Could I have some insight on how to proceed with the implementation?
Answer: from here I need to look at each line in the file and split them by whitespace then store the file in a vector by what they are. So the first number of the text file would be latitude, second longitude, and third (string) is location.
These are some general points whenever you end up using C++ :-
Avoid pointers if you can. Prefer references or composite classes like string in place of char *.
The C++ reference online can help you find out the correct usage very easily.
GDB can help you in most of the cases for such problems as in your question.
As suggested in the comments , you have to read the file in the string stream first and then only you can parse it. I have not compiled the code below but I hope it gives you a good idea about how to do this.In this case, the file is standard input. You can read from that in the following manner :-
char buffer[1000]; // assumine each line of input will not be more than this
while(cin.getline(buffer , 1000)) // constant 1000 can be moved to a MACRO
{
// cin does not eat up the newline character. So we have to do something like this to make it work
cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
//discard characters until newline is found
stringstream ss(buffer); // Insert the string into a stream
vector<string> tokens; // Create vector to hold our words
string buf ;
Distance distance ;
ss>>buf;
distance.x = stod(buf);
tokens.push_back(buf);
ss>>buf;
distance.x = stod(buf);
tokens.push_back(buf);
ss>>buf;
distance.x = buf;
tokens.push_back(buf);
}
I tried to make a code that reads from text file called aisha
This is a new file I did it for as a trial for university
but it worked =)
Its about Removing stopwords from the file
and apply casefolding to it
It tried doing that many times
and finally now I could do now
and then the code stores the read text on an array and then removes the stopwords from it
but now I nead to make the case folding step
the problem that this code reads the text file word by word
I want to read it char by char so I can apply casefolding to each char
is there ant way to make the code read the aisha file char by char ?
#include <iostream>
#include <string>
#include <fstream>
int main()
{
using namespace std;
ifstream file("aisha.txt");
if(file.is_open())
{
string myArray[200];
for(int i = 0; i < 200; ++i)
{
file >> myArray[i];
if (myArray[i] !="is" && myArray[i]!="the" && myArray[i]!="that"&& myArray[i]!="it"&& myArray[i]!="to"){
cout<< myArray[i]<<" ";
}
}
}
system("PAUSE");
return 0;
}
If you declare your array as an array of char instead of array of strings, the extraction operator shall automatically read char.
Also you will have to be careful because the >> operator by default skips the whitespace characters. If you want to read the whitespaces also, then you should add noskipws before reading the characters.
file >> std::noskipws;
The C++ way to do this is explained at this link: http://www.cplusplus.com/reference/istream/istream/get/
#include <iostream> // std::cin, std::cout
#include <vector> // store the characters in the dynamic vector
#include <fstream> // std::ifstream
int main () {
std::ifstream is("aisha.txt"); // open file and create stream
std::vector <char> stuff;
while (is.good()) // loop while extraction from file is possible
{
char c = is.get(); // get character from file
if (is.good())
std::cout << c; // print the character
stuff.push_back(c); // store the character in the vector
}
is.close(); // close file
return 0;
}
Now you basically have every character of the file stored in the vector known as stuff. You can now do your modification to this vector, for it is a far easier internal representation of data. Also, you have access to all the handy STL methods.
Use Whole string instead of reading char by char
using readline function.
I'm making a shift cipher that reads in text from a file and decodes it. The decryption works fine howver i can't figure out how to find the length of the file without hardcoding it into the size of the char array. It also only reads in one line, anything with a newline in corrupts.
Any help would be greatly appreciated, i've left out the main block of code as that deals with the array after it has been read in and seemed a bit long and irrelevant.
string fileName;
cout << "Please enter the locations of your encrypted text (e.g ""encryptedText.txt""): ";
getline( cin, fileName );
char encryptedMessage[446]; //How do i read in the file length and declare the array size as a variable instead of [446]?
char decryptedMessage[446];
ifstream in(fileName);
if(in.get(encryptedMessage, 446))
{
[my decrypting code]
}
else
{
cout << "Couldn't successfully read file.\n";
}
system("pause");
Well, a simple one-liner for reading a whole file into a dynamically sized array (don't use a statically sized array) of chars would be:
#include <vector>
#include <iterator>
std::vector<char> encryptedMessage(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
Don't mess with dynamic allocation yourself, just let std::vector do its job. And due to its optimized growth behaviour you don't really need to bother with checking the file size. Optimize for speed when neccessary or at least not before your files get larger than a few hundred characters. And of course the istreambuf_iterator (instead of istream_iterator) doesn't handle whitespace any special, it just takes each character raw from the file one by one.
You may do the same with a std::string instead of a std::vector<char>, but I'm not sure about its growth behaviour (maybe it always reallocates the array with one more element). But then again, who cares for speed when the file contains 400 charcters?
You can use seekg to get the size of an entire file:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
long begin_byte, end_byte;
ifstream in("example.txt");
begin_byte = in.tellg();
in.seekg (0, ios::end);
end_byte = in.tellg();
int total_bytes = end_byte - begin_byte;
in.seekg(0, ios::begin);
char *message = new char[total_bytes + 1];
int index = 0;
while (in) {
message[index++] = in.get();
}
in.close();
cout << "message is: " << message << endl;
delete [] message;
return 0;
}
You can read more about seekg, tellg and files in c++ as a whole here.
However a better solution then using char * is using a std:string and calling push_back on it while in has not ended:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main () {
ifstream in("example.txt");
string message;
while (in) {
message.push_back(in.get());
}
in.close();
cout << "message is: " << message << endl;
return 0;
}
You cannot have Variable Length Arrays(VLA) in C++.
Compilers do provide VLA's as extensions but using them would make your code non-portable.
Simplest and Best Solution is to use std::string instead of character arrays.
You might get answers all over which advice you to use to use dynamically allocated arrays but using std::string is the best choice, so ignore those.
EDIT:
Since somebody downvoted this. I would be very interested in knowing the reasons(provided they are technical) to do so.
You need dynamically allocated memory, and the best way to manage that is with std::vector.
std::vector<char> encryptedMessage;
encryptedMessage.resize(size_of_file);
in.get(&encryptedMessage[0], encryptedMessage.size());