Accessing specific lines of text file c++ - c++

I want to access specific lines in of text file to display or modify that one. My text file contains lines of different lengths and i heard cannot use seekg or seekp for such files. I got a code but it doesn't works. Can someone please tell me its fault of some other helpful idea?
#include <fstream>
#include <limits>
#include <string>
#include <iostream>
std::fstream& GotoLine(std::fstream& file, unsigned int num)
{
file.seekg(std::ios::beg);
for(unsigned int i=0; i < num - 1; ++i)
{
file.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
}
return file;
}
int main()
{
using namespace std;
std::fstream& GotoLine(std::fstream& file, unsigned int num);
cout << "Starting..." << endl;
fstream file("bla.txt");
GotoLine(file, 8);
string line8;
file >> line8;
cout << "[" << line8 << "]" << endl;
// cin.get();
cout << "Finished..." << endl;
return 0;
}

string line8;
file >> line8;
will only extract until the first whitespace character is hit.
you could use something like:
string line8;
getline(file, line8);
This at least worked for me with the rest of your code on Windows 7 with VS2012

Standard C++11 (and earlier versions of the standard) dont have notion of lines contained inside files. Also POSIX (and even Windows or MacOSX) don't have it. Textual files usually contain lines of variable length (and only the line terminator is relevant, either \n or \r\n or \n\r, depending upon the operating system and perhaps the file read mode -binary or textual-).
In the 1960s or 1970s IBM mainframe OS/360 operating systems had "file" systems which did have files made of fixed-length lines, mimicking punched cards.
So, you have to read your file line by line and remember where are the line limits (or use std::istream::ignore to skip till \n).
Alternatively, read every line of your file into a std::vector<std::string> using std::getline on std::ifstream-s.
For /etc/fstab (or /proc/mounts) reading all the lines in a vector is a good idea, since it is always a tiny file. It usually have less than an few dozens of lines, often less than about a hundred char each. The pathological case could be a file with many thousand lines of comments, but that don't really happen in practice.

Related

why does removing blank space while writing into file fails reading files?

I'm learning C++, I find problems reading a file if I didn't add blank space while writing into it.
Plus, the file I wrote into doesn't contain blank space as intended.(I opened it with notepad++)
Btw, I'm using code::blocks17.12.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{ ofstream out("file1");
int i;
ifstream in;
if(!out){cerr << "create file error!\n"; return 1;}
for(i = 1; i<=10; ++i) out << i <<' ';
/*if I remove (<<' ') here, nothing was pirnted on screen. */
out.close();
in.open("file1");
if(!in){cerr << "open file error!\n"; return 1;}
while(in >> i) cout<< i << ' ';
in.close();
return 0;
}
If you write 1, 3 and 8 to a file without spaces then you get 138 how do you now want to figure out that it was not 138 that was written?
The input stream needs some kind of indication of how the numbers are separated.
If you want to know why they decided that writing a number does not automatically add a space, that is because it is not always the desired behavior.
And as Martin Heralecký correctly mentions. in >> i does not read in anything because without spaces 12345678910 is written to the file, which is most certainly out of the range of an int only your setup.
The actual size of an int is platform-dependent but you should not expect that it can store numbers larger than 2147483647.
More details about the Fundamental types: Properties

std::vector<string> odd behavior

