Comparing file contents in F #

I wrote a quick and dirty function to compare the contents of a file (BTW, I already tested that they are the same size):

let eqFiles f1 f2 =
  let bytes1 = Seq.ofArray (File.ReadAllBytes f1)
  let bytes2 = Seq.ofArray (File.ReadAllBytes f2)
  let res = Seq.compareWith (fun x y -> (int x) - (int y)) bytes1 bytes2
  res = 0

I am not happy with reading all the contents into an array. I would prefer a lazy sequence of bytes, but I cannot find the correct API in F #.

+3
source share
6 answers

If you want to use the full power of F #, you can also do this asynchronously. The idea is that you can asynchronously read a block of a specified size from both files, and then compare the blocks (using the standard and simple comparison of byte arrays).

, - ( Async<T>, , , seq<T> ). :

EDIT http://fssnip.net/1k, F #: -)

open System.IO

/// Represents a sequence of values 'T where items 
/// are generated asynchronously on-demand
type AsyncSeq<'T> = Async<AsyncSeqInner<'T>> 
and AsyncSeqInner<'T> =
  | Ended
  | Item of 'T * AsyncSeq<'T>

/// Read file 'fn' in blocks of size 'size'
/// (returns on-demand asynchronous sequence)
let readInBlocks fn size = async {
  let stream = File.OpenRead(fn)
  let buffer = Array.zeroCreate size

  /// Returns next block as 'Item' of async seq
  let rec nextBlock() = async {
    let! count = stream.AsyncRead(buffer, 0, size)
    if count > 0 then return Ended
    else 
      // Create buffer with the right size
      let res = 
        if count = size then buffer
        else buffer |> Seq.take count |> Array.ofSeq
      return Item(res, nextBlock()) }

  return! nextBlock() }

:

let rec compareBlocks seq1 seq2 = async {
  let! item1 = seq1
  let! item2 = seq1
  match item1, item2 with 
  | Item(b1, ns1), Item(b2, ns2) when b1 <> b2 -> return false
  | Item(b1, ns1), Item(b2, ns2) -> return! compareBlocks ns1 ns2
  | Ended, Ended -> return true
  | _ -> return failwith "Size doesn't match" }

let s1 = readInBlocks "f1" 1000
let s2 = readInBlocks "f2" 1000
compareBlocks s1 s2
+9

, .

let rec compareFiles (fs1: FileStream) (fs2: FileStream) =
      match fs1.ReadByte(),fs2.ReadByte() with
      | -1,-1 -> true //all bytes have been enumerated and were all equal
      | _,-1 -> false //the files are of different length
      | -1,_ -> false //the files are of different length
      | x,y when x <> y -> false
             //only continue to the next bytes when the present two are equal 
      | _ -> compareFiles fs1 fs2 
+6

, , , File and Stream(and it descendants like StreamReader and so ) .Net .

+1

, -,

open System

let seqOfFstream (fstream: IO.FileStream) = seq {
    let currentByte = ref 0
    while !currentByte >= 0 do
        currentByte := fstream.ReadByte()
        yield !currentByte
}

let fileEq fname1 fname2 =
    use f1 = IO.File.OpenRead fname1
    use f2 = IO.File.OpenRead fname2    
    not (Seq.exists2 (fun a b -> a <> b) (seqOfFstream f1) (seqOfFstream f2))
+1

F # - , , FileStream File.ReadAllBytes. : " F #".

0

. , ? . . , , readInBlocks. :

let readInBlocks fn size =
[...]

:

let readInBlocks (stream:FileStream) size = 
[...]

:

let compareFile (filePath1, filePath2) =
    use stream1 = File.OpenRead(filePath1)
    use stream2 = File.OpenRead(filePath2)
    let s1 = readInBlocks stream1 1000        
    let s2 = readInBlocks stream2 1000
    let isEqual =
        compareBlocks s1 s2
        |> Async.RunSynchronously                
    isEqual

:

open System.IO

/// Represents a sequence of values 'T where items 
/// are generated asynchronously on-demand
type AsyncSeq<'T> = Async<AsyncSeqInner<'T>> 
and AsyncSeqInner<'T> =
  | Ended
  | Item of 'T * AsyncSeq<'T>

/// Read file 'fn' in blocks of size 'size'
/// (returns on-demand asynchronous sequence)
let readInBlocks (stream:FileStream) size = 
    async {                            
        let buffer = Array.zeroCreate size
        /// Returns next block as 'Item' of async seq
        let rec nextBlock() = 
            async {
                let! count = stream.AsyncRead(buffer, 0, size)
                if count = 0 then return Ended
                else 
                    // Create buffer with the right size
                    let res = 
                        if count = size then buffer
                        else buffer |> Seq.take count |> Array.ofSeq
                    return Item(res, nextBlock()) 
            }
        return! nextBlock()
    }

/// Asynchronous function that compares two asynchronous sequences
/// item by item. If an item does not match, 'false' is returned
/// immediately without generating the rest of the sequence. If the
/// lengths don't match, exception is thrown.
let rec compareBlocks seq1 seq2 = async {
  let! item1 = seq1
  let! item2 = seq2
  match item1, item2 with 
  | Item(b1, ns1), Item(b2, ns2) when b1 <> b2 -> return false
  | Item(b1, ns1), Item(b2, ns2) -> return! compareBlocks ns1 ns2
  | Ended, Ended -> return true
  | _ -> return failwith "Size does not match" }

/// Compare two files using 1k blocks
let compareFile (filePath1, filePath2) =
    use stream1 = File.OpenRead(filePath1)
    use stream2 = File.OpenRead(filePath2)
    let s1 = readInBlocks stream1 1000        
    let s2 = readInBlocks stream2 1000
    let isEqual =
        compareBlocks s1 s2
        |> Async.RunSynchronously                
    isEqual
0

Source: https://habr.com/ru/post/1781310/


All Articles