I'd like to eliminate the extra '\n' at the end of a txt file. Which function can be used to do this job in c / c++.
Thanks advanced
One approach would be to iterate of the file line-by-line using getline, saving off that data for later. After each line is read, write the previous line (with \n). When no more data is available write the final line without the \n anymore.
Alternately seek to the end to get the size, read the data in blocks of some chunk size, rewriting them until the second-to-last character. If the last character is a \n don't write it, otherwise do write it.
The posix way to do this is the truncate function.
If you get the length of the file using seek or another OS call you can use truncate to
trim the last char of the length of the file without rewriting the whole thing.
//This RESIZES the file without the need to REWRITE the whole thing:
struct stat fileStat;
stat("Filename", &fileStat);
status = truncate("Filename",fileStat.st_size-1);
The windows equivalent function is chsize however it requires opening a file first:
// Open a file
if( _sopen_s( &fh, "data", _O_RDWR | _O_CREAT, _SH_DENYNO,
_S_IREAD | _S_IWRITE ) == 0 )
{
if( ( result = _chsize( fh, _filelength( fh ) -1 ) ) == 0 )
printf( "Size successfully changed\n" );
else
printf( "Problem in changing the size\n" );
_close( fh );
}
Related
I need to be able to detect extraneous input and exit my program.
If I input a string of more than 1 char into scanf("%c", &var); it takes the first letter and stores it into var but the programs continues.
I tried to use if (scanf("%c", &var) != 1) but it returns 1 every time no matter the input so its no difference.
I know other functions like fgets() may be better suited for this but I have been instructed to use the scanf() function.
How should I do this?
When reading user input scanf can take its input from a file, or the console.
When reading from the console, the data becomes available to the program only when a line break is added. This means anything that was typed on the same line, is read.
Imagine I have a maze program, and it wants me to choose which way to go....
while( !atGoalLocation() ) {
printf( "Which direction (f)orward (l)eft (r)ight?\n" );
scanf( "%c", &dir );
processDirection( dir );
}
I could either enter the route through the maze as
f
l
f
r
f
f
Or it may also be correct to enter my input as
flfrff
Depending on your task, they may mean the same thing.
If you want to allow either of these inputs, then make sure you eat the white space by adding " " to the scanf.
if( scanf( " %c", &dir ) == 1 )
If the line break version is the only method you want to accept, then you should separate the lines and then try the scan.
char line[200];
while( fgets( line, sizeof( line ), stdin ) != NULL ){
sscanf( line, "%c", &dir );
Also for C++ we should be using the std::cin and std::cout. However the same complications occur for unread characters on the same line, so I would still use a line based parser.
std::string line;
while( std::getline( std::cin, line ) ){
// here we could parse the line using std::strstream to decode more complext things than a char.
dir = line[0];
You need to add a space before the %c:
scanf(" %c",&var);
And if you just dont want your program to wait for an enter after typing the character, use getch() defined in "conio.h"
c = getch();
OR
In scanf("%c",&var); you could add a newline character \n after %c in order to absorb the extra characters.
scanf("%c\n",&in);
I'm trying to make a code which would change one given word from a file, and change it into another one. The program works in a way that it copies word by word, if it's normal word it just writes it into the output file, and if it's the one i need to change it writes the one i need to change to. However, I've enountered a problem. Program is not putting whitespaces where they are in the input file. I don't know the solution to this problem, and I have no idea if I can use noskipws since I wouldn't know where the file ends.
Please keep in mind I'm a complete newbie and I have no idea how things work. I don't know if the tags are visible enough, so I will mention again that I use C++
Since each reading of word is ended with either a whitespace or end of file, you could simply check whether the thing which stop your reading is end of file, or otherwise a whitespace:
if ( reached the end of file ) {
// What I have encountered is end of file
// My job is done
} else {
// What I have encountered is a whitespace
// I need to output a whitespace and back to work
}
And the problem here is how to check the eof(end of file).
Since you are using ifstream, things will be quite simple.
When a ifstream reach the end of file (all the meaningful data have been read), the ifstream::eof() function will return true.
Let's assume the ifstream instance that you have is called input.
if ( input.eof() == true ) {
// What I have encountered is end of file
// My job is done
} else {
// What I have encountered is a whitespace
// I need to output a whitespace and back to work
}
PS : ifstream::good() will return false when it reaches the eof or an error occurs. Checking whether input.good() == false instead can be a better choice here.
First I would advise you not to read and write in the same file (at least not during reading) because it will make your program much more difficult to write/read.
Second if you want to read all whitespaces easiest is to read whole line with getline().
Program that you can use for modifying words from one file to another could look something like following:
void read_file()
{
ifstream file_read;
ofstream file_write;
// File from which you read some text.
file_read.open ("read.txt");
// File in which you will save modified text.
file_write.open ("write.txt");
string line;
// Word that you look for to modify.
string word_to_modify = "something";
string word_new = "something_new";
// You need to look in every line from input file.
// getLine() goes from beginning of the file to the end.
while ( getline (file_read,line) ) {
unsigned index = line.find(word_to_modify);
// If there are one or more occurrence of target word.
while (index < line.length()) {
line.replace(index, word_to_modify.length(), word_new);
index = line.find(word_to_modify, index + word_new.length());
}
cout << line << '\n';
file_write << line + '\n';
}
file_read.close();
file_write.close();
}
I have little issue with using strtok() function.
I am parsing two files. Firts I load file 1 into buffer. This file constains name of the second file I need to load. Both files are read line after line. My code looks like this:
char second_file_name[128] = { "" };
char * line = strtok( buffer, "\n" );
while( line != NULL )
{
if ( line[0] = 'f' )
{
sscanf( line, "%*s %s", &second_file_name );
LoadSecondFile( second_file_name );
}
// processing other lines, not relevant for question
line = strtok( NULL, "\n" );
}
While the LoadSecondFile(...) function works in pretty same way, thus:
char * line = strtok( buffer, "\n" );
while( line != NULL )
{
// process file data
line = strtok( NULL, "\n" );
}
What my problem is, after calling the LoadSecondFile(...) function, the strtok() pointer used for parsing the first file gets "messed up". Instead of giving me line that follows the name of the second file, it gives me nothing - understand as "complete nonsense". Do I get it right that this is caused by strtok() pointer being shared in program, not only in function? If so, how can I "back up" the pointer of strtok() used for parsing first file before using it for parsing second file?
Thanks for any advice.
Cheers.
strtok is an evil little function which maintains global state, so (as you've found) you can't tokenise two strings at the same time. On some platforms, there are less evil variants with names like strtok_r or strtok_s; but since you're writing C++ not C, why not use the C++ library?
ifstream first_file(first_file_name); // no need to read into a buffer
string line;
while (getline(first_file, line)) {
if (!line.empty() && line[0] == 'f') { // NOT =
istringstream line_stream(line);
string second_file_name;
line_stream.ignore(' '); // skip first word ("%*s")
line_stream >> second_file_name; // read second word ("%s")
LoadSecondFile(second_file_name);
}
}
You can use strtok_r which allows you to have different state pointers.
Which is why it is constantly recommended to not use strtok
(not to mention the problems with threads). There are many
better solutions, using the functions in the C++ standard
library. None of which modify the text they're working on, and
none of which use hidden, static state.
I have a binary file packing lots of files (something like a .tar), where I can found both binary and text files.
When processing in memory strings, carriage lines are usually '\n', but if I read the text part from this packed file, I get "\r\n". Therefore processing this text gives me errors.
Here is the code for reading the text from a binary file:
FILE* _fileDescriptor; // it's always open to improve performance
fopen_s(&_fileDescriptor, _filePath.string().c_str(), "rb");
char* data = new char[size + 1]; // size is a known and correct value
fseek(_fileDescriptor, begin, SEEK_SET); // begin is another known value, where the file starts inside the packed one
fread(data, sizeof(char), size, _fileDescriptor);
data[it->second.size] = '\0';
This gives me the right text into data, but the following code gives me error when reading an empty line:
istringstream ss(data); // create a stringstream to process it in another function
delete[] data; // free the data buffer
// start processing the file
string line;
getline(infile, line); // read an empty line
if(line.size() > 0) {
/*
enters here, because the "empty" line was "\r\n", and now the value of line is '\r', therefore line.size() == 1
*/
...
So, any advice to avoid the '\r'?
I edited it on Notepad++. Changing its configuration to use '\n' instead of '\r\n' as line carriage works, but I don't want to depend on this because other people can miss that, and it would be very hard to spot the problem if that happens.
Probably easiest to trim the '\r' characters out of your string and then discard blank lines. See this answer for approaches to trimming a std::string (I'm assuming that's what 'line' is):
What's the best way to trim std::string?
How do I edit a specific line of a text file in C++? Let's say I want to open a file and change the focus or pointer or whatever its called to line 17 column 20. That way I can edit text after line 17, column 20.
I tried this, but it didnt work.
ofstream txtFile("textFile.txt");
fseek(txtFile, 17, 20);
txtFile << "New stuff to enter at this point (overwrites old not insert)";
How do I do this?
fseek is not seeking counting lines, but rather bytes. What you instruct the program is not to position the pointer at column 20 of 17th line, but rather at the 17 + 20 = 37th byte of the file.
The first parameter of the function is the origin, i.e. the count of bytes from the origin from which you count, and the second - how many more you offset.
See the reference of fseek.
I am not aware of any library that can do byte positioning in respect of lines and columns in C++. You will probably need to use a higher level function and parse lines one by one (e.g. using getline if you are after C++ solution).
Use fputs, like it's done here. In order to recognize new lines, you have to loop through the content and count the newline characters (i.e.'\n' on linux). You can get the character currently pointed to with getc.
One way to implement this is to write a function that returns the byte-position of where a given row starts, like so:
#include <stdio.h>
int getRowPos(int row,FILE* fp){
int pos=1,lines=1,currChr;
do{
currChr=getc(fp);
if(currChr=='\n')
lines++;
}while(lines<row && currChr!=EOF && ++pos);
return pos;
}
Using fseek one could then go to the position returned by getRowPos plus the column number (i.e. byte number, on the line), and then write the desired content using fputs, like so:
int row=wanted row
int col=wanted column
FILE * pFile;
//Open file for read and write
pFile = fopen ( "myfile.txt" , "rb+" );
int rowPos=getRowPos(row,pFile);
fseek ( pFile , rowPos+colPos , SEEK_SET );
fputs ( "my new content" , pFile );
fclose ( pFile );
If you know how wide your lines are, say n wide, you can add an lseek skipping n bytes ahead before the call to getc.