Запись или чтение в / из нескольких файлов одновременно в неизвестном порядке в Юлии - PullRequest
0 голосов
/ 06 марта 2019

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

У меня есть рабочее решение (см. Код ниже), но я не знаю, является ли это очень оптимальным подходом. В частности, я хотел бы автоматически обрабатывать закрытие файлов даже в случае непредвиденной ошибки. Я пытался разобраться с этим делом, но, пожалуйста, см. Комментарий к коду:

Что, если здесь что-то пойдет не так, у нас все еще могут быть открытые файлы государство верно?

Насколько я прочитал, закрытие файлов защищает от повреждения файла. Это верно? Каковы другие последствия отсутствия файлов?

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

Рабочий раствор ( см. Вместо jupyter-notebook ):

Вопрос зависимости

# Ensure an empty directory for the execution of this question's code
tmp_dir = "/tmp/stackoverflow-question-55012211"
rm(tmp_dir, force=true, recursive=true)
mkdir(tmp_dir)


# Write example ".fakeq" files.
# In my real life problem, they would be ".fastq" (see https://en.wikipedia.org/wiki/FASTQ_format)
# and sample would not be known at this stage, simplifying to keep things relevant to question
open("$(tmp_dir)/pool1.fakeq", "w") do f
    write(f, "id1_sample1_ACGTA\n")
    write(f, "id2_sample3_CGTACG\n")
    write(f, "id3_sample2_GTACTAC\n")
    write(f, "id4_sample1_TACGGTAC\n")
    write(f, "id5_sample2_ACGTGTACG\n")
    write(f, "id6_sample3_CGTATACGTA\n")
    write(f, "id7_sample2_GTACCGTAC\n")
    write(f, "id8_sample1_TACGGTAC\n")
    write(f, "id9_sample1_ACGTGTA\n")
end

open("$(tmp_dir)/pool2.fakeq", "w") do f
    write(f, "id10_sample2_ACGTAACGTA\n")
    write(f, "id11_sample1_CGTACGCGTACG\n")
    write(f, "id12_sample3_GTACTACGTACTAC\n")
    write(f, "id13_sample2_TACGGTACTACGGTAC\n")
    write(f, "id14_sample1_ACGTGTACGACGTGTACG\n")
    write(f, "id15_sample3_CGTATACGTACGTATACGTA\n")
    write(f, "id16_sample2_GTACCGTACGTACCGTAC\n")
    write(f, "id17_sample1_TACGGTACTACGGTAC\n")
    write(f, "id18_sample1_ACGTGTAACGTGTA\n")
end


# This array can be in the order of 10 - 20 elements long
csv_header = [
    "identifier",
    "sample_name",
    "sequence",
    "sequence_length"
]

# This array can be in the order of 25 - 50 elements long.
# In real-life problem, we know this list of samples up front
# and sample_name is calculated by matching an array of nucleotide
# 'barcode' sequences up against each sequence in the .fastq files 
sample_names = [
    "sample1",
    "sample2",
    "sample3"
]

# This array can be in the order of 4 - 12 elements long
# In real-life problem, we know this list of pools up front and each
# pool corresponds to a .fastq file mentioned above
pool_list = [
    "pool1",
    "pool2"
]


# I am creating a mapping here so that a file is written in a location
# dependent on the sample name

# What if something goes wrong here, we could still have files in open state right?
# If inside the try block below, then potentially some files will be attempted to be
# closed before being opened
sample_csv_mapping = Dict(
    sample_name => open("$(tmp_dir)/$(sample_name).csv", "w")
    for sample_name in sample_names
)

Главный блок

# An attempt to ensure that files are closed in case of error
try
    # Initialises (overwrites) csv with header
    for (sample, csv_stream) in sample_csv_mapping
        write(csv_stream, join(csv_header, ","), "\n")
    end
    for pool in pool_list

        # This automatically handles closing file upon error
        open("$(tmp_dir)/$(pool).fakeq", "r") do f
            lines = readlines(f)
            for line in lines
                identifier, sample_name, sequence = split(line, "_")
                sequence_length = length(sequence)
                csv_row = [
                    identifier,
                    sample_name,
                    sequence,
                    sequence_length
                ]
                write(sample_csv_mapping[sample_name], join(csv_row, ","), "\n")
            end
        end
    end
finally
    println("Manually handle closing files whether upon successful run or upon error")
    for (sample, csv_stream) in sample_csv_mapping
        close(csv_stream)
    end
end

1 Ответ

1 голос
/ 06 марта 2019

В вашем коде лучшим вариантом кажется flush поток после записи каждого потока данных в поток.Это заставит записывать байты на диск, и, следовательно, вы можете избежать потери данных:

flush(f)

РЕДАКТИРОВАТЬ

, поскольку вы попросили помочь с редактированием кода:

  1. Ключи в именах образцов должны быть символами, а не строками
sample_names = Symbol.([
    "sample1",
    "sample2",
    "sample3"
])
Измените код, чтобы очистить файл всякий раз, когда изменения файла могут выглядеть следующим образом (при условии дальнейшей оптимизации):
        lastSample = :none
        open("$(tmp_dir)/$(pool).fakeq", "r") do f            
            lines = readlines(f)
            for line in lines
                identifier, sample_name, sequence = split(line, "_")
                sequence_length = length(sequence)
                csv_row = [
                    identifier,
                    sample_name,
                    sequence,
                    sequence_length

                if last_sample != :none || last_sample != sample_name
                   flush(sample_csv_mapping[last_sample])
                   last_sample  = sample_name
                end
                write(sample_csv_mapping[sample_name], join(csv_row, ","), "\n")
            end
        end
...