Writing a function that replaces a string using OpenText, ReadLine and WriteLine - f#

I have to write a function, given a filename, needle and a replace, that swaps the two strings in a given text document. The function has to use the System.IO.File.OpenText, WriteLine and ReadLine syntax. I'm currently stuck here, where the function seems to override given text document instead of replacing the needle.
open System
let fileReplace (filename : string) (needle : string) (replace : string) : unit =
try // uses try-with to catch fail-cases
let lines = seq {
use file = IO.File.OpenText filename // uses OpenText
while not file.EndOfStream // runs through the file
do yield file.ReadLine().Replace(needle, replace)
file.Close()
}
use writer = IO.File.CreateText filename // creates the file
for line in lines
do writer.Write line
with
_ -> failwith "Something went wrong opening this file" // uses failwith exception
let filename = #"C:\Users\....\abc.txt"
let needle = "string" // given string already appearing in the text
let replace = "string" // Whatever string that needs to be replaced
fileReplace filename needle replace

The problem with your code is that you are using lazy sequence when reading lines. When you use seq { .. }, the body is not actually evaluated until it is needed. In your example, this is when iterating over lines in a for loop - but before the code gets there, you call CreateText and overwrite the file!
You can fix this by using a list, which is evaluated immediately. You also need to replace Write with WriteLine, but the rest works!
let fileReplace (filename : string) (needle : string) (replace : string) : unit =
try // uses try-with to catch fail-cases
let lines =
[ use file = IO.File.OpenText filename // uses OpenText
while not file.EndOfStream do // runs through the file
yield file.ReadLine().Replace(needle, replace)
]
use writer = IO.File.CreateText filename // creates the file
for line in lines do
writer.WriteLine line
with
_ -> failwith "Something went wrong opening this file" // uses failwith exception
I also removed the Close call, because use takes care of that for you.
EDIT: I put back the required do keywords - I was confused by your formatting. Most people would write them at the end of the previous line as in my updated version.

Related

Non-optional expression of type 'String' used in a check for optionals

I am getting this warning from Xcode Swift 5, here is my code I don't get what is wrong, I use this to remove any new line or tab at the end of my String (line)
My code:
let url: String = String(line.filter { !" \n\t\r".contains($0) })
UPDATE
I was doing it inside an if let and was using the type cast operator here is the solution and the rest of code and an example of the line value.
let line = " http://test.com/testing.php \n"
if let url: String = line.filter({!" \n\t\r".contains($0)}) as String?
{
//More action here
}
Thank you
to me this line looks good, but you may be missing the parentheses for the string filter method. Here's two ways I did it in playground. Let me know if this works for you, or how I can help further.
var line = "\t Hello, line removal \n \t Another new line \n"
let filteredClosure = line.filter { (char) -> Bool in
return !"\n\t\r".contains(char)
}
let filterShorthand = line.filter({!"\n\t\r".contains($0)})
With the line you provided, I would expect white-space to be removed too. If that's what you're looking for, add a space inside the filter string: " \n\t\r"

Extracting a file extension from a given path in Rust idiomatically

