Arduino - SD text file remove row - c++

I build script to delete one row from SD from .txt file.
Script works well but if I use longer string TextToRemove (for example length is 9) script ignore the line and println empty line. Additionally following condition is always true.
if (buffer.substring(0, buffer.length() - 1) != TextToRemove)
Basically idea is to
Create a new file *_tmp
Read first line from original file
Compare if line is equal to TestToRemove
If text is not equal println to tmp file, if is equal igonore the line
Repeat steps 2-4.
Delete original file
Rename tmp file (remove _tmp)
boolean RemovePin(String TextToRemove, String FileName) {
String buffer;
String FileName_new = FileName + "_tmp";
File myFile;
File myFile_new;
boolean Removed = false;
char filename2[FileName.length() + 1];
FileName.toCharArray(filename2, sizeof(filename2));
myFile = SD.open(FileName);
myFile_new = SD.open(FileName + "_tmp", FILE_WRITE);
if (myFile_new) {
if (myFile) {
while (myFile.available()) {
buffer = myFile.readStringUntil('\n');
if (buffer.substring(0, buffer.length() - 1) != TextToRemove) {
myFile_new.println(buffer.substring(0, buffer.length() - 1));
} else {
Removed = true;
}
}
myFile.close();
SD.remove(filename2);
} else {
Serial.print("error opening ");
Serial.println(FileName);
}
myFile_new.rename(SD.vwd(), filename2);
myFile_new.close();
} else {
Serial.println("error opening tmp file");
}
return Removed;
}
Thank you very much for your help

Related

Removing a Line from a File without making a new file

I'm trying to remove a single line from a file without creating a new file. For example in the file before it is modified it would be:
This
is
a
file
and after it would be:
This
a
file
However, with the way I'm currently trying to do it what happens is
This
a
file
I know I could do it by writing only the contents that I want into another file and then renaming that file and deleting the old one but I wanted to know if there is another way besides that.
I've tried using
if (string::npos != line.find(SPSID))
{
iPos = (pos - line.size() - 2);
stream.seekg(iPos);
for (int i = (pos - line.size() - 2); i < pos; i++)
{
//Sets input position to the beginning of the current line and replaces it with NULL
stream.put(0);
}
stream.seekp(iPos);
pos = stream.tellp();
}
as well as replacing stream.put(0); with stream.write(nullLine, iPos);
but neither have worked.
int Delete(string fileName, string SPSID)
{
//Variables
string line;
char input[MAX_CHAR];
fstream stream;
streamoff pos = 0;
streamoff iPos = 0;
//Opening and confirming opened
stream.open(fileName);
if (!stream.is_open())
{
cout << "File Did not open.\n" << endl;
return -1;
}
//Loops until the end of the file
do
{
//Gets one line from the file and converts it to c++ string
stream.getline(input, MAX_CHAR, '\n');
line.assign(input);
//Finds the current output position (which is the start of the next line)
pos = stream.tellp();
//Finds and checks if the SPSID is in the string. If it is then print to screen otherwise do nothing
if (string::npos != line.find(SPSID))
{
iPos = (pos - line.size() - 2);
stream.seekg(iPos);
for (int i = (pos - line.size() - 2); i < pos; i++)
{
//Sets input position to the begining of the current line and replaces it with ""
stream.put(0);
}
stream.seekp(iPos);
pos = stream.tellp();
}
} while (stream.eof() == false); //Checks that the end of the file has not been reached
stream << "Test" << endl;
//Resets the input and output positions to the begining of the stream
stream.seekg(0, stream.beg);
stream.seekp(0, stream.beg);
//Closing and Confirming closed
stream.close();
if (stream.is_open())
{
cout << "File did not close.\n" << endl;
return -2;
}
return 0;
}
I'm probably gonna have to make a new file and rename it but figured it was still worth asking if this is possible. :/

Handling last byte in huffman compression/decompression

