Đầu tiên hãy để tôi xin lỗi về quy mô của vấn đề này nhưng tôi thực sự đang cố gắng suy nghĩ về chức năng và đây là một trong những vấn đề khó khăn hơn mà tôi phải làm việc.Cách viết một tập tin chức năng "scanner"
Tôi muốn nhận một số đề xuất về cách tôi có thể xử lý sự cố mà tôi có một cách hoạt động, đặc biệt trong F #. Tôi đang viết một chương trình để đi qua danh sách các thư mục và sử dụng danh sách các mẫu regex để lọc danh sách các tệp được truy xuất từ các thư mục và sử dụng danh sách thứ hai các mẫu regex để tìm các kết quả phù hợp trong văn bản của các tệp đã truy xuất. Tôi muốn điều này trả về tên tệp, chỉ mục dòng, chỉ mục cột, mẫu và giá trị phù hợp cho từng phần văn bản khớp với mẫu regex đã cho. Ngoài ra, trường hợp ngoại lệ cần phải được ghi lại và có 3 trường hợp ngoại lệ có thể xảy ra: không thể mở thư mục, không thể mở tệp, đọc nội dung từ tệp không thành công. Yêu cầu cuối cùng của điều này là khối lượng của các tập tin "quét" cho các trận đấu có thể là rất lớn vì vậy toàn bộ điều này cần phải được lười biếng. Tôi không quá lo lắng về một giải pháp chức năng "thuần khiết" nhiều như tôi quan tâm đến một giải pháp "tốt" mà đọc tốt và hoạt động tốt. Một thách thức cuối cùng là làm cho nó tương tác với C# bởi vì tôi muốn sử dụng các công cụ winform để đính kèm thuật toán này vào một ui. Đây là nỗ lực đầu tiên của tôi và hy vọng điều này sẽ làm rõ vấn đề:
open System.Text.RegularExpressions
open System.IO
type Reader<'t, 'a> = 't -> 'a //=M['a], result varies
let returnM x _ = x
let map f m = fun t -> t |> m |> f
let apply f m = fun t -> t |> m |> (t |> f)
let bind f m = fun t -> t |> (t |> m |> f)
let Scanner dirs =
returnM dirs
|> apply (fun dirExHandler ->
Seq.collect (fun directory ->
try
Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
with | e ->
dirExHandler e directory
Array.empty))
|> map (fun filenames ->
returnM filenames
|> apply (fun (filenamepatterns, lineExHandler, fileExHandler) ->
Seq.filter (fun filename ->
filenamepatterns |> Seq.exists (fun pattern ->
let regex = new Regex(pattern)
regex.IsMatch(filename)))
>> Seq.map (fun filename ->
let fileinfo = new FileInfo(filename)
try
use reader = fileinfo.OpenText()
Seq.unfold (fun ((reader : StreamReader), index) ->
if not reader.EndOfStream then
try
let line = reader.ReadLine()
Some((line, index), (reader, index + 1))
with | e ->
lineExHandler e filename index
None
else
None) (reader, 0)
|> (fun lines -> (filename, lines))
with | e ->
fileExHandler e filename
(filename, Seq.empty))
>> (fun files ->
returnM files
|> apply (fun contentpatterns ->
Seq.collect (fun file ->
let filename, lines = file
lines |>
Seq.collect (fun line ->
let content, index = line
contentpatterns
|> Seq.collect (fun pattern ->
let regex = new Regex(pattern)
regex.Matches(content)
|> (Seq.cast<Match>
>> Seq.map (fun contentmatch ->
(filename,
index,
contentmatch.Index,
pattern,
contentmatch.Value))))))))))
Cảm ơn mọi đầu vào.
Cập nhật - ở đây là bất kỳ giải pháp cập nhật dựa trên thông tin phản hồi tôi nhận được:
open System.Text.RegularExpressions
open System.IO
type ScannerConfiguration = {
FileNamePatterns : seq<string>
ContentPatterns : seq<string>
FileExceptionHandler : exn -> string -> unit
LineExceptionHandler : exn -> string -> int -> unit
DirectoryExceptionHandler : exn -> string -> unit }
let scanner specifiedDirectories (configuration : ScannerConfiguration) = seq {
let ToCachedRegexList = Seq.map (fun pattern -> new Regex(pattern)) >> Seq.cache
let contentRegexes = configuration.ContentPatterns |> ToCachedRegexList
let filenameRegexes = configuration.FileNamePatterns |> ToCachedRegexList
let getLines exHandler reader =
Seq.unfold (fun ((reader : StreamReader), index) ->
if not reader.EndOfStream then
try
let line = reader.ReadLine()
Some((line, index), (reader, index + 1))
with | e -> exHandler e index; None
else
None) (reader, 0)
for specifiedDirectory in specifiedDirectories do
let files =
try Directory.GetFiles(specifiedDirectory, "*", SearchOption.AllDirectories)
with e -> configuration.DirectoryExceptionHandler e specifiedDirectory; [||]
for file in files do
if filenameRegexes |> Seq.exists (fun (regex : Regex) -> regex.IsMatch(file)) then
let lines =
let fileinfo = new FileInfo(file)
try
use reader = fileinfo.OpenText()
reader |> getLines (fun e index -> configuration.LineExceptionHandler e file index)
with | e -> configuration.FileExceptionHandler e file; Seq.empty
for line in lines do
let content, index = line
for contentregex in contentRegexes do
for mmatch in content |> contentregex.Matches do
yield (file, index, mmatch.Index, contentregex.ToString(), mmatch.Value) }
Một lần nữa, bất kỳ đầu vào được chào đón.
Bạn đã xem các trình phân tích cú pháp chức năng như Parsec chưa? –
Đây là rất nhiều văn bản. Hãy thử chia nhỏ nó để dễ đọc hơn. – Marcin
Tôi chỉ đơn giản sẽ sử dụng một giao diện và biểu thức đối tượng để tạo một thể hiện và đưa nó vào mã C#. –