Сравнение содержимого файла в F # - PullRequest
3 голосов
/ 20 декабря 2010

Я написал быструю и грязную функцию для сравнения содержимого файлов (кстати, я уже проверял, что они имеют одинаковый размер):

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

Я не доволен чтением всего содержимого в массив. Я предпочел бы иметь ленивую последовательность байтов, но я не могу найти правильный API в F #.

Ответы [ 5 ]

9 голосов
/ 21 декабря 2010

Если вы хотите использовать всю мощь F #, то вы также можете сделать это асинхронно. Идея состоит в том, что вы можете асинхронно читать блок указанного размера из обоих файлов, а затем сравнивать блоки (используя стандартное и простое сравнение байтовых массивов).

На самом деле это интересная проблема, потому что вам нужно сгенерировать что-то вроде асинхронной последовательности (последовательность значений Async<T>, которая генерируется по требованию, но без блокировки потоков, как при простой seq<T> или итерации). Функция для чтения данных и объявления асинхронной последовательности может выглядеть так:

РЕДАКТИРОВАТЬ Я также разместил фрагмент на 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
6 голосов
/ 20 декабря 2010

это будет сравнивать файлы байтов для байта и ярлыка, если есть различия на этом пути. Он также будет обрабатывать файлы разных размеров

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 
1 голос
/ 20 декабря 2010

Как уже говорили другие, используйте ленивый ввод / вывод, например

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 голос
/ 20 декабря 2010

Вы должны передавать файлы в потоковом режиме, просто читайте их блок за блоком, возможно, File and Stream(and it's descendants like StreamReader and so ) библиотеки в .Net могут удовлетворить ваши потребности.

0 голосов
/ 20 декабря 2010

Для этого вам не нужно ничего нового в F # - я бы просто определил последовательность, которая выдает байты, используя FileStream снизу вместо File.ReadAllBytes.Затем вы можете сравнить две такие последовательности "F # way".

...