Matching Strings with string literals in Rust - rust

I'm learning Rust and have been stuck on this piece of code that matches a string with some literals for a while.
while done_setup == false {
println!("Enter difficultly mode you wish to play (Easy/Medium/Hard):");
let mut difficulty = String::new();
io::stdin()
.read_line(&mut difficulty)
.expect("Invalid input, aborting");
match difficulty.as_str() {
"Easy" => {num_guesses = 10;},
"Medium" => {num_guesses = 7;},
"Hard" => {num_guesses = 3;},
_ => {
println!("Pls enter a valid difficulty mode!");
continue;
},
}
println!("You are playing in {} mode, you have {} tries!", difficulty, num_guesses);
done_setup = true;
}
Apparently the pattern never matches with "Easy", "Medium" or "Hard" since user input ALWAYS flows to the default case. I've read similar SO questions and I understand that String objects are not the same as literals (str), but shouldn't difficulty.as_str() take care of that?
I'm looking for a clean & "proper" way to code this, any suggestions welcome & thanks in advance!

The result possibly contains a trailing newline.
trim can be used to strip away leading and trailing whitespace.

Related

How to get a substring of a &str based on character index?

I am trying to write a program that takes a list of words and then, if the word has an even length, prints the two middle letters. If the word has an odd length, it prints the single middle letter.
I can find the index of the middle letter(s), but I do not know how to use that index to print the corresponding letters of the word.
fn middle(wds: &[&str)){
for word in wds{
let index = words.chars().count() /2;
match words.chars().count() % 2{
0 => println!("Even word found"),
_ => println!("odd word found")
}
}
}
fn main(){
let wordlist = ["Some","Words","to","test","testing","elephant","absolute"];
middle(&wordlist);
}
You can use slices for this, specifically &str slices. Note the &.
These links might be helpful:
https://riptutorial.com/rust/example/4146/string-slicing
https://doc.rust-lang.org/book/ch04-03-slices.html
fn main() {
let s = "elephant";
let mid = s.len() / 2;
let sliced = &s[mid - 1..mid + 1];
println!("{}", sliced);
}
Hey after posting i found two different ways of doing it, the fact i had two seperate ways in my head was confusing me and stopping me finding the exact answer.
//i fixed printing the middle letter of the odd numbered string with
word.chars().nth(index).unwrap()
//to fix the even index problem i did
&word[index-1..index+1]

Swift string strip all characters but numbers and decimal point?

I have this string:
Some text: $ 12.3 9
I want to get as a result:
12.39
I have found examples on how to keep only numbers, but here I am wanting to keep the decimal point "."
What's a good way to do this in Swift?
This should work (it's a general approach to filtering on a set of characters) :
[EDIT] simplified and adjusted to Swift3
[EDIT] adjusted to Swift4
let text = "$ 123 . 34 .876"
let decimals = Set("0123456789.")
var filtered = String( text.filter{decimals.contains($0)} )
If you need to ignore anything past the second decimal point add this :
filtered = filtered.components(separatedBy:".") // separate on decimal point
.prefix(2) // only keep first two parts
.joined(separator:".") // put parts back together
Easiest and simplest reusable way: you can use this regex replacement option. This replaces all characters except 0 to 9 and dot (.) .
let yourString = "$123. 34"
//pattern says except digits and dot.
let pattern = "[^0-9.]"
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive)
//replace all not required characters with empty string ""
let string_With_Just_Numbers_You_Need = regex.stringByReplacingMatchesInString(yourString, options: NSMatchingOptions.WithTransparentBounds, range: NSMakeRange(0, yourString.characters.count), withTemplate: "")
//your number converted to Double
let convertedToDouble = Double(string_With_Just_Numbers_You_Need)
} catch {
print("Cant convert")
}
One possible solution to the question follows below. If you're working with text fields and currency, however, I suggest you take a look at the thread Leo Dabus linked to.
extension String {
func filterByString(myFilter: String) -> String {
return String(self.characters.filter {
myFilter.containsString(String($0))
})
}
}
var a = "$ 12.3 9"
let myFilter = "0123456789.$"
print(a.filterByString(myFilter)) // $12.39

