Коллеги, подскажите пожалуйста, есть ли способ посредством встроенного языка 1С отрезать (удалить) из файла (файлового потока) некоторое количество байт в начале, без копирования в новый файл ? И без помещения всего файла в память.
Например, есть некий большой (скажем, 10Гб) файл. Нужно удалить из него первые 100 байт. Сейчас мне приходится записывать в новый файловый поток (т.е. в новый файл) всё, начиная с 101-го байта. При этом требуется свободное дисковое пространство на размещение этого нового файла, т.е. еще 10Гб. Хотелось бы оптимизировать использование дискового пространства.
---
посредством встроенного языка 1С отрезать (удалить) из файла (файлового потока) некоторое количество байт в начале, без копирования в новый файл ? Открываем файл на чтение/запись, создаем две переменных для рабочего смещения: одну - для чтения, вторую - для записи. К первой (СмещениеЧтения) добавляем количество отрезаемых байт и дальше в цикле: считываем в буфер кусочек файла начиная со СмещениеЧтения и записываем в тот же файл по СмещениеЗаписи. Прибавляем к СмещениеЧтения и СмещениеЗаписи размер буфера - и так до конца файла.
---
посредством встроенного языка 1С отрезать (удалить) из файла (файлового потока) некоторое количество байт в начале, без копирования в новый файл ? Открываем файл на чтение/запись, создаем две переменных для рабочего смещения: одну - для чтения, вторую - для записи. К первой (СмещениеЧтения) добавляем количество отрезаемых байт и дальше в цикле: считываем в буфер кусочек файла начиная со СмещениеЧтения и записываем в тот же файл по СмещениеЗаписи. Прибавляем к СмещениеЧтения и СмещениеЗаписи размер буфера - и так до конца файла.
---
Правильно! Тоже как раз к такому решению и пришел.
Приз отправляется Игорю Ткаченко.

```1c
    Процедура СрезДвоичныхДанных(ФайлИсточник, УдалитьНач = 0, УдалитьКон = 0, РазмерБуфера = 512)

    Поток = Новый ФайловыйПоток(ФайлИсточник, РежимОткрытияФайла.Открыть, ДоступКФайлу.ЧтениеИЗапись);

    ИсходныйРазмер = Поток.Размер();
    ЦелевойРазмер = ИсходныйРазмер - УдалитьНач - УдалитьКон;

    Если УдалитьНач > 0 Тогда

    ПозицияЧтения = УдалитьНач;
    ПозицияЗаписи = 0;
    Буфер = Новый БуферДвоичныхДанных(РазмерБуфера);

    Пока Истина Цикл
    Поток.Перейти(ПозицияЧтения, ПозицияВПотоке.Начало);
    Прочитано = Поток.Прочитать(Буфер, 0, РазмерБуфера);
    Поток.Перейти(ПозицияЗаписи, ПозицияВПотоке.Начало);
    Поток.Записать(Буфер, 0, РазмерБуфера);
    Если Поток.ТекущаяПозиция() >= ЦелевойРазмер Тогда
    Прервать;
    КонецЕсли;
    ПозицияЧтения = ПозицияЧтения + РазмерБуфера;
    ПозицияЗаписи = ПозицияЗаписи + РазмерБуфера;
    КонецЦикла;

    КонецЕсли;

    Поток.УстановитьРазмер(ЦелевойРазмер);

    Поток.Закрыть();

    КонецПроцедуры
```
---
Судя по описанию, сдвиг в потоке чтения нужно выполнить только один раз, далее указатель сдвигается автоматически при чтении. То же касается потока записи. Ну и размер буфера нужно побольше, хотя бы мегабайт.
---
Судя по описанию, сдвиг в потоке чтения нужно выполнить только один раз
У меня тут один поток. Указатель всё время приходится двигать. Вперед для чтения, назад для записи.
А ваше предложение - это два отдельных потока, направленных в один файл? Интересно! Проверяли, так работает?
Ну и размер буфера нужно побольше, хотя бы мегабайт
Это пример ведь, для проверки. Буфер, да, можно и до 64Мб увеличить. По крайней мере такой по умолчанию у метода МенеджераФайловыхПотоков.СоздатьВременныйФайл().
---
Я бы для решения такой задачи два потока использовал и два файла (на чтение и на запись) так как такой способ точно работает. Скорее всего, физически всё равно это будут два разных файла, неочевидно, что обрезанный файл будет записан на то-же место.
---
для решения такой задачи два потока использовал и два файла
Это как раз противоречит начальной постановке. Хотелось именно избежать наличия двух огромных файлов. С лишним файлом всё делается вообще в одну строку (предварительно отступив пропускаемые байты):
Поток.КопироватьВ(НовыйПоток)
---
Задача чисто теоретическая. Во первых строках письма байтах файла обычно лежит заголовок, отрезав который файл нельзя будет использовать по назначению. Хотя, возможно, иногда может имеет смысл.