I have some weird issues I cannot figure out. When I run the code below which takes a file.txt reads it line by line into a vector<string> and then compares each index to string "--" it does not make it to the comparison stage.
Further more, in the convert_file() under the for loop string m, has some weird behavior: string m = "1"; m+= "--"; ('--' inside vector) m+= "2"; will print to console 2--; which makes me think something is bugging out the vector. The 2 is replacing the 1, the first character. This makes it look like the vector is bugged.
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
vector<string> get_file(const char* file){
int SIZE=256, ln=0;
char str[SIZE];
vector<string> strs;
ifstream in(file, ios::in);
if(!in){
return strs;
} else {
while(in.getline(str,SIZE)){
strs.push_back(string(str));
ln++;
}
}
in.close();
return strs;
}
void convert_file(const char* file){
vector<string> s = get_file(file);
vector<string> d;
int a, b;
bool t = false;
string comp = "--";
for(int i=0; i<s.size(); i++){
string m = "1";
m+= string(s.at(i));
m+= "2";
cout << m << endl;
if(s.at(i) == comp){
cout << "s[i] == '--'" << endl;
}
}
}
int main(){
convert_file("test.txt");
return 0;
}
now when I run a test file to check a similar program:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(){
vector<string> s;
s.push_back("--");
s.push_back("a");
for(int i=0; i<s.size(); i++){
cout << "1" << s.at(i) << "2" << endl;
if(s.at(i) == "--"){
cout << i << "= --" << endl;
}
}
return 0;
}
prints off 1--2, 0= --, 1a2. it works, it prints properly, and does the comparison. This leads me to think something is happening when I pull the line into a string.
Windows 7, cygwin64
g++ version 4.9.3
compile: D:\projects\test>g++ -o a -std=c++11 test.cpp
Based on the behavior and the discussion the lines in the file are terminated using a "\r\n" sequence. The easiest approach for dealing with the remaining '\r' is to remove it after reading a line. For example:
for (std::string line; std::getline(file, line); ) {
if (!line.empty() && line.back() == '\r') {
line.resize(line.size() - 1u);
}
strs.push_back(line);
}
If you insist in reading into char arrays you can use file.gcount() to determine the number of characters read to find the end of the string quickly. Note, however, that the number includes the bewline character, i.e., you'd want to check str[file.gcount() - 2] and potentially set it to '\0' (if the count is bigger or equal to 2, of course).
As answered by Dietmar Kühl already, the problem is with the \r\n line endings.
However, you should not need to modify your source code. The default behaviour in C++ is supposed to be to open files in text mode. Text mode means that whenever a line ending is found, where "line ending" depends on the platform you're using, it gets translated so that your program just sees a single \n. You're supposed to explicitly request "binary mode" from your program to disable this line ending translation. This has been long-standing practise on Windows systems, is the behaviour well supported by the C++ standard, and is the expected behaviour with native Windows compilers, but for compatibility with POSIX and existing Unix programs that do not bother setting the file mode properly, Cygwin ignores this and defaults to opening files in binary mode unless a custom Cygwin-specific text mode is explicitly requested.
This is covered in the Cygwin FAQ. The first solutions provided there (using O_TEXT or "t", depending on how you open your file) are non-standard so break your code with other environments, and they are not as easy to use with C++ <fstream> file access.
However, the next solutions provided there do work even for C++ programs:
You can also avoid to change the source code at all by linking an additional object file to your executable. Cygwin provides various object files in the /usr/lib directory which, when linked to an executable, changes the default open modes of any file opened within the executed process itself. The files are
binmode.o - Open all files in binary mode.
textmode.o - Open all files in text mode.
textreadmode.o - Open all files opened for reading in text mode.
automode.o - Open all files opened for reading in text mode,
all files opened for writing in binary mode.
And indeed, changing your compiler and linker invocation from g++ -o a -std=c++11 test.cpp to g++ -o a -std=c++11 test.cpp /usr/lib/textmode.o, your program works without changes to your source code. Linking with textmode.o basically means that your I/O will work the way it already should work by default.

How to write to middle of a file in C++?

I think this should be quite simple, but my googling didn't help so far... I need to write to an existing file in C++, but not necessarily at the end of the file.
I know that when I just want to append text to my file, I can pass the flag ios:app when calling open on my stream object. However, this only let's me write to the very end of the file, but not into its middle.
I made a short program to illustrate the issue:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
string path = "../test.csv";
fstream file;
file.open(path); // ios::in and ios::out by default
const int rows = 100;
for (int i = 0; i < rows; i++) {
file << i << "\n";
}
string line;
while (getline(file, line)) {
cout << "line: " << line << endl; // here I would like to append more text to certain rows
}
file.close();
}
You cannot insert in the middle of the file. You have to copy the old file to a new file and insert whatever you want in the middle during copying to the new file.
Otherwise, if you intend to overwrite data/lines in the existing file, that is possible by using std::ostream::seekp() to identify the position within the file.
You could write to the end and swap lines until it ends up in the right position.
Here's what I had to do.
Here's the test.txt file before:
12345678
12345678
12345678
12345678
12345678
Here's a sample of my program
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
fstream& goToLine(fstream& file, int line){
int charInLine = 10; //number of characters in each line + 2
//this file has 8 characters per line
int pos = (line-1)*charInLine;
file.seekg(pos);
file.seekp(pos);
return file;
}
fstream& swapLines(fstream& file, int firstLine, int secondLine){
string firstStr, secondStr;
goToLine(file,firstLine);
getline(file,firstStr);
goToLine(file,secondLine);
getline(file,secondStr);
goToLine(file,firstLine);
file.write(secondStr.c_str(),8); //Make sure there are 8 chars per line
goToLine(file,secondLine);
file.write(firstStr.c_str(),8);
return file;
}
int main(){
fstream file;
int numLines = 5; //number of lines in the file
//open file once to write to the end
file.open("test.txt",ios::app);
if(file.is_open()){
file<<"someText\n"; //Write your line to the end of the file.
file.close();
}
//open file again without the ios::app flag
file.open("test.txt");
if(file.is_open()){
for(int i=numLines+1;i>3;i--){ //Move someText\n to line 3
swapLines(file,i-1,i);
}
file.close();
}
return 0;
}
Here's the test.txt file after:
12345678
12345678
someText
12345678
12345678
12345678
I hope this helps!
Based on my basic knowledge of Operating systems, I would say it is not possible.
I mean it is not impossible to make an OS that can allow such functionality with current storage technologies, but doing so would always lead to wastage of space in segments.
But I am not aware of any technology that can allow that. Although some cloud-based DataBases do use such kinds of functionally (like inserting content in middle of a file), but they are made specifically for that DBMS software, with very specifically targeted hardware, and they may also have some custom-built kernels to perform such tasks.