I am trying to extract the extension of a file from a given String path.
The following piece of code works, but I was wondering if there is a cleaner and more idiomatic Rust way to achieve this:
use std::path::Path;
fn main() {
fn get_extension_from_filename(filename: String) -> String {
//Change it to a canonical file path.
let path = Path::new(&filename).canonicalize().expect(
"Expecting an existing filename",
);
let filepath = path.to_str();
let name = filepath.unwrap().split('/');
let names: Vec<&str> = name.collect();
let extension = names.last().expect("File extension can not be read.");
let extens: Vec<&str> = extension.split(".").collect();
extens[1..(extens.len())].join(".").to_string()
}
assert_eq!(get_extension_from_filename("abc.tar.gz".to_string()) ,"tar.gz" );
assert_eq!(get_extension_from_filename("abc..gz".to_string()) ,".gz" );
assert_eq!(get_extension_from_filename("abc.gz".to_string()) , "gz");
}
In idiomatic Rust the return type of a function that can fail should be an Option or a Result. In general, functions should also accept slices instead of Strings and only create a new String where necessary. This reduces excessive copying and heap allocations.
You can use the provided extension() method and then convert the resulting OsStr to a &str:
use std::path::Path;
use std::ffi::OsStr;
fn get_extension_from_filename(filename: &str) -> Option<&str> {
Path::new(filename)
.extension()
.and_then(OsStr::to_str)
}
assert_eq!(get_extension_from_filename("abc.gz"), Some("gz"));
Using and_then is convenient here because it means you don't have to unwrap the Option<&OsStr> returned by extension() and deal with the possibility of it being None before calling to_str. I also could have used a lambda |s| s.to_str() instead of OsStr::to_str - it might be a matter of preference or opinion as to which is more idiomatic.
Notice that both the argument &str and the return value are references to the original string slice created for the assertion. The returned slice cannot outlive the original slice that it is referencing, so you may need to create an owned String from this result if you need it to last longer.
What's more idiomatic than using Rust's builtin method for it?
Path::new(&filename).extension()

Formatting strings in Swift

In some languages, like C# for example, you can create a string in the following way:
"String {0} formatted {1} "
And then format it with String.format by passing in the values to format.
The above declaration is good, because you don't have to know of what type its parameters are when you create the string.
I tried to find similar approach in Swift, but what I found out was something like the following format:
"String %d formatted %d"
which requires you to format the string with String(format: , parameters). This is not good because you would also have to know parameter types when declaring the string.
Is there a similar approach in Swift where I wouldn't have to know the parameter types?
Use this one:
let printfOutput = String(format:"%# %2.2d", "string", 2)
It's the same as printf or the Obj-C formatting.
You can also mix it in this way:
let parm = "string"
let printfOutput = String(format:"\(parm) %2.2d", 2)
Edit: Thanks to MartinR (he knows it all ;-)
Be careful when mixing string interpolation and formatting. String(format:"\(parm) %2.2d", 2) will crash if parm contains a percent character. In (Objective-)C, the clang compiler will warn you if a format string is not a string literal.
This gives some room for hacking:
let format = "%#"
let data = "String"
let s = String(format: "\(format)", data) // prints "String"
In contrast to Obj-C which parses the format string at compile time, Swift does not do that and just interprets it at runtime.
In Swift, types need to conform to the CustomStringConvertible protocol in order to be used inside strings. This is also a requirement for the types used in string interpolation like this:
"Integer value \(intVal) and double value \(doubleVal)"
When you understand the CustomStringConvertible, you can create your own function to fulfill your needs. The following function formats the string based on the given arguments and prints it. It uses {} as a placeholder for the argument, but you can change it to anything you want.
func printWithArgs(string: String, argumentPlaceHolder: String = "{}", args: CustomStringConvertible...) {
var formattedString = string
// Get the index of the first argument placeholder
var nextPlaceholderIndex = string.range(of: argumentPlaceHolder)
// Index of the next argument to use
var nextArgIndex = 0
// Keep replacing the next placeholder as long as there's more placeholders and more unused arguments
while nextPlaceholderIndex != nil && nextArgIndex < args.count {
// Replace the argument placeholder with the argument
formattedString = formattedString.replacingOccurrences(of: argumentPlaceHolder, with: args[nextArgIndex].description, options: .caseInsensitive, range: nextPlaceholderIndex)
// Get the next argument placeholder index
nextPlaceholderIndex = formattedString.range(of: argumentPlaceHolder)
nextArgIndex += 1
}
print(formattedString)
}
printWithArgs(string: "First arg: {}, second arg: {}, third arg: {}", args: "foo", 4.12, 100)
// Prints: First arg: foo, second arg: 4.12, third arg: 100
Using a custom implementation allows you to have more control over it and tweak its behavior. For example, if you wanted to, you could modify this code to display the same argument multiple times using placeholders like {1} and {2}, you could fill the arguments in a reversed order, etc.
For more information about string interpolation in Swift: https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292