Is there an equivalent to the string function String(format: ...) using Swift formatting

I'm starting to like the Swift string formatting since it uses variable names in the string rather than ambiguous formatting tags like "%#"
I want to load a large string from a file that has Swift-style formatting in it (like this)
Now is the time for all good \(who) to come to babble incoherently.
Then I want to feed the contents of that String variable into a statement that lest me replace
\(who)
with the contents of the constant/variable who at runtime.
The code below works with a string constant as the formatting string.
let who = "programmers"
let aString = "Now is the time for all good \(who) to come to babble incoherently."
That code does formatting of a quoted string that appears in-line in my code.
Instead I want something like the code
let formatString = "Now is the time for all good %# to come to babble incoherently."
aString = String(format: formatString, who)
But where I can pass in a Swift-style format string in a constant/variable I read from a file.
Is that possible? I didn't have any luck searching for it since I wasn't exactly sure what search terms to use.
I can always use C-style string formatting and the String class' initWithFormat method if I have to...
I don't think there's a way to do this. String interpolation is implemented via conforming to the StringInterpolationConvertible protocol, and presumably you're hoping to tap into that in the same way you can tap into the methods required by StringLiteralConvertible, a la:
let someString = toString(42)
// this is the method String implements to conform to StringLiteralConvertible
let anotherString = String(stringLiteral: someString)
// anotherString will be "42"
print(anotherString)
Unfortunately, you can't do quite the same trick with StringInterpolationConvertible. Seeing how the protocol works may help:
struct MyString: Printable {
let actualString: String
var description: String { return actualString }
}
extension MyString: StringInterpolationConvertible {
// first, this will get called for each "segment"
init<T>(stringInterpolationSegment expr: T) {
println("Processing segment: " + toString(expr))
actualString = toString(expr)
}
// here is a type-specific override for Int, that coverts
// small numbers into words:
init(stringInterpolationSegment expr: Int) {
if (0..<4).contains(expr) {
println("Embigening \(expr)")
let numbers = ["zeo","one","two","three"]
actualString = numbers[expr]
}
else {
println("Processing segment: " + toString(expr))
actualString = toString(expr)
}
}
// finally, this gets called with an array of all of the
// converted segments
init(stringInterpolation strings: MyString...) {
// strings will be a bunch of MyString objects
actualString = "".join(strings.map { $0.actualString })
}
}
let number = 3
let aString: MyString = "Then shalt thou count to \(number), no more, no less."
println(aString)
// prints "Then shalt thou count to three, no more, no less."
So, while you can call String.init(stringInterpolation:) and String.init(stringInterpolationSegment:) directly yourself if you want (just try String(stringInterpolationSegment: 3.141) and String(stringInterpolation: "blah", "blah")), this doesn't really help you much. What you really need is a facade function that coordinates the calls to them. And unless there's a handy pre-existing function in the standard library that does exactly that which I've missed, I think you're out of luck. I suspect it's built into the compiler.
You could maybe write your own to achieve your goal, but a lot of effort since you'd have to break up the string you want to interpolate manually into bits and handle it yourself, calling the segment init in a loop. Also you'll hit problems with calling the combining function, since you can't splat an array into a variadic function call.
I don't think so. The compiler needs to be able to resolve the interpolated variable at compile time.
I'm not a Swift programmer, specifically, but I think you can workaround it to something pretty close to what you want using a Dictionary and standard string-replacing and splitting methods:
var replacement = [String: String]()
replacement["who"] = "programmers"
Having that, you can try to find the occurrences of "\(", reading what is next and prior to a ")", (this post can help with the split part, this one, with the replacing part), finding it in the dictionary, and reconstructing your string from the pieces you get.
this one works like a charm:
let who = "programmers"
let formatString = "Now is the time for all good %# to come to babble incoherently."
let aString = String(format: formatString, who)

Accessing a character in a borrowed string by index

