Suppose I have the following three paths:
let file = path::Path::new("/home/meurer/test/a/01/foo.txt");
let src = path::Path::new("/home/meurer/test/a");
let dst = path::Path::new("/home/meurer/test/b");
Now, I want to copy file into dst, but for that I need to correct the paths, so that I can have new_file with a path that resolves to /home/meurer/test/b/01/foo.txt. In other words, how do I remove src from file and then append the result to dst?
/home/meurer/test/a/01/foo.txt -> /home/meurer/test/b/01/foo.txt
Note that we can't assume that src will always be this similar to dst.
You can use Path::strip_prefix and Path::join:
use std::path::Path;
fn main() {
let file = Path::new("/home/meurer/test/a/01/foo.txt");
let src = Path::new("/home/meurer/test/a");
let dst = Path::new("/home/meurer/test/b");
let relative = file.strip_prefix(src).expect("Not a prefix");
let result = dst.join(relative);
assert_eq!(result, Path::new("/home/meurer/test/b/01/foo.txt"));
}
As usual, you probably don't want to use expect in your production code, it's only for terseness of the answer.
Related
I want to extract this part. But I couldn't do it well. So I need you to tell me how to do it.
Example)
https://twitter.com/straw_berry0721/status/1596714080345415681?s=20&t=1nIbnSZ2YN2m5KZaOjO5GA
1596714080345415681
https://twitter.com/xxx/status/1595920708323999744
1595920708323999744
・my code (failed)
final result = _controller.text;
t = s.lastIndexOf('status'));
s.substring(t)
One way to get this is parse it to Uri and use its path like this:
var str =
"https://twitter.com/straw_berry0721/status/1596714080345415681?s=20&t=1nIbnSZ2YN2m5KZaOjO5GA";
Uri uri = Uri.parse(str);
print("id= ${uri.path.substring(uri.path.lastIndexOf('/') + 1)}");//id= 1596714080345415681
or as #MendelG mentions in comment you can go with regex like this:
var reg = RegExp(r'status\/(\d+)');
var result = reg.firstMatch(str)?.group(1);
print("result = $result"); // result = 1596714080345415681
You could simply extract the last shown number from URLs like https://twitter.com/xxx/status/1595920708323999744 by splitting it to a List<String> then take the last element of it, like this:
String extractLastNumber(String url) {
return url.split("/").last;
}
final extractedNumber = extractLastNumber("https://twitter.com/xxx/status/1595920708323999744");
print(extractedNumber); // "1595920708323999744"
print("status: $extractedNumber"); // "status: 1595920708323999744"
Note: this will return the number as String, if you want to get it as an int number you could use the int.tryParse() method like this:
print(int.tryParse(extractedNumber)); // 1595920708323999744
I am new to rust and I am trying to port golang code that I had written previosuly. The go code basically downloaded files from s3 and directly (without writing to disk) ungziped the files and parsed them.
Currently the only solution I found is to save the gzipped files on disk then ungzip and parse them.
Perfect pipeline would be to directly ungzip and parse them.
How can I accomplish this?
const ENV_CRED_KEY_ID: &str = "KEY_ID";
const ENV_CRED_KEY_SECRET: &str = "KEY_SECRET";
const BUCKET_NAME: &str = "bucketname";
const REGION: &str = "us-east-1";
use anyhow::{anyhow, bail, Context, Result}; // (xp) (thiserror in prod)
use aws_sdk_s3::{config, ByteStream, Client, Credentials, Region};
use std::env;
use std::io::{Write};
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> Result<()> {
let client = get_aws_client(REGION)?;
let keys = list_keys(&client, BUCKET_NAME, "CELLDATA/year=2022/month=06/day=06/").await?;
println!("List:\n{}", keys.join("\n"));
let dir = Path::new("input/");
let key: &str = &keys[0];
download_file_bytes(&client, BUCKET_NAME, key, dir).await?;
println!("Downloaded {key} in directory {}", dir.display());
Ok(())
}
async fn download_file_bytes(client: &Client, bucket_name: &str, key: &str, dir: &Path) -> Result<()> {
// VALIDATE
if !dir.is_dir() {
bail!("Path {} is not a directory", dir.display());
}
// create file path and parent dir(s)
let mut file_path = dir.join(key);
let parent_dir = file_path
.parent()
.ok_or_else(|| anyhow!("Invalid parent dir for {:?}", file_path))?;
if !parent_dir.exists() {
create_dir_all(parent_dir)?;
}
file_path.set_extension("json");
// BUILD - aws request
let req = client.get_object().bucket(bucket_name).key(key);
// EXECUTE
let res = req.send().await?;
// STREAM result to file
let mut data: ByteStream = res.body;
let file = File::create(&file_path)?;
let Some(bytes)= data.try_next().await?;
let mut gzD = GzDecoder::new(&bytes);
let mut buf_writer = BufWriter::new( file);
while let Some(bytes) = data.try_next().await? {
buf_writer.write(&bytes)?;
}
buf_writer.flush()?;
Ok(())
}
fn get_aws_client(region: &str) -> Result<Client> {
// get the id/secret from env
let key_id = env::var(ENV_CRED_KEY_ID).context("Missing S3_KEY_ID")?;
let key_secret = env::var(ENV_CRED_KEY_SECRET).context("Missing S3_KEY_SECRET")?;
// build the aws cred
let cred = Credentials::new(key_id, key_secret, None, None, "loaded-from-custom-env");
// build the aws client
let region = Region::new(region.to_string());
let conf_builder = config::Builder::new().region(region).credentials_provider(cred);
let conf = conf_builder.build();
// build aws client
let client = Client::from_conf(conf);
Ok(client)
}
Your snippet doesn't tell where GzDecoder comes from, but I'll assume it's flate2::read::GzDecoder.
flate2::read::GzDecoder is already built in a way that it can wrap anything that implements std::io::Read:
GzDecoder::new expects an argument that implements Read => deflated data in
GzDecoder itself implements Read => inflated data out
Therefore, you can use it just like a BufReader: Wrap your reader and used the wrapped value in place:
use flate2::read::GzDecoder;
use std::fs::File;
use std::io::BufReader;
use std::io::Cursor;
fn main() {
let data = [0, 1, 2, 3];
// Something that implements `std::io::Read`
let c = Cursor::new(data);
// A dummy output
let mut out_file = File::create("/tmp/out").unwrap();
// Using the raw data would look like this:
// std::io::copy(&mut c, &mut out_file).unwrap();
// To inflate on the fly, "pipe" the data through the decoder, i.e. wrap the reader
let mut stream = GzDecoder::new(c);
// Consume the `Read`er somehow
std::io::copy(&mut stream, &mut out_file).unwrap();
}
playground
You don't mention what "and parse them" entails, but the same concept applies: If your parser can read from an impl Read (e.g. it can read from a std::fs::File), then it can also read directly from a GzDecoder.
I'm rewriting this code:
use regex::Regex;
use std::fs;
use std::io;
fn main() {
let contents = fs::read_to_string("/home/jerzy/trackers_best.txt")
.expect("Something went wrong reading the file");
println!("Give me the magnet link.");
let mut magnet = String::new();
io::stdin()
.read_line(&mut magnet)
.expect("Failed to read line");
magnet = magnet.trim().to_string();
let re = Regex::new(r"\&tr=.{3,1000}").expect("Failed to initialize the variable `re`.");
magnet = re.replace(&magnet, contents).to_string();
println!("The updated magnet link:\n{}", magnet);
}
/home/jerzy/trackers_best.txt is a text file that gets automatically downloaded every evening to my home directory from https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt with the help of wget and cron.
I want to use the hyperscan crate instead of the regex crate to improve the performance.
The new code:
use hyperscan::regex::Regex;
use std::fs;
use std::io;
fn main() {
let contents = fs::read_to_string("/home/jerzy/trackers_best.txt")
.expect("Something went wrong reading the file");
println!("Give me the magnet link.");
let mut magnet = String::new();
io::stdin()
.read_line(&mut magnet)
.expect("Failed to read line");
magnet = magnet.trim().to_string();
let re = Regex::new(r"\&tr=.{3,1000}").expect("Failed to initialize the variable `re`.");
magnet = re.replace(&magnet, contents).to_string();
println!("The updated magnet link:\n{}", magnet);
}
The dependencies section of my Cargo.toml:
[dependencies]
hyperscan = "0.2.2"
This code does not compile, I get the following output from the compiler:
error[E0599]: no method named `replace` found for struct `Regex` in the current scope
--> src/main.rs:26:14
|
26 | magnet = re.replace(&magnet, contents).to_string();
| ^^^^^^^ method not found in `Regex`
How might I rewrite the line in question? What method should I use instead of .replace()?
I looked for resources about using hyperscan, but there is not much on their GitHub.
I am writing a code in octave which is the same as well in matlab, with a recursive function. I have a lot of folders and subfolders and inside one of the subfolders, there is a folder which is called img and inside the img folder there are two image files which I read and calculate some data.
Here is the code:
function findin(folder)
foldername = 'img';
filetofind = '*.ppm';
scalefront = 0.05;
scaletop = 0.05;
dirinfo = dir(folder);
dirinfo(~[dirinfo.isdir]) = []; %remove non-directories
tf = ismember( {dirinfo.name}, {'.', '..'});
dirinfo(tf) = []; %remove current and parent directory.
for i=1:length(dirinfo)
if strcmp(dirinfo(i).name, foldername)
files = dir(fullfile(dirinf(i).name, filetofind));
frontimage = strcat(files(1).name);
topimage = strcat(files(2).name);
Angle = getAngle(frontimage, scalefront, topimage, scaletop);
printf('\nAngle for images %s and %s is: &f\n', frontimage, topimage, Angle);
else
findin(dirinfo(i).name);
end
end
printf('done!');
endfunction
The problem is that the if statement never becomes true and in whatever folder I am, it just prints 'done!' according to the number of folders. This means that it doesn't do the recursive call.
Am i doing something wrong with the recursive call??
I have a file that I need to modify. The part I need to modify (not the entire file), is similar to the properties shown below. The problem is that I only need to replace part of the "value", the "ConfigurablePart" if you will. I receive this file so can not control it's format.
alpha.beta.gamma.1 = constantPart1ConfigurablePart1
alpha.beta.gamma.2 = constantPart2ConfigurablePart2
alpha.beta.gamma.3 = constantPart3ConfigurablePart3
I made this work this way, though I know it is really bad!
def updateFile(String pattern, String updatedValue) {
def myFile = new File(".", "inputs/fileInherited.txt")
StringBuffer updatedFileText = new StringBuffer()
def ls = System.getProperty('line.separator')
myFile.eachLine{ line ->
def regex = Pattern.compile(/$pattern/)
def m = (line =~ regex)
if (m.matches()) {
def buf = new StringBuffer(line)
buf.replace(m.start(1), m.end(1), updatedValue)
line = buf.toString()
}
println line
updatedFileText.append(line).append(ls)
}
myFile.write(updatedFileText.toString())
}
The passed in pattern is required to contain a group that is substituted in the StringBuffer. Does anyone know how this should really be done in Groovy?
EDIT -- to define the expected output
The file that contains the example lines needs to be updated such that the "ConfigurablePart" of each line is replaced with the updated text provided. For my ugly solution, I would need to call the method 3 times, once to replace ConfigurablePart1, once for ConfigurablePart2, and finally for ConfigurablePart3. There is likely a better approach to this too!!!
*UPDATED -- Answer that did what I really needed *
In case others ever hit a similar issue, the groovy code improvements I asked about are best reflected in the accepted answer. However, for my problem that did not quite solve my issues. As I needed to substitute only a portion of the matched lines, I needed to use back-references and groups. The only way I could make this work was to define a three-part regEx like:
(.*)(matchThisPart)(.*)
Once that was done, I was able to use:
it.replaceAdd(~/$pattern/, "\$1$replacement\$3")
Thanks to both replies - each helped me out a lot!
It can be made more verbose with the use of closure as args. Here is how this can be done:
//abc.txt
abc.item.1 = someDummyItem1
abc.item.2 = someDummyItem2
abc.item.3 = someDummyItem3
alpha.beta.gamma.1 = constantPart1ConfigurablePart1
alpha.beta.gamma.2 = constantPart2ConfigurablePart2
alpha.beta.gamma.3 = constantPart3ConfigurablePart3
abc.item.4 = someDummyItem4
abc.item.5 = someDummyItem5
abc.item.6 = someDummyItem6
Groovy Code:-
//Replace the pattern in file and write to file sequentially.
def replacePatternInFile(file, Closure replaceText) {
file.write(replaceText(file.text))
}
def file = new File('abc.txt')
def patternToFind = ~/ConfigurablePart/
def patternToReplace = 'NewItem'
//Call the method
replacePatternInFile(file){
it.replaceAll(patternToFind, patternToReplace)
}
println file.getText()
//Prints:
abc.item.1 = someDummyItem1
abc.item.2 = someDummyItem2
abc.item.3 = someDummyItem3
alpha.beta.gamma.1 = constantPart1NewItem1
alpha.beta.gamma.2 = constantPart2NewItem2
alpha.beta.gamma.3 = constantPart3NewItem3
abc.item.4 = someDummyItem4
abc.item.5 = someDummyItem5
abc.item.6 = someDummyItem6
Confirm file abc.txt. I have not used the method updateFile() as done by you, but you can very well parameterize as below:-
def updateFile(file, patternToFind, patternToReplace){
replacePatternInFile(file){
it.replaceAll(patternToFind, patternToReplace)
}
}
For a quick answer I'd just go this route:
patterns = [pattern1 : constantPart1ConfigurablePart1,
pattern2 : constantPart2ConfigurablePart2,
pattern3 : constantPart3ConfigurablePart3]
def myFile = new File(".", "inputs/fileInherited.txt")
StringBuffer updatedFileText = new StringBuffer()
def ls = System.getProperty('line.separator')
myFile.eachLine{ line ->
patterns.each { pattern, replacement ->
line = line.replaceAll(pattern, replacement)
}
println line
updatedFileText.append(line).append(ls)
}
myFile.write(updatedFileText.toString())