I have a program that produces a Huffman tree based on ASCII character frequency read in a text input file. The Huffman codes are stored in a string array of 256 elements, empty string if the character is not read. This program also then encodes and compresses an output file and then is able to take the compressed file as an input file and does decompression and decoding.
In summary, my program takes a input file compresses and encodes an output file, closes the output file and opens the encoding as an input file, and takes a new output file that is supposed to have a decoded message identical to the original text input file.
My current problem with this program: When decoding the compressed file I get an extra character or so that is not in the original input file decoded. This is due to the trash bits from what I know. With research I found one solution may be to use a psuedo-EOF character to stop decoding before the trash bits are read but I am not sure how to implement this in my current functions that handle encoding and decoding so all guidance and help is much appreciated.
My end goal is to be able to use this program to also completely decode the encoded file without the trash bits sent to output file.
Below I have two functions, encodedOutput and decodeOutput that handle the compression and decompression.
(For encodedOutput function, fileName is the input file parameter, fileName2 is the output file parameter)
(For decodeOutput function, fileName2 is the input file parameter, fileName 3 is output file parameter)
code[256] is a parameter for both of these functions and holds the Huffman code for each unique character read in the original input file, for example, the character 'H' being read in the input file may have a code of "111" stored in the code array for code[72] at the time it is being passed to the functions.
freq[256] holds the frequency of each ascii character read or holds 0 if it is not in original input file.
void encodeOutput(const string & fileName, const string & fileName2, string code[256]) {
ifstream ifile; //to read file
ifile.open(fileName, ios::binary);
if (!ifile)//to check if file is open or not
{
die("Can't read again"); // function that exits program if can't open
}
ofstream ofile;
ofile.open(fileName2, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
int read;
read = ifile.get(); //read one char from file and store it in int
char buffer = 0, bit_count = 0;
while (read != -1) {//run this loop until reached to end of file(-1)
for (unsigned b = 0; b < code[read].size(); b++) { // loop through bits (code[read] outputs huffman code)
buffer <<= 1;
buffer |= code[read][b] != '0';
bit_count++;
if (bit_count == 8) {
ofile << buffer;
buffer = 0;
bit_count = 0;
}
}
read = ifile.get();
}
if (bit_count != 0)
ofile << char(buffer << (8 - bit_count));
ifile.close();
ofile.close();
}
void decodeOutput(const string & fileName2, const string & fileName3, string code[256], const unsigned long long freq[256]) {
ifstream ifile;
ifile.open(fileName2, ios::binary);
if (!ifile)
{
die("Can't read again");
}
ofstream ofile;
ofile.open(fileName3, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
priority_queue < node > q;
for (unsigned i = 0; i < 256; i++) {
if (freq[i] == 0) {
code[i] = "";
}
}
for (unsigned i = 0; i < 256; i++)
if (freq[i])
q.push(node(unsigned(i), freq[i]));
if (q.size() < 1) {
die("no data");
}
while (q.size() > 1) {
node *child0 = new node(q.top());
q.pop();
node *child1 = new node(q.top());
q.pop();
q.push(node(child0, child1));
} // created the tree
string answer = "";
const node * temp = &q.top(); // root
for (int c; (c = ifile.get()) != EOF;) {
for (unsigned p = 8; p--;) { //reading 8 bits at a time
if ((c >> p & 1) == '0') { // if bit is a 0
temp = temp->child0; // go left
}
else { // if bit is a 1
temp = temp->child1; // go right
}
if (temp->child0 == NULL && temp->child1 == NULL) // leaf node
{
answer += temp->value;
temp = &q.top();
}
}
}
ofile << ans;
}
Change it to freq[257] and code[257], and set freq[256] to one. Your EOF is symbol 256, and it will appear once in the stream, at the end. At the end of your encoding, send symbol 256. When you receive symbol 256 while decoding, stop.

Huffman Decoding Function Uncompressing One Character Repeatedly

I have a program that produces a Huffman tree based on ASCII character frequency read in a text input file. The Huffman codes are stored in a string array of 256 elements, empty string if the character is not read. This program also encodes and compresses an output file.
I am now trying to decompress and decode my current output file which is opened as an input file and a new output file is to have the decoded message identical to the original text input file.
My thought process for this part of the assignment is to recreate a tree with huffman codes and then while reading 8 bits at a time, traverse through tree until I reach a leaf node where I will have updated an empty string(string answer) and then output it to my output file.
My problem: After writing this function I see that only one character in between all of the other characters of my original input file gets output repeatedly. I am confused as to why this is the case because I am expecting the output file to be identical to the original input file.
Any guidance or solution to this problem is appreciated.
(For encodedOutput function, fileName is the input file parameter, fileName2 is the output file parameter)
(For decodeOutput function, fileName2 is the input file parameter, fileName 3 is output file parameter)
code[256] is a parameter for both of these functions and holds the Huffman code for each unique character read in the original input file, for example, the character 'H' being read in the input file may have a code of "111" stored in the code array for code[72] at the time it is being passed to the functions.
freq[256] holds the frequency of each ascii character read or holds 0 if it is not in original input file.
void encodeOutput(const string & fileName, const string & fileName2, string code[256]) {
ifstream ifile;
ifile.open(fileName, ios::binary);
if (!ifile)
{
die("Can't read again");
}
ofstream ofile;
ofile.open(fileName2, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
int read;
read = ifile.get();
char buffer = 0, bit_count = 0;
while (read != -1) {
for (unsigned b = 0; b < code[read].size(); b++) { // loop through bits (code[read] outputs huffman code)
buffer <<= 1;
buffer |= code[read][b] != '0';
bit_count++;
if (bit_count == 8) {
ofile << buffer;
buffer = 0;
bit_count = 0;
}
}
read = ifile.get();
}
if (bit_count != 0)
ofile << (buffer << (8 - bit_count));
ifile.close();
ofile.close();
}
// Work in progress
void decodeOutput(const string & fileName2, const string & fileName3, string code[256], const unsigned long long freq[256]) {
ifstream ifile;
ifile.open(fileName2, ios::binary);
if (!ifile)
{
die("Can't read again");
}
ofstream ofile;
ofile.open(fileName3, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
priority_queue < node > q;
for (unsigned i = 0; i < 256; i++) {
if (freq[i] == 0) {
code[i] = "";
}
}
for (unsigned i = 0; i < 256; i++)
if (freq[i])
q.push(node(unsigned(i), freq[i]));
if (q.size() < 1) {
die("no data");
}
while (q.size() > 1) {
node *child0 = new node(q.top());
q.pop();
node *child1 = new node(q.top());
q.pop();
q.push(node(child0, child1));
} // created the tree
string answer = "";
const node * temp = &q.top(); // root
for (int c; (c = ifile.get()) != EOF;) {
for (unsigned p = 8; p--;) { //reading 8 bits at a time
if ((c >> p & 1) == '0') { // if bit is a 0
temp = temp->child0; // go left
}
else { // if bit is a 1
temp = temp->child1; // go right
}
if (temp->child0 == NULL && temp->child1 == NULL) // leaf node
{
ans += temp->value;
temp = &q.top();
}
ofile << ans;
}
}
}
(c >> p & 1) == '0'
Will only return true when (c >> p & 1) equals 48, so your if statement will always follow the else branch. The correct code is:
(c >> p & 1) == 0

Alternating between reading and writing repeatedly

My objective is to read a file line by line, check if that line contains some number, and if so rewrite that line. Then continue reading the file.
I've successfully been able to do this for one line, but I can't figure out how to continue reading the rest of the file.
Here's how I replace one line (every line is a known fixed size):
while(getline(fs, line)){
if(condition){
pos = fs.tellg(); //gets current read position (end of the line I want to change)
pos -= line.length()+1; //position of the beginning of the line
fs.clear(); //switch to write mode
fs.seekp(pos); //seek to beginning of line
fs << new_data; //overwrite old data with new data (also fixed size)
fs.close(); //Done.
continue;
}
}
How do I switch back to read and continue the getline loop?
I had the same problem, TB-scale files and I wanted to modify some header information in the beginning of the file.
Obviously one has to leave enough room when one initially creates the file for any new content, because there is no way to increase the file size (besides appending to it) and the new line has to have the exact same line length as the original one.
Here is a simplification of my code:
#include <iostream>
#include <fstream>
using namespace std;
bool CreateDummy()
{
ofstream out;
out.open("Dummy.txt");
// skip: test if open
out<<"Some Header"<<endl;
out<<"REPLACE1 12345678901234567890"<<endl;
out<<"REPLACE2 12345678901234567890"<<endl;
out<<"Now ~1 TB of data follows..."<<endl;
out.close();
return true;
}
int main()
{
CreateDummy(); // skip: test if successful
fstream inout;
inout.open("Dummy.txt", ios::in | ios::out);
// skip test if open
bool FoundFirst = false;
string FirstText = "REPLACE1";
string FirstReplacement = "Replaced first!!!";
bool FoundSecond = false;
string SecondText = "REPLACE2";
string SecondReplacement = "Replaced second!!!";
string Line;
size_t LastPos = inout.tellg();
while (getline(inout, Line)) {
if (FoundFirst == false && Line.compare(0, FirstText.size(), FirstText) == 0) {
// skip: check if Line.size() >= FirstReplacement.size()
while (FirstReplacement.size() < Line.size()) FirstReplacement += " ";
FirstReplacement += '\n';
inout.seekp(LastPos);
inout.write(FirstReplacement.c_str(), FirstReplacement.size());
FoundFirst = true;
} else if (FoundSecond == false && Line.compare(0, SecondText.size(), SecondText) == 0) {
// skip: check if Line.size() >= SecondReplacement.size()
while (SecondReplacement.size() < Line.size()) SecondReplacement += " ";
SecondReplacement += '\n';
inout.seekp(LastPos);
inout.write(SecondReplacement.c_str(), SecondReplacement.size());
FoundSecond = true;
}
if (FoundFirst == true && FoundSecond == true) break;
LastPos = inout.tellg();
}
inout.close();
return 0;
}
The input is
Some Header
REPLACE1 12345678901234567890
REPLACE2 12345678901234567890
Now ~1 TB of data follows...
The output is:
Some Header
Replaced first!!!
Replaced second!!!
Now ~1 TB of data follows...

C++/CLI - URL Download to File

I'm not entirely familiar with how CLI works, but I have a general idea. I have a function that takes 2 System::String variables, and uses those to download a file from a webpage. As far as the download goes, it works fine, and the file shows up in my directory with the necessary content. However, it gives me the error
An unhandled exception of type 'System.AccessViolationException'
occurred in ParseLinks.exe
void downloadFile(System::String ^_URL, System::String ^_saveAs)
{
try
{
System::Net::WebClient ^webClient = gcnew System::Net::WebClient();
// Downloads the resource with the specified URI to a local file.
webClient->DownloadFile(_URL, _saveAs);
webClient->Dispose();
}
catch (System::Exception ^_e)
{
// Error
System::Console::WriteLine("Exception caught in process: {0}", _e);
}
}
I did some digging and output testing, and found out that the exe is hitting a break point somewhere in the text file, as the entire webpage did not save to the txt file.
Relevant code for that:
if (myFile.is_open()) //if file open
{
while (!myFile.eof()) //before end of file
{
getline(myFile, ln);
lines[count] = ln;
count++; //count total lines to set loop length for later parsing
//Error occurs somewhere in here
}
myFile.close();
}
else
cout<<"Error: Could not access file\n";
Brand New Error! :(
An unhandled exception of type 'System.Runtime.InteropServices.SEHException' occurred in ParseLinks.exe
The code after the file -> line array loop
myFile.close(); //Close txt file
//Loop through lines
for (int i = 0; i < count; i++)
{
string temp = parseLinks(lines[i]); //parse links from each line
The function for that:
string parseLinks(string str)
{
const int len = str.length();
string link;
bool quotes = false, islink = false;
string compare[5] = {".htm",".html",".php",".asp",".pdf"};
//Parse all quoted text
for (int i = 0; i != len; i++)
{
//Change bool if quote found
if (str[i] == '"')
{
if (quotes == false)
quotes = true;
else
quotes = false;
}
//If bool true, and char is not a quote, add to link string
if (quotes == true && str[i] != '"')
link += str[i];
}
//Discard non-link text
for (int i = 0; i < 5; i++)
{
//Link check for links given array of path filetypes
if (link.compare((link.length() - compare[i].length()),compare[i].length(),compare[i]) == 0)
islink = true;
}
//Link check for links with no path filetype (.html, .php, etc.)
if (link.compare(0,7,"http://") == 0)
islink = true;
//If not a link, return empty string
if (islink == false)
link = "";
return link;
}
The error points to my large compare statement in this function. (Also, I'm clearly terrible at compressing my code)
You're using getline wrong, and possibly that's causing your error. The correct idiom is this:
std::string line;
while (std::getline(myFile, line))
{
// process `line`
}
There's no need to check myFile for openness separately.