(9)
...Поток.ТекущаяПозиция() >= ЦелевойРазмер...

Как быть в случае, когда требуемый набор байт по количеству не кратен РазмерБуфера?
Ставить туда всегда 1?
Рассчитывать размер перед вызовом процедуры?
---
Задача чисто теоретическая
Задача как раз чисто практическая :)
Один из примеров (не единственный) - распаковка и извлечение инфы из DT-файла.
Другой пример (как раз то что оптимизирую сейчас) - из некоего веб-сервиса получен XML-ответ, в одном из элементов которого лежат данные файла в Base64 размером в несколько Гб. Вот как раз чтобы разделить этот ответ на отдельный XML без файла и сам файл, использую такой метод.

(17) Как быть в случае, когда требуемый набор байт по количеству не кратен РазмерБуфера?
О какой кратности тут речь? Зачем?
---
Коллеги, решение найдено. Но попробуем усложнить задачу. Всё то же самое:
(1) посредством встроенного языка 1С отрезать (удалить) из файла (файлового потока) некоторое количество байт в начале, без копирования в новый файл ? И без помещения всего файла в память.
Но теперь 2 СМ пойдут тому, кто предложит работающий быстрый вариант. Всё-таки перезапись кусками (даже по 64 Мб) работает... скажем, не быстро. Есть варианты ускорить, как думаете? Не за счет увеличения буфера.
---
кто предложит работающий быстрый вариант ...если только он вообще существует. Потому что вполне может оказаться, что тормозит эту операцию сама файловая система, а не исполнение кода 1С.

Для выяснения этого нужно (если возможно) временно отказаться от экономии памяти и дискового пространства и замерить - сколько времени выполняется код "в одну строку", пример - в (14).

Это и будет эталонный результат, теоретически достижимый максимум скорости. С ним сравниваем "дробное" обрезание и делаем выводы - можно ли его вообще ускорить и насколько?
Есть варианты ускорить, как думаете? Не за счет увеличения буфера. Ускорение вряд ли будет безболезненным - бесплатный сыр известно где лежит. ;)

Классическая дилемма - скорость или ресурсы. Нужна скорость - придется расходовать ресурсы. Экономим ресурсы - теряем в скорости.

Аналогично: нужно перевезти кучу коробок - нанимаем трейлер, получается быстро. Если трейлер дорого - возим по несколько коробок газелькой, это медленно. Можно ускорить,без трейлера? Да, нанять много газелей... но это опять получается расход ресурсов.
---
...если только он вообще существует
Да, конечно, если он вообще существует.
Вот отсечь "хвост" можно очень быстро, методом УстановитьРазмер().
Может быть есть какой-то хитрый способ сделать аналогичную операцию с "головой"?
---
Может быть есть какой-то хитрый способ сделать аналогичную операцию с "головой"? Вряд ли: файл на физическом уровне записывается на диск посекторно и его начало всегда совпадает с началом сектора. А секторы с точки зрения ОС, группируются в кластеры, с ними она и работает. Поэтому теоретически можно провернуть такую операцию, но! Только если надо было бы отрезать число байт, строго кратное размеру кластера (0,5-32 кб для FATxx, 4 кб для NTFS).

И то, возможность эта была (скорее всего) в уже далеком прошлом, когда использовалась FAT/FAT32. И не штатными средствами 1С, а двоичным дисковым редактором - тупо изменить в системной таблице FAT указатель на начало файла (номер начального кластера).

Но это была ювелирная "ручная" работа, которая сейчас, в эпоху NTFS, вообще вряд ли возможна (я так подозреваю, хотя в формате NTFS не копался).

К тому же, такая манипуляция приводит к нарушениям в файловой системе - "отрезанные" кластеры становятся "зависшими", они не принадлежат никакому файлу, но и не помечены как свободные. Спрашивается, оно нам надо?

