So I have a code which constantly asks for input and then executes your input as a shell command. I understand that the output I am getting from the shell commands is in a buffer of some sort. Now, however, as there are many commands which output lots of lines, I would like to get all of the output into one single string.
extern crate subprocess;
use std::io;
use std::io::{BufRead, BufReader};
use subprocess::Exec;
fn main() {
loop {
let mut mycommand_string: String = String::new();
io::stdin()
.read_line(&mut mycommand_string)
.expect("Failed to read line");
let mycommand: &str = &*mycommand_string;
let x = Exec::shell(mycommand).stream_stdout().unwrap();
let br = BufReader::new(x);
let full: String = " ".to_string();
let string = for (i, line) in br.lines().enumerate() {
let string: String = line.unwrap().to_string();
let full = format!("{}{}", full, string);
println!("{}", full);
};
println!("{}", string);
}
}
This is my code. As you can see, the thing I am aiming for is to somehow iterate over br.lines() and for each line of output it contains, append or concatenate it to a string, so that all the output ends up in one single string, preferably with "\n" in between each line, but not neccesarilly.
Specifically I would like to iterate over the result of the variable br which has a type I dont understand and to concatenate all the strings together.
If you have an iterator of lines, then you can simply collect that into a string:
br.lines().collect();
Of course we should not ignore that there do not seem to be many possible reasons for ever doing that...
Related
Now that I'm typing it, this seems like a very convoluted process that could definitely be solved easier. Ignoring that for the moment, I'm trying to take a string (from user input), separate the characters into an array, then call individual characters to make a new string. The issue I'm running into is that the "join" function doesn't like working with the "Vec" function (not sure if function is the right term, sorry). Here is my code so far:
use std::io;
fn main() {
println!("Enter P1:");
let mut mono = String::new();
io::stdin()
.read_line(&mut mono)
.expect("Failed to read line");
let mono: Vec<char> = mono.chars().collect();
let x = [mono[0],mono[1]].join("");
println!("Square 1: {x}");
}
I'm very new to Rust, so any suggestions are extremely helpful. Thank you!
You could always just slice the original string str[a..b].to_string().
If you need to work with char arrays, there's String::from_iter and collecting into a String. Example:
fn main() {
let string = "My String".to_string();
let substr1 = string[0..3].to_string();
let substr2 = string[3..].to_string();
println!("substr1 = {}", substr1);
println!("substr2 = {}", substr2);
let chars: Vec<_> = string.chars().collect();
let collected_substr1: String = chars.iter().take(2).collect();
let collected_substr2: String = chars.iter().skip(3).collect();
println!("collected_substr1 = {}", collected_substr1);
println!("collected_substr2 = {}", collected_substr2);
let from_iter_substr1 = String::from_iter([chars[0], chars[1]].iter());
let from_iter_substr2 = String::from_iter(chars.iter().skip(3));
println!("from_iter_substr1 = {}", from_iter_substr1);
println!("from_iter_substr2 = {}", from_iter_substr2);
}
Vec is a type, FYI.
join only works on string slices (&str), not chars. Assuming you are just trying to join two characters without a separator, you can do
let x: String = mono.chars().take(2).collect();
If the goal is only to extract a substring from the input it can be done much simpler
use std::io;
fn main() {
println!("Enter P1:");
let mut mono = String::new();
io::stdin()
.read_line(&mut mono)
.expect("Failed to read line");
let x = &mono[..2].to_string(); // get rhe slice you need and create a new string from it
println!("Square 1: {x}");
}
Edit
As pointed out in comments, note that in a real life use case you should check the length of your string before slicing in it with arbitrary indexes... this example could easily crash at runtime.
I'm trying to figure out how to write a string to a file. The following gives me the error format argument must be a string literal:
use std::fs::File;
use std::io::{Error, Write};
fn main() -> Result<(), Error> {
let path = "results.txt";
let mut output = File::create(path)?;
let line = println!("{}", "hello");
write!(output, line);
}
How can I write to a file from a string variable? If I change the follow line it works, but I can't figure out how to write to a file if the string is in a variable.
write!(output, "foo,bar,baz");
write! is a macro, similar to print!. It expects 2 or more arguments, the first being a writer, the second a format string (which must be a string literal) and after that zero or more expressions which will be placed into the placeholders in the format string.
So, you could combine both your last two lines like this:
fn main() -> std::io::Result<()> {
let path = "results.txt";
let mut output = File::create(path)?;
let line = "hello";
write!(output, "{}", line)
}
Other methods to write to a Writer include calling std::io::Write::write_all, or to write a slice into a file directly you can use std::fs::write.
Another thing to note: the print! macro does not return the output in a string, it prints the output to stdout. If you want to get the formatted result in a string, use the format! macro:
let my_string = format!("Hello {}", "world");
I'm trying to figure out how to write a string to a file. The following gives me the error format argument must be a string literal:
use std::fs::File;
use std::io::{Error, Write};
fn main() -> Result<(), Error> {
let path = "results.txt";
let mut output = File::create(path)?;
let line = println!("{}", "hello");
write!(output, line);
}
How can I write to a file from a string variable? If I change the follow line it works, but I can't figure out how to write to a file if the string is in a variable.
write!(output, "foo,bar,baz");
write! is a macro, similar to print!. It expects 2 or more arguments, the first being a writer, the second a format string (which must be a string literal) and after that zero or more expressions which will be placed into the placeholders in the format string.
So, you could combine both your last two lines like this:
fn main() -> std::io::Result<()> {
let path = "results.txt";
let mut output = File::create(path)?;
let line = "hello";
write!(output, "{}", line)
}
Other methods to write to a Writer include calling std::io::Write::write_all, or to write a slice into a file directly you can use std::fs::write.
Another thing to note: the print! macro does not return the output in a string, it prints the output to stdout. If you want to get the formatted result in a string, use the format! macro:
let my_string = format!("Hello {}", "world");
I am attempting to write a lexer for fun, however something keeps bothering me.
let mut chars: Vec<char> = Vec::new();
let mut contents = String::new();
let mut tokens: Vec<&String> = Vec::new();
let mut append = String::new();
//--snip--
for _char in chars {
append += &_char.to_string();
append = append.trim().to_string();
if append.contains("print") {
println!("print found at: \n{}", append);
append = "".to_string();
}
}
Any time I want to do something as simple as append a &str to a String I have to convert it using .to_string, String::from(), .to_owned, etc.
Is there something I am doing wrong, so that I don't have to constantly do this, or is this the primary way of appending?
If you're trying to do something with a type, check the documentation. From the documentation for String:
push: "Appends the given char to the end of this String."
push_str: "Appends a given string slice onto the end of this String."
It's important to understand the differences between String and &str, and why different methods accept and return each of them.
A &str or &mut str are usually preferred in function arguments and return types. That's because they are just pointers to data so nothing needs to be copied or moved when they are passed around.
A String is returned when a function needs to do some new allocation, while &str and &mut str are slices into an existing String. Even though &mut str is mutable, you can't mutate it in a way that increases its length because that would require additional allocation.
The trim function is able to return a &str slice because that doesn't involve mutating the original string - a trimmed string is just a substring, which a slice perfectly describes. But sometimes that isn't possible; for example, a function that pads a string with an extra character would have to return a String because it would be allocating new memory.
You can reduce the number of type conversions in your code by choosing different methods:
for c in chars {
append.push(c); // append += &_char.to_string();
append = append.trim().to_string();
if append.contains("print") {
println!("print found at: \n{}", append);
append.clear(); // append = "".to_string();
}
}
There isn't anything like a trim_in_place method for String, so the way you have done it is probably the only way.
I'm trying to test out my Rust skills with a simple program that reads multiple integers from a single line of input. It compiles correctly, but unfortunately when it receives the input of 1 2 3, it panics, saying that the input wasn't a valid integer. Can someone please explain the reason for this, and also provide an explanation as to how I can fix my program?
use std::io;
fn main() {
let mut string = String::new();
io::stdin().read_line(&mut string);
let int_vec: Vec<u32> = string.split(" ")
.map(|x| x.parse::<u32>().expect("Not an integer!"))
.collect();
for i in (0..int_vec.len()).rev() {
print!("{} ", int_vec[i]);
}
}
In addition to Dogberts answer... it might be helpful to see how you might be able to debug this sort of issue with an iterator yourself in future.
The Iterator trait exposes an inspect function that you can use to inspect each item. Converting your code to use inspect both before and after each map results in:
let int_vec: Vec<u32> = string.split(" ")
.inspect(|x| println!("About to parse: {:?}", x))
.map(|x| {
x.parse::<u32>()
.expect("Not an integer!")
})
.inspect(|x| println!("Parsed {:?} successfully!", x))
.collect();
Outputs:
1 2 3
About to parse: "1"
Parsed 1 successfully!
About to parse: "2"
Parsed 2 successfully!
About to parse: "3\n"
thread '<main>' panicked at 'Not an integer!...
Notice what its attempting to parse when it gets to the number 3.
Of course, you can inspect string all by itself. inspect is handy though for when iterators are involved.
This is because io::stdin().read_line(&mut String) also adds a trailing newline character to the string, which causes the last str after splitting with " " to be "123\n", which is not a valid integer. You can use str::trim() for this:
use std::io;
fn main() {
let mut string = String::new();
io::stdin().read_line(&mut string);
let int_vec: Vec<u32> = string.trim()
.split(" ")
.map(|x| {
x.parse::<u32>()
.expect("Not an integer!")
})
.collect();
for i in (0..int_vec.len()).rev() {
print!("{} ", int_vec[i]);
}
}
With this change, the program works:
$ ./a
1 2 3
3 2 1
Also, you can simplify your for loop:
for i in int_vec.iter().rev() {
print!("{} ", i);
}
You ran into the old problem of the terminating line-ending. Let's try putting
println!("{:?}", string);
in the third line of your code. For the input 1 2 3 it will print (on Windows):
"1 2 3\r\n"
So at some point you are trying to parse "3\r\n" as integer, which obviously fails. One easy way to remove trailing and leading whitespace from a string is to use trim(). This works:
let int_vec: Vec<_> = string.trim().split(" ")
.map(|x| x.parse::<u32>().expect("Not an integer!"))
.collect();