How to pass F# a string and get the result back in c# [duplicate]

This question already has answers here:
Call F# code from C#
(4 answers)
Closed 8 years ago.
I am SQL developer and am really new to both F# and C#. I need help on how to pass a string to f# function below and to return the result from F# to C#.
Description of project:
I am using stanford postagger to tag a sentence with the parts of speech.
Reference link from where i copied this code.
(http://sergey-tihon.github.io/Stanford.NLP.NET/StanfordPOSTagger.html)
module File1
open java.io
open java.util
open edu.stanford.nlp.ling
open edu.stanford.nlp.tagger.maxent
// Path to the folder with models
let modelsDirectry =
__SOURCE_DIRECTORY__ + #'..\stanford-postagger-2013-06-20\models\'
// Loading POS Tagger
let tagger = MaxentTagger(modelsDirectry + 'wsj-0-18-bidirectional-nodistsim.tagger')
let tagTexrFromReader (reader:Reader) =
let sentances = MaxentTagger.tokenizeText(reader).toArray()
sentances |> Seq.iter (fun sentence ->
let taggedSentence = tagger.tagSentence(sentence :?> ArrayList)
printfn "%O" (Sentence.listToString(taggedSentence, false))
)
// Text for tagging
let text = System.Console.ReadLine();
tagTexrFromReader <| new StringReader(text)
it won't matter if C# or F# - do make a function that gets a string and returns ... let
s say an int, you just need something like this (put it in some MyModule.fs):
namespace MyNamespace
module MyModule =
// this is your function with one argument (a string named input) and result of int
let myFun (input : string) : int =
// do whatever you have to
5 // the value of the last line will be your result - in this case a integer 5
call it in from C#/.net with
int result = MyNamespace.MyModule.myFun ("Hallo");
I hope this helps you out a bit
For your example this would be:
let myFun (text : string) =
use reader = new StringReader(text)
tagTexrFromReader reader
as you'll have this in the module File1 you can just call it with var res = Fiel1.myFun(text);
BTW: use is in there because StringReader is IDisposable and using use F# will dispose the object when you exit the scope.
PS: is tagTexrFromReader a typo?

F# lazy eval from stream reader?

I'm running into a bug in my code that makes me think that I don't really understand some of the details about F# and lazy evaluation. I know that F# evaluates eagerly and therefore am somewhat perplexed by the following function:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
When I call this in FSI:
> let d = getStringFromFile();;
System.ObjectDisposedException: Cannot read from a closed TextReader.
at System.IO.__Error.ReaderClosed()
at System.IO.StreamReader.ReadToEnd()
at <StartupCode$FSI_0134>.$FSI_0134.main#()
Stopped due to error
This makes me think that getStringFromFile is being evaluated lazily--so I'm totally confused. I'm not getting something about how F# evaluates functions.
For a quick explanation of what's happening, lets start here:
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
You can re-write the first two lines of your function as:
let s = File.OpenRead(#"c:\eo\raw.txt")
Next, you've omitted the parentheses on this method:
let data = r.ReadToEnd
r.Close()
s.Close()
data
As a result, data has the type unit -> string. When you return this value from your function, the entire result is unit -> string. But look what happens in between assigning your variable and returning it: you closed you streams.
End result, when a user calls the function, the streams are already closed, resulting in the error you're seeing above.
And don't forget to dispose your objects by declaring use whatever = ... instead of let whatever = ....
With that in mind, here's a fix:
let getStringFromFile() =
use s = File.OpenRead(#"c:\eo\raw.txt")
use r = new StreamReader(s)
r.ReadToEnd()
You don't read from your file. You bind method ReadToEnd of your instance of StreamReader to the value data and then call it when you call getStringFromFile(). The problem is that the stream is closed at this moment.
I think you have missed the parentheses and here's the correct version:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd()
r.Close()
s.Close()
data

Resources