Getting input from file troubles C++

I've been trying to read some information in from a .txt file in C++ but it's not all working like I expect. Here is some example code:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char words[255];
int value = 0;
ifstream input_stream("test.txt");
input_stream >> value;
input_stream.getline(words, 256);
cout << value << endl;
cout << words << endl;
}
And test.txt contains:
1234
WordOne WordTwo
What I expect is for the code to print the two lines contained in the text file, but instead I just get:
1234
I've been reading about getline and istream but can't seem to find any solutions so any help would be appreciated.
Thanks
The newline character remains in the input stream after the read of the integer:
// Always check result to ensure variables correctly assigned a value.
if (input_stream >> value)
{
}
Then, the call to getline() reads the newline character and stops, producing an empty string. To correct, consume the newline character before calling getline() (options include using getline() or ignore()).
Note there is a version std::getline() that accepts a std::string as its argument to avoid using a fixed sized array of char, which is used incorrectly in the posted code.
ifstream's getline method gathers input until one of two options is hit. Either a terminating character or the size passed in is reached. In your case, the newline terminator is encountered before the size is reached.
Use another getline to retrieve the second line of text.
Reference
The problem you are seeing is that the first newline after 1234 is not consumed by input_stream>>(int); so the next getline only reads to the end of that file.
This is a very constructed scenario, commonly found in schoolwork. The more common scenario when reading a textfile is to consider the entire file as linebased text.
In this case the more convenient
string line;
while( std::getline( input_stream, line ) ){
}
is appropriate, and way less error prone.
The textfile would commonly have a predefined format. Perhaps name = value lines, and are parsed as such after the line is read from the file.
Here is a somewhat corrected version of your original code:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char words[256]; // was 255
int value = 0;
ifstream input_stream("test.txt");
input_stream >> value;
input_stream.ignore(); // skip '\n'
input_stream.getline(words, 256);
cout << value << endl;
cout << words << endl;
}
Also, I would advise you to use a string instead of a char[] and use the other getline function.

Ho do you do the following file-related operations in C++?

How do you do the following operations in C++?
Opening Files
Closing Files
Reading Files
Writing Files
Reading
#include <fstream>
int main()
{
std::ifstream inputFile("MyFileName") // Opens a File.
int x;
inputFile >> x; // Reads an integer from a file.
std::string word;
inputFile >> word; // Reads a space separated word from a file.
double y;
inputFile >> y; // Reads a floating point number from the file.
// etc..
} // File AutoMagically closed by going out of scope.
Writing
#include <fstream>
int main()
{
std::ofstream inputFile("MyFileName") // Opens a File.
int x = 5;
inputFile << x << " "; // Writes an integer to a file then a space.
inputFile << 5 << " "; // Same Again.
std::string word("This is a line");
inputFile << word << "\n"; // Writes a string to a file followed by a newline
// Notice the difference between reading and
// writing a string.
inputFile << "Write a string constant to a file\n";
double y = 15.4;
inputFile << y << ":"; // Writes a floating point number
// to the file followed by ":".
// etc..
} // File AutoMagically closed by going out of scope.
All at once
{
std::ifstream in("foo.txt"); /* opens for reading */
std::ofstream out("bar.txt"); /* opens for writing */
out << in.rdbuf(); /* streams in into out. writing and reading */
} /* closes automatically */
http://www.cplusplus.com/doc/tutorial/files.html
I personally still use the C style fopen, fread, fwrite, etc, but that is more of preference than actually "correct".
With C++ you've got lots of choices for how to interact with files, especially if you are using one of the many frameworks around, like Qt, wxWidgets, or GLib. To summarize, the standard C++ library uses a streams based model of file access, via std::ifstream and std::ofstream. This is similar to what you see when using std::cout and is what #Martin's post exemplifies. You also have available the standard C library functions for reading and writing files, namely open(), close(), read() and write(). The f*() variants take a FILE pointer rather than a file descriptor. The C variants are more useful when you want to treat a file as a raw stream of bytes, which unfortunately happens more often than it should. While both of these are "portable", constructing paths and handling directories/special files usually isn't, which is why you get things like boost::filesystem.