How would you access an element in a borrowed string by index?
Straightforward in Python:
my_string_lst = list(my_string)
print my_string_list[0]
print my_string[0] # same as above
Rust (attempt 1):
let my_string_vec = vec![my_string]; # doesn't work
println!("{}", my_string_vec[0]); # returns entire of `my_string`
Rust (attempt 2):
let my_string_vec = my_string.as_bytes(); # returns a &[u8]
println!("{}", my_string_vec[0]); # prints nothing
My end goal is to stick it into a loop like this:
for pos in 0..my_string_vec.len() {
while shift <= pos && my_string_vec[pos] != my_string_vec[pos-shift] {
shift += shifts[pos-shift];
}
shifts[pos+1] = shift;
}
for ch in my_string_vec {
let pos = 0; // simulate some runtime index
if my_other_string_vec[pos] != ch {
...
}
}
I think it's possible to do use my_string_vec.as_bytes()[pos]and my_string_vec.as_bytes()[pos-shift]in my condition statement, but I feel that this has a bad code smell.
You can use char_at(index) to access a specific character. If you want to iterate over the characters in a string, you can use the chars() method which yields an iterator over the characters in the string.
The reason it was specifically not made possible to use indexing syntax is, IIRC, because indexing syntax would give the impression that it was like accessing a character in your typical C-string-like string, where accessing a character at a given index is a constant time operation (i.e. just accessing a single byte in an array). Strings in Rust, on the other hand, are Unicode and a single character may not necessarily consist of just one byte, making a specific character access a linear time operation, so it was decided to make that performance difference explicit and clear.
As far as I know, there is no method available for swapping characters in a string (see this question). Note that this wouldn't have been possible anyways via an immutably borrowed string, since such a string isn't yours to modify. You would have to most likely use a String, or perhaps a &mut str if you're strictly swapping, but I'm not too familiar with Unicode's intricacies.
I recommend instead you build up a String the way you want it, that way you don't have to worry about the mutability of the borrowed string. You'd refer/look into the borrowed string, and write into the output/build-up string accordingly based on your logic.
So this:
for pos in 0..my_string_vec.len() {
while shift <= pos && my_string_vec[pos] != my_string_vec[pos-shift] {
shift += shifts[pos-shift];
}
shifts[pos+1] = shift;
}
Might become something like this (not tested; not clear what your logic is for):
for ch in my_string.chars()
while shift <= pos && ch != my_string.char_at(pos - shift) {
// assuming shifts is a vec; not clear in question
shift += shifts[pos - shift];
}
shifts.push(shift);
}
Your last for loop:
for ch in my_string_vec {
let pos = 0; // simulate some runtime index
if my_other_string_vec[pos] != ch {
...
}
}
That kind of seems like you want to compare a given character in string A with the corresponding character (in the same position) of string B. For this I would recommend zipping the chars iterator of the first with the second, something like:
for (left, right) in my_string.chars().zip(my_other_string.chars()) {
if left != right {
}
}
Note that zip() stops iterating as soon as either iterator stops, meaning that if the strings are not the same length, then it'll only go as far as the shortest string.
If you need access to the "character index" information, you could add .enumerate() to that, so the above would change to:
for (index, (left, right)) in my_string.chars().zip(my_other_string.chars()).enumerate()

Last Instance of Character

I'm looking to find the last instance of a character in a string. Given the different way Swift deals with strings (ranges), I was hoping someone has run into this before as I can't seem to figure out the best way to deal with it.
The string I'd like to parse is similar to "http://imanimage_thatlooks_likethis_andmypixare_380.jpg". I need to parse the segment between the last "_" and the last ".". So the number 380. Each link is formatted this way, but the substring methodology for Swift is still a bit foreign to me, with the inclusion of different byte lengths.
Thanks in advance!
// regular expression to find substring between last "_" and last "."
let sourceStr = "abc_defg_hijk_lmn.xyz"
let regex = NSRegularExpression( pattern: "_([^_]*)\\.[^\\.]*$", options:nil, error:nil );
if let matchingResult = regex?.firstMatchInString( sourceStr, options: nil, range: NSMakeRange( 0, countElements( sourceStr ) ) ) {
let matchingRange = matchingResult.rangeAtIndex(1)
let matchingString = (sourceStr as NSString).substringWithRange( matchingRange )
}

Resources