Ну, а действия типа "берем последние 3996 (4 кб - 100) байт из первого кластера, добавляем 100 байт из второго, записываем все это в первый кластер (сектора/кластер пишутся полностью!), переходим ко второму кластеру, первые 100 байт нам уже не нужны, берем последние 3996, добавляем 100 байт из третьего,записываем... и так далее до конца файла" - это то, что мы и сейчас проделываем на уровне 1С. Только теперь уже на физическом уровне, который не обманешь и не ускоришь.
---
РазделитьДвоичныеДанные - СоединитьДвоичныеДанные =))

Ну и вообще ЧтениеДанных и прочее, чего вы с потоками то сидите
---
Тимур, мне кажется, или вы троллите?
Можете объяснить, каким образом для описанной задачи вы предлагаете использовать метод РазделитьДвоичныеДанные() ? Ну, заодно и где мы получим результат.
---
Делить на N кусков размером с нужный, объединяете N-1. А где вы получите результат лично я гадать не стану. Чисто теоретически серверу 1С может быть вообще не выделено 10 Гб ОЗУ, а на клиенте их и вообще скорее всего не будет. 1С не операционная система и не может решать, где хранятся данные. Операции с файлами тоже на ОС. В этом плане уже лет 40 или больше ничего не менялось. Нет?
---
"Однако по потоку можно перемещаться и без чтения или записи. Для этого используется метод Перейти. Первым параметром нужно указать смещение в байтах на которое нужно переместиться, вторым — позицию от которой нужно переместиться."
https://program1s.com/potoki/
---
Евгения, это понятно. Как раз так и пишу в новый поток, отступив на нужное количество байт. Можете раскрыть свое предложение поподробнее? Я не очень понял, что именно вы предлагаете.
---
Поток.Прочитать(<Буфер>, <ПозицияВБуфере>, <Количество>)
Оно?
---
+4 Количество=Поток.Размер()-100
---
Владимир, вы говорите (пишете) загадками :)
Согласно приведенному вами коду, мы прочитаем что-то из потока. Получим БуферДвоичныхДанных, размером чуть меньше 10Гб (исходя из примера). Заметьте, это будет в памяти, что уже противоречит условиям задачи. Но, даже если не обращать на это внимания, что предлагается сделать с этим дальше?
---
Насколько я понимаю в функционировании файловых систем и потоках чтения-записи:
это не будет занимать всё 10 Гб в памяти (оперативной).
если вы открыли файл на чтение, вы не можете его перезаписать, нужно открывать второй и в него писать, затем оба закрыть.
в буфер не обязательно считывать весь файл, можно переписывать один в другой порциями
---
..без копирования в новый файл ? И без помещения всего файла в память.

Вообще где планируешь работать с файлом?
---
Вопрос к первоначальной постановке (из 1)? Или к обновленной (из 12)?
По первоначальной решение есть (8, 9). Происходит запись кусками со сдвигом в тот же исходный файл. Затем он обрезается с конца.
Что касается обновленной постановки, ищется способ получить тот же результат, но быстрее.
---
Все же, размер буфера многократно повышает скорость.

Сделал обработку. Можете проверить.

Пример:
Удалить с начала = 100
Удалить с конца = 1000
Файл размер = 1 858 526 174 байт

При буфере 512 байт
Обрабатывается 31 226 мс

При буфере 1024 байт
Обрабатывается 15 755 мс

При буфере 16384 байт
Обрабатывается 1 857 мс

При буфере 65536 байт
Обрабатывается 1 206 мс
---
размер буфера многократно повышает скорость Размер буфера определяет число итераций цикла чтения-записи. И чем оно меньше (при прочих равных условиях) - очевидно, тем больше скорость выполнения программы. Любой, а не только предложенной автором.
 При буфере 1024 байт
Обрабатывается 15 755 мс Как и было сказано: в 2 раза меньше раз выполняется цикл - тупо в 2 раза быстрее выполняется программа.
При буфере 16384 байт
Обрабатывается 1 857 мс А вот тут линейная зависимость нарушается: буфер вырос в 16 раз, а скорость - всего в 8 с небольшим. Почему - см. ниже.
При буфере 65536 байт
Обрабатывается 1 206 мс Самое интересное - буфер больше в 4 раза предыдущего случая, а скорость - всего в полтора. То есть, скорость выполнения кода 1С уперлась во что-то не зависящее от оптимальности этого кода.

И это "что-то" - скорость дисковых операций чтения-записи, которая определяется производительностью дисковой подсистемы. И больше ничем.

На пальцах: при маленьком буфере программа дала задание файловой системе "записать 512 байт" - это выполнилось мгновенно и 1С бодро поскакала на следующий виток цикла.

А когда размер буфера 65536, то 1С так же бодро выдает команду записи 64к... и ждет... ждет... ждет - пока эта команда будет исполнена связкой ОС+железо. И никакой оптимизацией кода этого не исправить.

Думаю, что при буфере 64Мб цифры уже вообще практически не изменятся.

Так что вы своими данными только подтвердили мои предположения: на "быстрый" вариант автору вряд ли стоит рассчитывать.
---
Чуть ошибся в условие в цикле. На время выполнения в примере не влияет.

тут исправил.

```1c
    ...
    ПоследняяПозиция = ИсходныйРазмер - УдалитьКонец;

    ВремяНачалаВыполнения = ТекущаяУниверсальнаяДатаВМиллисекундах();

    Пока ПозицияЧтения < ПоследняяПозиция Цикл

    ФайловыйПоток.Перейти(ПозицияЧтения, ПозицияВПотоке.Начало);
    Прочитано = ФайловыйПоток.Прочитать(Буфер,0,РазмерБуфера);
    ПозицияЧтения = ПозицияЧтения + Прочитано;

    ФайловыйПоток.Перейти(ПозицияЗаписи, ПозицияВПотоке.Начало);
    ФайловыйПоток.Записать(Буфер, 0, Прочитано);
    ПозицияЗаписи = ПозицияЗаписи + Прочитано;

    КонецЦикла;
    ...
```
---
На время не влияет. То есть? Цифры в (22) не изменились? Или наоборот, вообще исчезла зависимость скорости выполнения от размера буфера?
---
Там обрез с конца 1000 байт, это ничтожно мало. Поэтому не влияет. Я про пример.

Чем больше размер буфера тем быстрее.
Можно найти экспериментально под определённый размер файла. Буфер 16384 или 65536 уже не сильно влияет на скорость.
---
Там обрез с конца 1000 байт, это ничтожно мало. Пральна, ведь обрезание конца файла - это просто изменение записи о файле в MFT, чтение-запись самого файла при этом не происходит.
Буфер 16384 или 65536 уже не сильно влияет на скорость. И это понятно: тут уже бОльший процент времени выполнения расходуется на дисковые операции, а не на исполнение кода 1С.
---
Коллеги, раз уж обновленная постановка задачи (про ускорение) не имеет решения, предлагаю следующий квест.
Теперь мы имеем большой файл данных, кодированных в Base64. Скажем, те же 10Гб. Задача раскодировать его также, на месте, в тот же файл. Не загоняя его целиком в память. На выходе должен получиться файл, абсолютно идентичный распакованному "стандартным" способом.
---
Задача раскодировать его также, на месте, в тот же файл. Не загоняя его целиком в память. А в чем проблема? Поскольку двоичные данные заведомо компактнее, чем Base64 (3 байта кодируются четырьмя символами), то раскодированный файл гарантированно поместится на месте исходного. Так что можно на 99% использовать ваш же код из (9), добавив в него буквально одну строчку раскодирования Base64 средствами языка 1С:

```1c
    Прочитано = Поток.Прочитать(Буфер, 0, РазмерБуфера);
    //begin
    Буфер = Base64Значение(Буфер);
    //end
    Поток.Перейти(ПозицияЗаписи, ПозицияВПотоке.Начало);
    //begin Поток.Записать(Буфер, 0, РазмерБуфера);
    Поток.Записать(Буфер, 0, РазмерБуфера*0.75);
    //end
```

В конце файл обрезать по получившемуся размеру содержимого. Фсё!

Впрочем, еще одно замечание: РазмерБуфера должен быть кратен 4, оптимальное значение подобрать экспериментально - как там 1С будет справляться с Base64Значение() разных по длине строк - ХЗ.

Если будет тормозить, то можно попробовать создать два буфера - для чтения и для записи:

```1c
    //begin
    БуферЗаписи = Base64Значение(БуферЧтения);
    //end
```
Соответственно, надо учесть разницу в размерах.
---
Это я уже попробовал, первым делом. Вариант с 2-мя буферами. Записываю в поток количество байт, равное длине второго буфера. Но у меня результирующий файл не получается идентичным эталону. Первый блок (содержимое прочитанное в буфер и декодированное) - идентичен, а дальше всё расползается.

Но я поразмышлял, и думаю, что уже понял в чем проблема. Там Base64 порезан на строки длиной по 64 символа. И из-за включений переносов строк, полученные в буфер данные не получаются кратными 4 (хотя сам размер буфера кратен). Из-за этого, думаю, срез (начало следующего блока) приходится на неправильные места. Вечером проверю свою догадку.
---
И из-за включений переносов строк, полученные в буфер данные не получаются кратными 4 (хотя сам размер буфера кратен). Из-за этого, думаю, срез происходит в неправильных местах. Совершенно верно! В таком случае надо делать буфер кратным 66 байтам - насколько я помню, символ переноса строки состоит из двух байт.

И перед декодированием Base64 удалять Символы.ПС из буфера чтения.

А при определении размера буфера записи учитывать все эти манипуляции.
---
В таком случае надо делать буфер кратным 66 байтам
Это сработает только в текущем случае. Нужно как-то универсально сделать, чтобы все Base64 декодировались нормально, независимо от того, порезаны ли, и на какие части.
У меня была мысль в прочитанном буфере (скажем 64-мегабайтном) найти последний перенос, отбросить байты после него, и на размер этих байт отступить назад позицию для чтения следующего блока.
---
найти последний перенос, отбросить байты после него, и на размер этих байт отступить назад позицию для чтения следующего блока. Это - оптимальное решение.
---
в прочитанном буфере (скажем 64-мегабайтном) Вот тут меня терзают смутные сомнения: дальше ведь работа с этим буфером будет как со строкой (поиск и удаление ПС, декодирование), поэтому лично я бы ориентировался на размер буфера 64 килобайта - вроде как что-то встречалось о таком ограничении на длину строковых переменных?

Во всяком случае, нагружать систему обработкой строки размером в 64 мегабайта лично я бы не стал в любом случае.
---
Во всяком случае, нагружать систему обработкой строки размером в 64 мегабайта лично я бы не стал в любом случае
Спасибо, я учту. В принципе, размер буфера делаю параметром метода, поэтому с ним можно будет поэкспериментировать. По крайней мере, без поисков, только декодирование в большом буфере не вызывает никаких проблем. А вот с поиском переносов нужно будет подумать, как оптимальнее сделать. Пока у меня в мыслях использование метода Разделить() по маркеру. Но это порождает в памяти массив с Буферами, идентичный по размеру исходному Буферу, хотя меня интересует лишь последний элемент этого массива.
---
Пока у меня в мыслях использование метода Разделить() по маркеру Я бы предпочел СтрНайти() - там можно задать направление поиска с конца строки.

Или "в лоб": удалить из буфера все Символы.ПС через СтрЗаменить() и посмотреть на длину получившейся строки - остаток от деления на 64 даст количество "лишних" прочитанных символов.
Но это порождает в памяти массив с Буферами, идентичный по размеру исходному Буферу Вообще очень многие операции со строками дополнительно создают в памяти выходную строку в дополнение к исходной. По этой причине тоже не стоит злоупотреблять размером буфера.
---
а что у вас за ОС, что вы так свободно решаете находятся данные в памяти или на диске?
---
Хотите рассказать мне и другим коллегам где 1С работает с объектами ДвоичныеДанные, БуферДвоичныхДанных и другими, употребляемыми в подобных задачах?

Код выполняется на клиенте. ОС может быть любой из поддерживаемых 1С. Равно как и доступные ресурсы.

ЧтениеДанных, ЗаписьДанных также используются. Там где ими оперировать удобнее.
---
Я хочу чтобы мне коллеги рассказали, как определяют место нахождения данных, ОЗУ или диск. Особенно, когда речь о 10 Гб. Вот у меня на ноуте 8Гб ОЗУ и мне все ясно, где там чего. А у кого 16, могут же подумать, что 10 из них содержат обрабатываемый файл, как они это проверяют?
---
http://forum.infostart.ru/forum9/topic138909/message1431739/#message1431739
---
Семен, спасибо за отклик.
Но мы ведь теперь держим курс на отказ от всяких COMОбъект() и ожидаем что нативный 1С-код будет работать и в Windows, и в Linux, и в Mac-OS.