I need to change order in every word in sentence. I have string of separators to split my code into a words, and function swap(0,1) to change order of letters in word. However I need to skip first and last letter in every word and I can't use regular expressions for this purposes.
Here some code:
const SEPARATORS: &str = " ,;:!?./%*$=+)#_-('\"&1234567890\r\n";
fn main() {
dbg!(mix("According, research"));
}
fn mix(s: &str) -> String {
let mut a: Vec<char> = s.chars().collect();
for group in a.split_mut(|num| SEPARATORS.contains(*num)) {
group.chunks_exact_mut(2).for_each(|x| x.swap(0, 1));
}
a.iter().collect()
}
Output as follows:
[src/main.rs:4] mix("According, research") = "cAocdrnig, eresrahc"
But I need output as follows:
[src/main.rs:4] mix("According, research") = "Accroidng, rseaerch"
Someone knows how to fix it ?
All you need is to use a slice that doesn't have the first and last character using group[1..len-2]:
const SEPARATORS: &str = " ,;:!?./%*$=+)#_-('\"&1234567890\r\n";
fn main() {
dbg!(mix("According, research"));
}
fn mix(s: &str) -> String {
let mut a: Vec<char> = s.chars().collect();
for group in a.split_mut(|num| SEPARATORS.contains(*num)) {
let len = group.len();
if len > 2 {
group[1..len-2].chunks_exact_mut(2).for_each(|x| x.swap(0, 1));
}
}
a.iter().collect()
}
Related
I have this following rust code:
fn tokenize(line: &str) -> Vec<&str> {
let mut tokens = Vec::new();
let mut chars = line.char_indices();
for (i, c) in chars {
match c {
'"' => {
if let Some(pos) = chars.position(|(_, x)| x == '"') {
tokens.push(&line[i..=i+pos]);
} else {
// Not a complete string
}
}
// Other options...
}
}
tokens
}
I am trying to elegantly extract a string surrounded by double quotes from the line, but since chars.position takes a mutable reference and chars is moved into the for loop, I get a compilation error - "value borrowed after move". The compiler suggests borrowing chars in the for loop but this doesn't work because an immutable reference is not an iterator (and a mutable one would cause the original problem where I can't borrow mutably again for position).
I feel like there should be a simple solution to this.
Is there an idiomatic way to do this or do I need to regress to appending characters one by one?
Because a for loop will take ownership of chars (because it calls .into_iter() on it) you can instead manually iterate through chars using a while loop:
fn tokenize(line: &str) -> Vec<&str> {
let mut tokens = Vec::new();
let mut chars = line.char_indices();
while let Some((i, c)) = chars.next() {
match c {
'"' => {
if let Some(pos) = chars.position(|(_, x)| x == '"') {
tokens.push(&line[i..=i+pos]);
} else {
// Not a complete string
}
}
// Other options...
}
}
}
It works if you just desugar the for-loop:
fn tokenize(line: &str) -> Vec<&str> {
let mut tokens = Vec::new();
let mut chars = line.char_indices();
while let Some((i, c)) = chars.next() {
match c {
'"' => {
if let Some(pos) = chars.position(|(_, x)| x == '"') {
tokens.push(&line[i..=i+pos]);
} else {
// Not a complete string
}
},
_ => {},
}
}
tokens
}
The normal for-loop prevents additional modification of the iterator because this usually leads to surprising and hard-to-read code. Doing it as a while-loop has no such protection.
If all you want to do is find quoted strings, I would not, however, go with an iterator at all here.
fn tokenize(line: &str) -> Vec<&str> {
let mut tokens = Vec::new();
let mut line = line;
while let Some(pos) = line.find('"') {
line = &line[(pos+1)..];
if let Some(end) = line.find('"') {
tokens.push(&line[..end]);
line = &line[(end+1)..];
} else {
// Not a complete string
}
}
tokens
}
I have a task to shuffle words but the first and last letter of every word must be unchanged. When I try to use filter() it doesn't work properly.
const SEPARATORS: &str = " ,;:!?./%*$=+)#_-('\"&1234567890\r\n";
fn main() {
print!("MAIN:{:?}", mix("Evening,morning"));
}
fn mix(s: &str) -> String {
let mut a: Vec<char> = s.chars().collect();
for group in a.split_mut(|num| SEPARATORS.contains(*num)) {
if group.len() > 4 {
let k = group.first().unwrap().clone();
let c = group[group.len() - 1].clone();
group
.chunks_exact_mut(2)
.filter(|x| x != &[k])
.for_each(|x| x.swap(0, 1))
}
}
let s: String = a.iter().collect();
s
}
Is this what you are looking for?
fn mix(s: &str) -> String {
let mut a: Vec<char> = s.chars().collect();
for words in a.split_mut(|num| SEPARATORS.contains(*num)) {
if words.len() > 4 {
let initial_letter = words.first().unwrap().clone();
let last_letter = words[words.len() - 1].clone();
words[0] = last_letter;
words[words.len() - 1] = initial_letter;
}
}
let s: String = a.iter().collect();
s
}
I'm working on a parser for a mini language, and I have the need to differentiate between plain strings ("hello") and strings that are meant to be operators/commands, and start with a specific sigil character (e.g. "$add").
I also want to add a way for the user to escape the sigil, in which a double-sigil gets consolidated into one, and then is treated like a plain string.
As an example:
"hello" becomes Str("hello")
"$add" becomes Operator(Op::Add)
"$$add" becomes Str("$add")
What would be the best way to do this check and manipulation? I was looking for a method that counts how many times a character appears at the start of a string, to no avail.
Can't you just use starts_with?
fn main() {
let line_list= [ "hello", "$add", "$$add" ];
let mut result;
for line in line_list.iter() {
if line.starts_with("$$") {
result = line[1..].to_string();
}
else if line.starts_with("$") {
result = format!("operator:{}", &line[1..]);
}
else {
result = line.to_string();
}
println!("result = {}", result);
}
}
Output
result = hello
result = operator:add
result = $add
According to the comments, your problem seems to be related to the access to the first chars.
The proper and efficient way is to get a char iterator:
#[derive(Debug)]
enum Token {
Str(String),
Operator(String),
}
impl From<&str> for Token {
fn from(s: &str) -> Self {
let mut chars = s.chars();
let first_char = chars.next();
let second_char = chars.next();
match (first_char, second_char) {
(Some('$'), Some('$')) => {
Token::Str(format!("${}", chars.as_str()))
}
(Some('$'), Some(c)) => {
// your real handling here is probably different
Token::Operator(format!("{}{}", c, chars.as_str()))
}
_ => {
Token::Str(s.to_string())
}
}
}
}
fn main() {
println!("{:?}", Token::from("π"));
println!("{:?}", Token::from("hello"));
println!("{:?}", Token::from("$add"));
println!("{:?}", Token::from("$$add"));
}
Result:
Str("π")
Str("hello")
Operator("add")
Str("$add")
playground
I'm trying to remove newline characters from a String (file content read from a file) and convert it to a Vec<u8>.
Example input string:
let ss = String::from("AAAAAAAA\nBBBBBBBBB\nCCCCCC\nDDDDD\n\n");
fn parse(s: String) -> Vec<u8> {
let s = s.chars().skip_while(|c| *c == '\n');
let sett = s.into_iter().map(|c| c as u8).collect();
sett
}
While I get no error, skip_while doesn't seem to remove the newline characters from the string. What am I doing wrong here?
You can basically replace the \n from the string then convert it to Vec<u8> with into_bytes()
fn parse(s: String) -> Vec<u8> {
s.replace("\n", "").into_bytes()
}
If you want to do it with iterators you can do it with filter:
fn parse(s: String) -> Vec<u8> {
s.chars().filter(|c| *c != '\n').map(|c| c as u8).collect()
}
You can call it like following:
use std::str::from_utf8;
fn main() {
let my_string = String::from("AAAAAAAA\nBBBBBBBBB\nCCCCCC\nDDDDD\n\n");
let parsed_string = parse(my_string.clone());
println!("{:?}", from_utf8(&parsed_string));
}
Playground
I have the following:
A Vec<&str>.
A &str that may contain $0, $1, etc. referencing the elements in the vector.
I want to get a version of my &str where all occurences of $i are replaced by the ith element of the vector. So if I have vec!["foo", "bar"] and $0$1, the result would be foobar.
My first naive approach was to iterate over i = 1..N and do a search and replace for every index. However, this is a quite ugly and inefficient solution. Also, it gives undesired outputs if any of the values in the vector contains the $ character.
Is there a better way to do this in Rust?
This solution is inspired (including copied test cases) by Shepmaster's, but simplifies things by using the replace_all method.
use regex::{Regex, Captures};
fn template_replace(template: &str, values: &[&str]) -> String {
let regex = Regex::new(r#"\$(\d+)"#).unwrap();
regex.replace_all(template, |captures: &Captures| {
values
.get(index(captures))
.unwrap_or(&"")
}).to_string()
}
fn index(captures: &Captures) -> usize {
captures.get(1)
.unwrap()
.as_str()
.parse()
.unwrap()
}
fn main() {
assert_eq!("ab", template_replace("$0$1", &["a", "b"]));
assert_eq!("$1b", template_replace("$0$1", &["$1", "b"]));
assert_eq!("moo", template_replace("moo", &[]));
assert_eq!("abc", template_replace("a$0b$0c", &[""]));
assert_eq!("abcde", template_replace("a$0c$1e", &["b", "d"]));
println!("It works!");
}
I would use a regex
use regex::Regex; // 1.1.0
fn example(s: &str, vals: &[&str]) -> String {
let r = Regex::new(r#"\$(\d+)"#).unwrap();
let mut start = 0;
let mut new = String::new();
for caps in r.captures_iter(s) {
let m = caps.get(0).expect("Regex group 0 missing");
let d = caps.get(1).expect("Regex group 1 missing");
let d: usize = d.as_str().parse().expect("Could not parse index");
// Copy non-placeholder
new.push_str(&s[start..m.start()]);
// Copy placeholder
new.push_str(&vals[d]);
start = m.end()
}
// Copy non-placeholder
new.push_str(&s[start..]);
new
}
fn main() {
assert_eq!("ab", example("$0$1", &["a", "b"]));
assert_eq!("$1b", example("$0$1", &["$1", "b"]));
assert_eq!("moo", example("moo", &[]));
assert_eq!("abc", example("a$0b$0c", &[""]));
}
See also:
Split a string keeping the separators