Что Лучший способ состоит в том, чтобы заменить текст в Файле с помощью C#/.NET?

Давайте снова посмотрим на ваш последний пример (сокращенный мной):

trait GenericAssociated {
    type GenericAssociated;
}

impl GenericAssociated for Struct {
    type GenericAssociated = G;
}

Это не не имеет родственных связанных типов! У вас просто есть универсальный тип в вашем блоке impl, который вы назначаете для связанного типа. Мм, хорошо, я вижу, откуда возникла путаница.

В вашем примере ошибки с «параметром типа G не ограничены признаком impl, self-типом или предикатами». Это не изменится, когда GAT будут внедрены, потому что, опять же, это не имеет ничего общего с GAT.

Использование GAT в вашем примере может выглядеть так:

trait Associated {
    type Associated; // <-- note the ``! The type itself is 
                        //     generic over another type!

    // Here we can use our GAT with different concrete types 
    fn user_choosen(&self, v: X) -> Self::Associated;
    fn fixed(&self, b: bool) -> Self::Associated;
}

impl Associated for Struct {
    // When assigning a type, we can use that generic parameter `T`. So in fact,
    // we are only assigning a type constructor.
    type Associated = Option;

    fn user_choosen(&self, v: X) -> Self::Associated {
        Some(x)
    }
    fn fixed(&self, b: bool) -> Self::Associated {
        Some(b)
    }
}

fn main() {
    Struct.user_choosen(1);    // results in `Option`
    Struct.user_choosen("a");  // results in `Option<&str>`
    Struct.fixed(true);        // results in `Option`
    Struct.fixed(1);           // error
}

Но чтобы ответить на ваш главный вопрос:

В чем разница между чертой универсальный тип и родовой ассоциированный тип?

blockquote>

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

Есть много мотивационных примеров в RFC , в частности, потоковый итератор и пример семейства указателей. Давайте быстро разберемся, почему потоковый итератор не может быть реализован с помощью обобщенных признаков.

GAT-версия потокового итератора выглядит следующим образом:

trait Iterator {
    type Item<'a>;
    fn next(&self) -> Option>;
}

В текущем Rust мы можем поместить параметр времени жизни в признак вместо связанного с ним типа:

trait Iterator<'a> {
    type Item;
    fn next(&'a self) -> Option;
}
[1141 ] Пока все хорошо: все итераторы могут реализовать эту черту, как и раньше. Но что, если мы хотим использовать это?

fn count>(it: I) -> usize {
    let mut count = 0;
    while let Some(_) = it.next() {
        count += 1;
    }
    count
}

Какое время жизни мы должны аннотировать? Помимо аннотирования времени жизни 'static, у нас есть два варианта:

  • fn count<'a, I: Iterator<'a>>(it: I): это не сработает, потому что вызывающий объект выбирает универсальные типы функций. Но it (который станет self в вызове next) живет в нашем стековом фрейме. Это означает, что время жизни it неизвестно вызывающей стороне. Таким образом мы получаем компилятор ( Playground ). Это не вариант.
  • fn count Iterator<'a>>(it: I) (с использованием HRTB): кажется, это работает, но у него есть тонкие проблемы. Теперь нам необходимо I реализовать Iterator для любого времени жизни 'a. Это не проблема для многих итераторов, но некоторые итераторы возвращают элементы, которые не существуют вечно, и, следовательно, они не могут реализовать Iterator для любого времени жизни - просто время жизни короче, чем их элемент. Использование этих более высоких ранговых границ часто приводит к секретным 'static границам, которые очень ограничивают. Так что это тоже не всегда работает.

Как видите, мы не можем правильно записать границы I. И на самом деле, мы даже не хотим упоминать время жизни в сигнатуре функции count! Это не должно быть необходимо. И это именно то, что нам позволяют делать GAT (между прочим). С GAT мы могли бы написать:

fn count(it: I) { ... }

И это будет работать. Потому что «применение конкретного времени жизни» происходит только тогда, когда мы называем next.

Если вас интересует еще больше информации, вы можете взглянуть на мое сообщение в блоге «Решение проблемы обобщенного потокового итератора без GAT» , где я пытаюсь использовать универсальные типы для признаков, чтобы обойти недостаток ГАТ. И (спойлер): обычно это не работает.

7
задан Timothy Khouri 18 May 2009 в 20:11
поделиться

6 ответов

Если вы можете, вы должны вставить заполнитель, который вы перезаписываете в конце фактическим числом и пробелами.

Если это не вариант, сначала запишите ваши данные в файл кэша. Когда вы знаете фактическое число, создайте выходной файл и добавьте данные из кеша.

4
ответ дан 6 December 2019 в 19:41
поделиться

Мне не нужно заменять "исправлено количество байтов "

Вы уверены? Если вы напишете большое число в первую строку файла (UInt32.MaxValue или UInt64.MaxValue), тогда, когда вы найдете правильное фактическое число, вы можете заменить это количество байтов правильным числом, но оставленным с нулями, так что это все еще действительное целое число. например,

Replace  999999 - your "large number placeholder"
With     000100 - the actual number of accounts
2
ответ дан 6 December 2019 в 19:41
поделиться

BEST очень субъективен. Для любого небольшого файла вы можете легко открыть весь файл в памяти и заменить то, что вы хотите, используя замену строки, а затем перезаписать файл.

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

Вы тестировали этот наивный подход? Вы видели реальную проблему с этим?

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

 TYPE temp.txt >> outfile.txt
3
ответ дан 6 December 2019 в 19:41
поделиться

Мне кажется, правильно ли я понял вопрос?

Как НАИЛУЧШИЙ способ в C # / .NET открыть файл (в данном случае простой текстовый файл) и заменить данные, которые находятся в первой «строке» текста?

Как насчет размещения в верхней части файла токена {UserCount} при его первом создании.

Затем используйте TextReader для чтения файла построчно. Если это первая строка, найдите {UserCount} и замените своим значением. Запишите каждую прочитанную вами строку, используя TextWriter

Пример:

    int lineNumber = 1;
    int userCount = 1234;
    string line = null;

    using(TextReader tr = File.OpenText("OriginalFile"))
    using(TextWriter tw = File.CreateText("ResultFile"))
    {

        while((line = tr.ReadLine()) != null)
        {
            if(lineNumber == 1)
            {
                line = line.Replace("{UserCount}", userCount.ToString());
            }

            tw.WriteLine(line);
            lineNumber++;
        }

    }
2
ответ дан 6 December 2019 в 19:41
поделиться

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

1
ответ дан 6 December 2019 в 19:41
поделиться

Хорошо, ранее я предлагал подход, который был бы лучше при работе с существующими файлами.

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

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

    private void WriteUsers()
    {   
        string userCountString = null;
        ASCIIEncoding enc = new ASCIIEncoding();
        byte[] userCountBytes = null;
        int userCounter = 0;

        using(StreamWriter sw = File.CreateText("myfile.txt"))
        {
            // Write a blank line and return
            // Note this line will later contain our user count.
            sw.WriteLine();

            // Write out the records and keep track of the count 
            for(int i = 1; i < 100; i++)
            {
                sw.WriteLine("User" + i);
                userCounter++;
            }

            // Get the base stream and set the position to 0
            sw.BaseStream.Position = 0;

            userCountString = "User Count: " + userCounter;

            userCountBytes = enc.GetBytes(userCountString);

            sw.BaseStream.Write(userCountBytes, 0, userCountBytes.Length);
        }

    }
1
ответ дан 6 December 2019 в 19:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: