'OCaml String Removal and Concatenation

The question I have to solve is the following:

Write a function

val newFileName : string -> string -> string;

such that a call (newFileName oldFileName newExtension) returns oldFileName but with the new extension.

For example...

  1. the call (newFileName "MyFileName.java" "class") should return the string "MyFileName.class". The input file name may have zero or more dots.

  2. The call (newFileName "MyFile" "anything") (i.e., with no dots) should return just "MyFile";

  3. The call (newFileName "My.File.java" "class") should return "My.File.class" (i.e., only the rightmost dot matters);

  4. The call (newFileName "MyFile." "class") should return "MyFile.class", i.e., a trailing dot with no actual file extension still gives the new extension.

Hint: Take a look at String.rindex_opt in the standard library.

Here is what I have so far but it's not working:

let newFileName old_file new_class = 
  match String.rindex_opt old_file '.' with
  | None -> old_file
  | _ -> let first_part = String.sub old_file 0 (String.rindex old_file '.') in 
      first_part ^ new_class;;

Is there anything I could do differently?



Solution 1:[1]

You could use the value from rindex_opt.

utop # 
let replace_extension fn new_ext =
  match String.rindex_opt fn '.' with
  | None -> fn
  | Some ri -> String.sub fn 0 ri ^ "." ^ new_ext;; 
val replace_extension : string -> string -> string = <fun>
?( 13:34:57 )?< command 1 >??????????????????????????????????????{ counter: 0 }?
utop # replace_extension "hello.java" "class";;
- : string = "hello.class"
?( 13:36:19 )?< command 2 >??????????????????????????????????????{ counter: 0 }?
utop # replace_extension "hello" "class";;
- : string = "hello"

You might also use Format.sprintf.

utop # 
let replace_extension fn new_ext =
  match String.rindex_opt fn '.' with
  | None -> fn
  | Some ri -> Format.sprintf "%s.%s" (String.sub fn 0 ri) new_ext;; 
val replace_extension : string -> string -> string = <fun>
?( 13:37:33 )?< command 7 >??????????????????????????????????????{ counter: 0 }?
utop # replace_extension "hello.java" "class";;
- : string = "hello.class"

We might also use a local binding to clean this up a bit further.

let replace_extension fn new_ext =
  match String.rindex_opt fn '.' with
  | None -> fn
  | Some ri -> 
      let name = String.sub fn 0 ri in
      Format.sprintf "%s.%s" name new_ext

Solution 2:[2]

It is easily done using the OCaml standard library, namely using the Filename module, e.g.,

let change_extension path ext = 
  Filename.remove_extension path ^ "." ^ ext

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 ivg