Skip to content

Instantly share code, notes, and snippets.

@aklefdal
Last active August 29, 2015 14:28
Show Gist options
  • Save aklefdal/25d1835eafbe340ef762 to your computer and use it in GitHub Desktop.
Save aklefdal/25d1835eafbe340ef762 to your computer and use it in GitHub Desktop.
open System
open System.IO
open System.Collections.Generic
open System.Security.Cryptography
type CommandLineOptions = {
startkatalog : DirectoryInfo
minFilStorrelseIBytes : int64
}
let filesInDirectory (dir:DirectoryInfo) =
try
// Kaster exception dersom ingen tilgang
dir.EnumerateFiles()
with
| _ ->
printfn "Ingen tilgang til %s" dir.FullName
// Ignorerer kataloger uten tilgang
Seq.empty
let directoriesInDirectory (dir:DirectoryInfo) =
try
dir.EnumerateDirectories()
with
| _ -> Seq.empty // Ignorerer kataloger uten tilgang. dette er logget ifm enumerering av filer
let rec filer dir =
seq { yield! filesInDirectory dir
yield! directoriesInDirectory dir |> Seq.collect filer }
let lagHash (fil:FileInfo) =
use sha256 = SHA256.Create()
try
// Kaster exception dersom vi ikke har lesetilgang til filen
use stream = fil.OpenRead()
let hash = sha256.ComputeHash stream
Some (Convert.ToBase64String(hash))
with
| _ -> None
// Her lagrer vi informasjon om filene vi har lest. Nøkkel er en hash av filen, verdien er fullt filnavn
let filLager = Dictionary<string, string>()
let finnDuplikat fil =
let hashOption = lagHash fil
match hashOption with
| Some hash ->
if (filLager.ContainsKey(hash)) then
printfn "Original fil: %s" filLager.[hash]
printfn "Duplikat fil: %s" fil.FullName
printfn ""
else
filLager.Add(hash, fil.FullName)
| None -> printfn "Ingen tilgang til %s" fil.FullName
let PrintDuplikater options =
filer options.startkatalog
|> Seq.filter (fun fil -> fil.Length >= options.minFilStorrelseIBytes)
|> Seq.iter (fun fil -> finnDuplikat fil)
let parseCommandLineOptions (args: string[]) =
match args with
| [|bytes; katalog|] ->
let isLong, minFilStorrelseIBytes = Int64.TryParse(bytes)
let katalogEksisterer = Directory.Exists katalog
if isLong && katalogEksisterer then
Some {
startkatalog = DirectoryInfo(katalog)
minFilStorrelseIBytes = minFilStorrelseIBytes
}
else
None
| _ -> None
[<EntryPoint>]
let Main(args) =
let commandLineOptions = parseCommandLineOptions args
match commandLineOptions with
| Some options ->
PrintDuplikater options
0
| None ->
// Enkel feilmelding ved feil bruk
printfn "Kalles fx. slik: %s 100000 C:\\Temp\\" (Environment.GetCommandLineArgs()).[0]
-1
@aklefdal
Copy link
Author

Problemet som skulle løses var å liste ut duplikate filer i en katalog, inkludert underkataloger. Bare filer over en gitt størrelse skal sjekkes. Programmet skal kunne kompileres uten noe tillegg, og ved kjøring skal skikkelige feilmeldinger ved feil bruk av programmet.

Spesielle utfordringer:

  • Parsing av parametrene til programmet
  • Håndtering av kataloger uten tilgang
  • Håndtering av filer uten lesetilgang

Designvalg:

  • Dersom filen finnes mange steder, så vil den bli listet ut flere ganger. Alternativet hadde vært å bygge opp en liste over duplikater, hvor vi for hver fil (hash) har en liste over filnavn.
  • Duplikat fil finnes ved hjelp av en hash av hele filen. Alternativet hadde vært bit-for-bit sammenligning med early exit

@aklefdal
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment