How do I completely remove a line in Rust? Not just replace it with an empty line.
In Rust, when you delete a line from a file with the following code as an example:
let mut file: File = File::open("file.txt").unwrap();
let mut buf = String::from("");
file.read_to_string(&mut buf).unwrap(); //Read the file to a buffer
let reader = BufReader::new(&file);
for (index, line) in reader.lines().enumerate() { //Loop through all the lines in the file
if line.as_ref().unwrap().contains("some text") { //If the line contains "some text", execute the block
buf = buf.replace(line.as_ref().unwrap(), ""); //Replace "some text" with nothing
}
}
file.write_all(buf.as_bytes()).unwrap(); //Write the buffer back to the file
file.txt:
random text
random text
random text
some text
random text
random text
When you run the code, file.txt turns into this:
random text
random text
random text
random text
random text
Rather than just
random text
random text
random text
random text
random text
Is there any way to completely remove the line rather than just leaving it blank? Like some sort of special character?
This part is bad-news: buf = buf.replace(line.as_ref().unwrap(), "");
This is doing a search through your entire buffer to find the line contents (without '\n') and replace it with "". To make it behave as you expect you need to add back in the newline. You can just about do this by buf.replace(line.as_ref().unwrap() + "\n", "") The problem is that lines() treats more than "\n" as a newline, it also splits on "\r\n". If you know you're always using "\n" or "\r\n" as newlines you can work around this - if not you'll need something tricker than lines().
However, there is a trickier issue. For larger files, this may end up scanning through the string and resizing it many times, giving an O(N^2) style behaviour rather than the expected O(N). Also, the entire file needs to be read into memory, which can be bad for very large files.
The simplest solution to the O(N^2) and memory issues is to do your processing line-by-line, and
then move your new file into place. It would look something like this.
//Scope to ensure that the files are closed
{
let mut file: File = File::open("file.txt").unwrap();
let mut out_file: File = File::open("file.txt.temp").unwrap();
let reader = BufReader::new(&file);
let writer = BufWriter::new(&out_file);
for (index, line) in reader.lines().enumerate() {
let line = line.as_ref().unwrap();
if !line.contains("some text") {
writeln!(writer, "{}", line);
}
}
}
fs::rename("file.txt.temp", "file.txt").unwrap();
This still does not handle cross-platform newlines correctly, for that you'd need a smarter lines iterator.
Hmm could try removing the new line char in the previous line
I'm trying to get a random line from a file:
extern crate rand;
use rand::Rng;
use std::{
fs::File,
io::{prelude::*, BufReader},
};
const FILENAME: &str = "/etc/hosts";
fn find_word() -> String {
let f = File::open(FILENAME).expect(&format!("(;_;) file not found: {}", FILENAME));
let f = BufReader::new(f);
let lines: Vec<_> = f.lines().collect();
let n = rand::thread_rng().gen_range(0, lines.len());
let line = lines
.get(n)
.expect(&format!("(;_;) Couldn't get {}th line", n))
.unwrap_or(String::from(""));
line
}
This code doesn't work:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:18:16
|
18 | let line = lines
| ________________^
19 | | .get(n)
20 | | .expect(&format!("(;_;) Couldn't get {}th line", n))
| |____________________________________________________________^ cannot move out of borrowed content
I tried adding .clone() before .expect(...) and before .unwrap_or(...) but it gave the same error.
Is there a better way to get a random line from a file that doesn't involve collecting the whole file in a Vec?
Use IteratorRandom::choose to randomly sample from an iterator using reservoir sampling. This will scan through the entire file once, creating Strings for each line, but it will not create a giant vector for every line:
use rand::seq::IteratorRandom; // 0.7.3
use std::{
fs::File,
io::{BufRead, BufReader},
};
const FILENAME: &str = "/etc/hosts";
fn find_word() -> String {
let f = File::open(FILENAME)
.unwrap_or_else(|e| panic!("(;_;) file not found: {}: {}", FILENAME, e));
let f = BufReader::new(f);
let lines = f.lines().map(|l| l.expect("Couldn't read line"));
lines
.choose(&mut rand::thread_rng())
.expect("File had no lines")
}
Your original problem is that:
slice::get returns an optional reference into the vector.
You can either clone this or take ownership of the value:
let line = lines[n].cloned()
let line = lines.swap_remove(n)
Both of these panic if n is out-of-bounds, which is reasonable here as you know that you are in bounds.
BufRead::lines returns io::Result<String>, so you have to handle that error case.
Additionally, don't use format! with expect:
expect(&format!("..."))
This will unconditionally allocate memory. When there's no failure, that allocation is wasted. Use unwrap_or_else as shown.
Is there a better way to get a random line from a file that doesn't involve collecting the whole file in a Vec?
You will always need to read the whole file, if only to know the number of lines. However, you don't need to store everything in memory, you can read lines one by one and discard them as you go so that you only keep one in the end. Here is how it goes:
Read and store the first line;
Read the second line, draw a random choice and either:
keep the first line with a probability of 50%,
or discard the first line and store the second line with a probability of 50%,
Keep reading lines from the file and for line number n, draw a random choice and:
keep the currently stored line with a probability of (n-1)/n,
or replace the currently stored line with the current line with a probability of 1/n.
Note that this is more or less what sample_iter does, except that sample_iter is more generic since it can work on any iterator and it can pick samples of any size (eg. it can choose k items randomly).
I am trying to match image files to a specific row in an array.
In a first step I am selecting a folder that contain the tif files.
Then I am selecting a csv file with the information I would like to use.
I am opening each image one after the other. For an opened image I would like to retrieve the values from the line that correspond to the image name in the csv file. The header of the CSV file is FileName, XValue, YValue.
Here's my code so far... Any help would be appreciated.
// Make a pop up to select the input directory
InputDirPath=getDirectory("Select Input directory");
// Get the list of files in the input input directory selected above as an array
InputFileList=getFileList(InputDirPath);
// Defines cell separator and line separator
CellSeparator=",";
LineSeparator="\n";
// open the csv file as a string
FileCCValuesPath=File.openDialog("Select the file containing the coordinates");
FileCCValuesString=File.openAsString(FileCCValuesPath);
// Split each row into an array
FileCCValuesRows=split(FileCCValuesString, LineSeparator);
// Create new arrays for the content of column and for each row
FileNameArray=newArray(FileCCValuesRows.length);
XValueArray=newArray(FileCCValuesRows.length);
YValueArray =newArray(FileCCValuesRows.length);
// Start of the loop going through the list of image files in the input folder selected above
for (Filei = 0; Filei < InputFileList.length; Filei++)
{
InputFileNamei=InputFileList[Filei];
InputFilePathi = InputDirPath+InputFileNamei;
if(endsWith(InputFilePathi, ".tif"))
{
open(InputFilePathi);
//////////This is where I am stuck
//////////Get the XValue and Value from the CSV file for the row in which
//////////FileName=InputFileNamei
run("Translate...", "x=XValue y=YValue interpolation=None");
}//end if
}//end for File i loop
// Notice of end of process
waitForUser("Process is done");
You can add another for loop (after opening the current image) that goes through all lines and checks if the line starts with the current image name and then split the current line to get the x and y values:
for (i=0; i < FileCCValuesRows.length; i++) {
if (startsWith(FileCCValuesRows[i], InputFileNamei)) {
values = split(FileCCValuesRows[i], CellSeparator);
xValue = parseInt(values[1]);
yValue = parseInt(values[2]);
run("Translate...", "x=" + xValue+ " y=" + yValue + " interpolation=None");
}
}
Note that the "ImageJ" way of doing it would be to open your CSV file in a Results table and use the getResultString and getResult macro functions to get the required values. Here's a version of your macro using these:
// Make a pop up to select the input directory
InputDirPath=getDirectory("Select Input directory");
// Get the list of files in the input input directory selected above as an array
InputFileList=getFileList(InputDirPath);
// open the csv file in a Results table
FileCCValuesPath=File.openDialog("Select the file containing the coordinates");
open(FileCCValuesPath);
// Start of the loop going through the list of image files in the input folder selected above
for (Filei = 0; Filei < InputFileList.length; Filei++)
{
InputFileNamei=InputFileList[Filei];
InputFilePathi = InputDirPath+InputFileNamei;
if(endsWith(InputFilePathi, ".tif"))
{
open(InputFilePathi);
for (i=0; i < nResults; i++) {
if (getResultString("FileName", i) == InputFileNamei) {
xValue = getResult("XValue", i);
yValue = getResult("YValue", i);
run("Translate...", "x=" + xValue+ " y=" + yValue + " interpolation=None");
}
}
}//end if
}//end for File i loop
// Notice of end of process
waitForUser("Process is done");
Thank you very much for your replies
This is what I end up doing:
1-Split the array by row using
FileValuesRows=split(FileValuesString, LineSeparator);
2- Create new array for each column (I removed one line because of the header)
Column1Array=newArray(FileValuesRows.length-1);
Column2Array=newArray(FileValuesRows.length-1);
3- Create a for loop that screen each row and break it into each individual column (Starting at 1 because of the header)
for (Rowi=1;Rowi<FileCCValuesRows.length; Rowi++){
FileCCValuesRowi=split(FileCCValuesRows[Rowi], CellSeparator);
//Array.show(FileCCValuesRowi);
4- Add the content content of each row into the previously created array (-1 because of the header)
Column1Array[Rowi-1]=FileCCValuesRowi[0];
Column2Array[Rowi-1]=FileCCValuesRowi[1];
}
//end if for Rowi
5- In the next step the aim is to find the row number corresponding to current open image. This is done in two steps:
5.1- Screen the csv file for the number of occurrence of the string (in this case the filename)
5.2 if occurrence is non null add them to an Indices Array
5.3 Use this indice to get the value corresponding to that row in the array created before
//Returns the indices at which a value occurs within an array
Occurence=0;
// Screen the FileName Array row by row and count the number of occurence
for (Rowi=0; Rowi<lengthOf(FileNameArray); Rowi++) {
if (FileNameArray[Rowi]==InputFileName) {
Occurence++;
//print(Occurence);
} //end of if
} // end of for Rowi
// If found
if (Occurence>0) {
// Create an array of length the number of occurence
IndicesArray=newArray(Occurence);
Occurence=0;
// Screen the FileName Array row by row and add the row of occurence into the Indices Array
for (Rowi=0; Rowi<lengthOf(FileNameArray); Rowi++) {
if (FileNameArray[Rowi]==InputFileName) {
IndicesArray[Occurence]=Rowi;
Occurence++;
}
}
//Array.show(IndicesArray);
}
Final step
//Get the X and Y translation value for the File being processed
In this case I have only 1 occurrence so I will take the first line of the Indice array which is the line 0
XValue=Column1Array[(IndicesArray[0])];
YValue=Column2Array[IndicesArray[0]];
//Translate the picture
run("Translate...", "x=XValue y=YValue interpolation=None");
I have the following string array in matlab built the following way:
labels=textread(nome_tecnicas_base, '%s');
for i=1:size(labels)
temp_vector=cell(1,10);
[temp_vector{1:10}]=deal(labels{i});
final_vector=horzcat(final_vector,temp_vector);
end
I want to save this vector with each string value separated with commas (e.g., csv files) in a text file. I tried in several ways, but when I try to read it with, for example, the textread function i have the following error:
a=textread('labels-cpen-R.txt')
Error using dataread
Trouble reading number from file (row 1, field 1) ==> dct,dct,dct,dct,dct,dct,dct,dct,dct,dct,hierar
this is how my file was saved
dct,dct,dct,dct,dct,dct,dct,dct,dct,dct,hierarch-sift,hierarch-sift,hierarch-sift,hierarch-sift,hierarch-sift,hierarch-sift,hierarch-sift,hierarch sift,hierarch-sift,hierarch
sift,zernike,zernike,zernike,zernike,zernike,zernike,zernike,zernike,zernike,zernike,zernike2,zernike2,zernike2,zernike2,zernike2,zernike2,zernike2,zernike2,zernike2,zernike2,kpca,kpca,kpca,kpca,kpca,kpca,kpca,kpca,kpca,kpca,sift,sift,sift,sift,sift,sift,sift,sift,sift,sift,surf,surf,surf,surf,surf,surf,surf,surf,surf,surf,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bayesianfusion0,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,bks-fusion,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting4,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,fusionvoting6,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,multiscale_voting,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_rf_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_lvt,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,bks_svr_otsu,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_rf_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt,multiscale_bks_svr_lvt
How can I save this vector and how can I read this file properly?
try textscan for reading and fprintf for writing
from the matlab documentation:
fileID = fopen('data.csv');
C = textscan(fileID,'%f %f %f %f %u8 %f',...
'Delimiter',',','EmptyValue',-Inf);
so in your case:
textscan(fileID,'%s', 'Delimiter', ',')
edit: for writing data to a file, you can use fprintf with a file identifier:
fileID= fopen('data.csv', 'w') ;
fprintf(fileID, '%s,', data{1,1:end-1}) ;
fprintf(fileID, '%s\n', data{1,end}) ;
fclose(fileID)
I have 20 text files, and I want to use a matlab loop to get the last line of each file without taking into consideration the other lines. is there any matlab command to solve this problem?
One thing you can try is to open the text file as a binary file, seek to the end of the file, and read single characters (i.e. bytes) backwards from the end of the file. This code will read characters from the end of the file until it hits a newline character (ignoring a newline if it finds it at the very end of the file):
fid = fopen('data.txt','r'); %# Open the file as a binary
lastLine = ''; %# Initialize to empty
offset = 1; %# Offset from the end of file
fseek(fid,-offset,'eof'); %# Seek to the file end, minus the offset
newChar = fread(fid,1,'*char'); %# Read one character
while (~strcmp(newChar,char(10))) || (offset == 1)
lastLine = [newChar lastLine]; %# Add the character to a string
offset = offset+1;
fseek(fid,-offset,'eof'); %# Seek to the file end, minus the offset
newChar = fread(fid,1,'*char'); %# Read one character
end
fclose(fid); %# Close the file
On Unix, simply use:
[status result] = system('tail -n 1 file.txt');
if isstrprop(result(end), 'cntrl'), result(end) = []; end
On Windows, you can get the tail executable from the GnuWin32 or UnxUtils projects.
It may not be very efficient, but for short files it can be sufficient.
function pline = getLastTextLine(filepath)
fid = fopen(filepath);
while 1
line = fgetl(fid);
if ~ischar(line)
break;
end
pline = line;
end
fclose(fid);