Stream read error delphi

Delphi 10.3.2 + Windows 10 Pro (1903, сборка ОС 18362.267) = Stream Read Error В последнее время использую исключительно Delphi Community Edition. Недавно, а именно 18 июля 2019 года, вышла Delphi 10.3.2 и я, естественно, решил обновиться. Поставил, поработал ровно два дня и… сегодня решил запустить Delphi и получил вот такую вот ошибку: При […]

В последнее время использую исключительно Delphi Community Edition. Недавно, а именно 18 июля 2019 года, вышла Delphi 10.3.2 и я, естественно, решил обновиться. Поставил, поработал ровно два дня и… сегодня решил запустить Delphi и получил вот такую вот ошибку:

и всё. Подчеркну, что IDE абсолютно «голая» — никаких сторонних компонентов даже из GetIt не устанавливалось. Пробовал запускать IDE от имени администратора, перезапускать комп, протирать пыль с монитора и прочие первостепенные операции — результата ноль.

Примечательно, что сей баг появился ровно после того, как Windows 10 Pro обновился до версии 1903

Попробую переустановить IDE, возможно, поможет. Ну, а если не поможет…будем откатываться на версию назад до тех пор пока Delphi снова не заработает.

Update: полная переустановка Delphi IDE помогла. Во всяком случае, после переустановки Delphi запустилась без ошибок

Stream classes all share several methods for reading and writing data. These methods are distinguished by whether they:

The Read method reads a specified number of bytes from the stream, starting at its current Position, into a buffer. Read then advances the current position by the number of bytes actually transferred. The prototype for Read is

Read is useful when the number of bytes in the file is not known. Read returns the number of bytes actually transferred, which may be less than Count if the stream did not contain Count bytes of data past the current position.

The Write method writes Count bytes from a buffer to the stream, starting at the current Position. The prototype for Write is:

After writing to the file, Write advances the current position by the number of bytes written, and returns the number of bytes actually written, which may be less than Count if the end of the buffer is encountered or the stream can’t accept any more bytes.

The counterpart procedures are ReadBuffer and WriteBuffer which, unlike Read and Write, do not return the number of bytes read or written. These procedures are useful in cases where the number of bytes is known and required, for example when reading in structures. ReadBuffer and WriteBuffer raise an exception (EReadError and EWriteError) if the byte count can not be matched exactly. This is in contrast to the Read and Write methods, which can return a byte count that differs from the requested value. The prototypes for ReadBuffer and WriteBuffer are:

These methods call the Read and Write methods to perform the actual reading and writing.

TStream defines specialized methods, ReadComponent and WriteComponent, for reading and writing components. You can use them in your applications as a way to save components and their properties when you create or alter them at run time.

ReadComponent and WriteComponent are the methods that the IDE uses to read components from or write them to form files. When streaming components to or from a form file, stream classes work with the TFiler classes, TReader and TWriter, to read objects from the form file or write them out to disk.

If you are passing a string to a read or write function, you need to be aware of the correct syntax. The Buffer parameters for the read and write routines are var and const types, respectively. These are untyped parameters, so the routine takes the address of a variable.

The default type when working with strings is UnicodeString. However, passing a long string as the Buffer parameter does not produce the correct result. Long strings contain a size, a reference count, and a pointer to the characters in the string. Consequently, dereferencing a long string does not result in the pointer element. You need to first cast the string to a Pointer or PChar, and then dereference it. For example:

В моем автономном приложении при чтении/записи из моей базы данных с помощью BDE проблем не возникает. Когда я выгружаю .EXE на наш сетевой том NetWare 3.11, я получаю случайные сообщения об ошибке «Stream Read Error» (ошибка чтения потока). В сети у меня имеется BDE, но пользователи имеют на своих жестких дисках собственные файлы IDAPI.CFG. Может мне кто-нибудь прояснит ситуацию?

В программе конфигурирования Database Engine Configuration, на закладке ‘system’, попробуйте изменить значение по умолчанию для MAXFILEHANDLES с 48 на 12. Не знаю почему, но это решило мои проблемы, у меня исчезли ошибки ‘Stream read error’ и различные GPF-ы.

Вопреки логики, как мне УМЕНЬШИТЬ количество дескрипторов файлов? Повышать мне их не удалось.

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

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

1. Базы данных (Paradox, Oracle и т.п.)

2. Способа доступа (ADO, BDE и т.д.)

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) — крупнейшем в рунете сборнике материалов по Дельфи
  • Вопросы по SQL и вопросы по базам данных не связанные с Дельфи задавать здесь

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Vit, Петрович.

Правила форума «Delphi: Базы данных и репортинг»
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Базы данных и репортинг | Следующая тема »

[ Время генерации скрипта: 0.0845 ] [ Использовано запросов: 22 ] [ GZIP включён ]

Источник

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

12 ноября 2011 г.

Сериализация — потоки данных

Оглавление

Общие сведения

Если вы вспомните определение файла, то оно звучало так: «файл — это устройство с последовательным доступом, к которому можно обратиться по имени». При этом физические файлы на диске являются лишь частным случаем. Отнимите из этого определения часть «имеющие имя» — и вы получите определение потока данных. Поток данных представляет собой «что-то» с последовательным доступом. Вы можете читать из него данные или записывать. Некоторые потоки поддерживают позиционирование (вроде физического файла на диске), другие — нет (вроде сетевого сокета).

Потоки данных являются де-факто стандартом для обмена данными в Delphi. Всюду в своих процедурах, где вам необходимо принимать или отправлять нетипизированный набор данных, используйте потоки данных. Многие механизмы Delphi по умолчанию умеют работать именно с потоками данных, предоставляя методы вроде LoadFromStream и SaveToStream (и иногда предоставляя к ним обёртки-переходники вроде LoadFromFile и SaveToFile ).

Примечание: поток данных (stream) не следует путать с потоком кода (thread), который также иногда называют нитью. Они не имеют между собой ничего общего, кроме слова «поток» в названии. Если какой-то текст говорит про «потоки», не уточняя кода или данных, то значение термина должно быть ясно из контекста. Эти понятия не пересекаются, так что здесь не должно быть никаких проблем с пониманием. Замечу, что подобная путаница возможна только в русском языке, где два разных понятия переводятся одинаково. В английском языке для них используются разные слова (stream и thread).

В Delphi все потоки данных реализованы как объекты (экземпляры) классов, наследуемых от TStream . TStream является абстрактным базовым классом, который поддерживает операции чтения, записи и позиционирования, но сам при этом не умеет делать ничего. Конкретная работа реализуется его классами-наследниками.

В Delphi есть широкий набор классов, предназначенный для работы с чем угодно: от файла на диске до блока памяти. Каждый такой класс-наследник реализует базовые методы TStream по-своему. К примеру, при чтении из потока данных: наследник для работы с файлами вызовет функцию чтения данных с диска, а наследник для работы с блоком памяти использует процедуру Move для копирования данных.

Вот (неполный) список классов-наследников TStream (примеры):

  • TFileStream (для работы с файлами)
  • TResourceStream (для работы с ресурсами программы)
  • TStringStream (для работы со строками)
  • TMemoryStream (для работы с буфером в памяти)
  • TBlobStream (для работы с BLOB полями)
  • TWinSocketStream (для работы с сетевым сокетом)

По сравнению с прошлой темой, где было всего три вполне конкретных файловых типа, это может быть немного непонятно: зачем нужен какой-то абстрактный класс и классы-наследники? Очень просто: пусть вы хотите уметь загружать растровые изображения (bitmap). Но ведь рисунок может лежать не только в файле, он может быть и в ресурсах программы и в памяти. Не писать же три разных метода, которые делают одно и то же? Вот поэтому и нужен абстрактный класс. Он объявляет общий контракт, которому обязуются следовать все его наследники. Поэтому вы можете спокойно написать (один раз) код, который грузит рисунок из TStream . А уж вызывающий подставит вам TFileStream для загрузки рисунка из файла, TResourceStream для загрузки из ресурса и TMemoryStream для загрузки из памяти. В определённом смысле все эти классы-наследники представляют собой простые переходники (от общей спецификации, определённой TStream , до конкретного метода доступа: файл, ресурс, память, сеть и так далее; и наоборот). В общем, полиморфизм в действии.

Здесь и ниже я буду говорить в основном про физические файлы и примеры приводить в расчёте на TFileStream — как наиболее типичный случай. Просто имейте в виду, что всё сказанное будет применимо и к другим потокам данных.

Кроме того, потоки обеспечивают поддержку для загрузки/сохранения компонентов и форм. Именно благодаря этому механизму работает загрузка .dfm файлов в run-time. Этот механизм работает автоматически. Впрочем, вы можете использовать его и в своих целях. Но на это мы посмотрим в другой раз, потому что он тесно связан с RTTI. Это будет темой одной из следующих статей.

Общие принципы работы с потоками данных

Чтение/запись
Класс TStream имеет два метода для чтения данных и два метода для записи данных. Работают они обычным образом: поток читает (или пишет) указанный блок данных и сдвигает текущую позицию на размер блока данных, так что следующая операция начинается там, где закончилась предыдущая. Ничего сложного.

Итак, для чтения класс TStream предлагает методы Read и ReadBuffer , а для записи — методы Write и WriteBuffer . Эти методы используются одинаково: первым параметром указывается буфер (это нетипизированный параметр), а вторым параметром — его размер в байтах, например: Разница между методами с «Buffer» и без него заключается в том, что методы с суффиксом «Buffer» гарантируют выполнение операции до конца. Если же это невозможно (к примеру, в файле 12 байт, а вы командуете прочитать 24 байта), то будет возбуждено исключение (см. также ниже раздел про обработку ошибок).

А вот методы без суффикса «Buffer» допускают частичное выполнение операции. Они никогда не возбуждают ошибку, а вместо этого возвращают, сколько реально байт было прочитано или записано. Иногда, это может быть и 0 байт. К примеру, если в файле 12 байт, а вы вызываете Read , указывая 24 байта, то метод Read прочитает 12 байт и вернёт вам число 12 (это метод-функция). Ещё пример: если у вас поток связан с сетевым сокетом и вы вызываете Read , но пока никаких данных ещё не пришло: метод завершится тут же, возвращая 0.

Замечу, что некоторые потоки данных могут быть только для чтения или только для записи. К примеру, однонаправленный pipe может не допускать чтения, а ресурсы, очевидно, не разрешают запись. В таких случаях попытка вызова запрещённого метода приведёт к ошибке.

Помимо общих методов чтения/записи, потоки поддерживают специализированные методы для реализации чтения/записи компонентов — но, как я уже сказал, это тема для следующего раза.

Разумеется, некоторые из наследников TStream могут вводить свои специальные методы для чтения/записи. Мы посмотрим на некоторые примеры ниже.

Кроме чтения и записи TStream имеет и некоторые другие методы.

Позиционирование
Во-первых, это методы позиционирования. Вы можете вызвать свойство Size , которое вернёт вам размер потока данных в байтах (кроме того, вы можете устанавливать свойство Size , чтобы менять размер потока, но это поддерживается далеко не всеми видами потоков данных). Свойство Position указывает текущую позицию в потоке данных, где 0 соответствует началу, а значение, равное Size , — концу. Вы можете читать свойство Position , чтобы узнать текущую позицию, и записывать значение в Position , чтобы изменить текущую позицию (позиционирование поддерживается не всеми видами потоков). К примеру: Кроме абсолютного позиционирования через свойство Position , потоки данных поддерживают относительное позиционирование в стиле файловых Seek-процедур: метод Seek . Этот метод имеет два параметра: позицию и точку отсчёта. Причём последнее может иметь значения soBeginning (отсчёт от начала потока, аналог абсолютного позиционирования), soCurrent (отсчёт от текущей позиции, относительное смещение) и soEnd (отсчёт от конца потока). Для soCurrent позиция может быть и положительным числом (смещение в сторону конца потока) и отрицательным (смещение в сторону начала), для soBeginning смещение может быть только положительным (или нулём), а для soEnd — только отрицательным (или нулём). Есть также устаревшие константы вида soFromBeginning , soFromCurrent , soFromEnd — не используйте их в новом коде. При этом метод Seek возвращает предыдущее значение текущей позиции (до позиционирования). К примеру:
Копирование
У TStream есть ещё один метод: CopyFrom . Этот метод копирует указанное количество данных (в байтах) из указанного массива. Копирование производится с текущей позиции. Метод работает аналогично методу записи WriteBuffer , сдвигая текущую позицию на указанное количество байт и возбуждая исключение при ошибках. Использование CopyFrom позволяет избежать создания буфера, чтения в него данных из исходного потока, запись буфера в выходной поток и удаления буфера — все эти действия выполняются автоматически внутри метода CopyFrom . К примеру, вот простейший пример копирования файлов (см. ниже описание TFileStream ): У CopyFrom есть специальный случай: если последний параметр (размер) равен 0, то CopyFrom скопирует весь поток целиком — начиная с начала потока (вне зависимости от текущей позиции) и до конца. Так что если число байт для записи у вас не фиксировано, а как-то вычисляется, то вставьте перед вызовом CopyFrom проверку на 0: иначе вы скопируете поток целиком вместо 0 байт.

Обработка ошибок

Здесь нет ничего сложного, потоки данных используют стандартную обработку исключений. Вы можете просто писать код, не производя проверок — он по умолчанию будет иметь обработку ошибок. Конечно же, вам нужно использовать try..finally для корректной обработки утечек ресурсов программы: Во втором примере try..finally не используется при работе с потоком, потому что он вынесен во внешний код (вызывающий).

Все исключения, возбуждаемые самим потоком, наследуются от EStreamError . Наиболее типичными случаями являются ошибки EFileStreamError , EFCreateError , EFOpenError , EReadError и EWriteError .

Так что код обработки исключений потоков данных (если он вам нужен) может выглядеть как-то так:
Всё вышеуказанное — это типичное поведение для работы с исключениями. Тут не должно быть ничего нового и нетипичного.

Так что единственное «ага!» в обработке ошибок — разница между Read и ReadBuffer и между Write и WriteBuffer . Запомните, что если вы вызываете методы без суффикса «Buffer», то вы должны либо явно проверять результат их вызова (прямо как с кодом на кодах ошибок), либо иметь в виду, что данные могут прочитаться/записаться не полностью.

Правила использования

Хотелось бы сказать о «правилах использования» — а, скорее, о наиболее типичных ошибках новичков при работе с потоками данных.

    (Почти) всегда используйте ReadBuffer и WriteBuffer . Используйте Read и Write только если вам не важно выполнение операции до конца (к примеру, вы не знаете, сколько данных нужно прочитать). Иными словами, ReadBuffer и WriteBuffer должны быть вашим вариантом по умолчанию, а не наоборот.
  • Если вы в своей процедуре принимаете или отправляете какие-то данные — используйте TStream . Не используйте для этого нетипизированные параметры, указатели или конкретные экземпляры TStream . Т.е. вместо: должно быть:
  • Частая ошибка новичков: они забывают про позиционирование. К примеру: Должно быть:
  • TStream является абстрактным классом. Это значит, что в вашем коде не должно быть строк вида TStream.Create . Вы всегда должны использовать конкретного наследника — например, TFileStream или TResourceStream . Если вам нужен «просто поток», то вы можете использовать TMemoryStream — это создаст поток в памяти программы. Если при этом вы хотите использовать большой объём данных, то вы можете использовать TFileStream для временного файла. См. также.

    Не забывайте, что потоки работают с большими данными, поэтому всё позиционирование осуществляется на базе Int64 (8 байт/64 бита), а не Integer (4 байта/32 бита). Поэтому если вы по недосмотру где-то используете для позиционирования выражение/переменную типа Integer , то этим вы автоматически ограничите свои данные 2 Гб. Но этот момент также зависит и от вида используемого потока. К примеру, в 32-битных приложениях TMemoryStream не может работать с памятью больше 4 Гб, а TResourceStream ограничен 2 Гб — потому что он работает с ресурсами в исполняемых файлах формата PE. А любой файл формата PE не может быть больше 2 Гб.

  • Ну и под конец упомяну ещё такой момент: поскольку базовые методы чтения/записи работают с нетипизированными параметрами, то тут возможны ошибки при использовании динамических типов и указателей. Я уже приводил подобный пример в предыдущей статье, но приведу его ещё раз и тут:
  • Практика

    Все примеры ниже приводятся с полной обработкой ошибок. Примеры используют файл в папке с программой. Это удобно для тестирования и экспериментов, но, как указано ранее, этого нужно избегать в реальных программах. После тестирования и перед использованием кода в релизе вы должны заменить папку программы на подпапку в Application Data.

    Как вы сейчас увидите, использование базовых методов потоков данных эквивалентно использованию нетипизированных файлов со всеми их преимуществами и недостатками. Впрочем, благодаря классам и наследованию, классы-наследники могут предоставлять другие методы и более удобный доступ для частных случаев — подробнее на это мы посмотрим ниже, а также в следующих статьях цикла. Также мы увидим как ООП предоставляет нам возможность использования некоторых интересных ходов для записи сложных динамических структур данных.

      Одно значение: Double

    Одно значение переменного размера: String Данный случай прост — размер данных определяется по размеру файла. Для примера я выбрал AnsiString , а не String по двум причинам: во-первых, String — это псевдоним либо на AnsiString , либо на UnicodeString (в зависимости от версии Delphi). Так что вам нужно использовать явные типы: AnsiString , WideString (или UnicodeString ), а не String — иначе файл, созданный в одном варианте программы, нельзя будет прочитать в другом варианте программы.

    Во-вторых, используя AnsiString , я показал, как вы можете загрузить в строку весь файл целиком, «как есть». Хотя, если подобный подход использовать в реальных программах, то уж лучше использовать array of Byte или хотя бы RawByteString — чтобы подчеркнуть двоичность данных.

    Набор однородных значений: array of Double И снова, благодаря фиксированности размеров элементов, мы можем установить размер массива ещё до чтения из файла. Обратите внимание, что мы могли бы свести этот пример к предыдущему, прочитав/записав весь массив за раз, вместо поэлементного копирования.

    Также обратите внимание, что не имеет значения, какой индекс используется внутри выражения у SizeOf . Более того, не требуется даже наличие (существование) этого элемента. Это потому, что мы не обращаемся к нему — мы только просим у компилятора его размер. Это, по сути, константа. Так что всё выражение вообще не вычисляется — оно просто заменяется числом. Это удобный трюк для написания подобного кода, потому что это удобнее, чем писать тип явно: SizeOf(Double) . Почему? А что, если мы изменим объявление типа с Double на Single ? И забудем обновить SizeOf ? Тогда это приведёт к порче памяти — т.к. писаться или читаться будет больше, чем реально есть байт в элементе. Это выглядит не очень страшно для массива из Double , но рассмотрите вариант, скажем, строки — изменение размера Char гораздо более вероятно. А вот если мы используем форму SizeOf как в примере, то такой проблемы не будет — размер изменится автоматически.

    Набор однородных значений переменного размера: array of String С записью набора динамических данных возникает проблема — как отличить один элемент от другого? Мы не можем более использовать переход на другую строку, как это было с текстовыми файлами. Тут есть несколько вариантов.

    Самый простой и очевидный — ввести разделитель данных. Т.е. элементы отделяются друг от друга специальным символом. В качестве такого чаще всего выступает нулевой символ (#0) — это аналог разделителя строк в текстовых файлах. Тогда чтение-запись сведётся к примеру два. Но я не стал показывать этот путь, т.к. он очевидно вводит ограничение на возможные данные: теперь данные не могут содержать в себе разделитель (каким бы вы его ни выбрали). Конечно, вы можете его экранировать, но гораздо проще будет выбор другого подхода.

    И я его показал — это явная запись размера данных до записи самих данных. Т.е. мы пишем два значения для каждого элемента: длину и сами данные.

    Кроме того, в этом же примере показано, как можно сделать так, чтобы внутри программы работать с хорошо знакомым String , а в файле хранить фиксированный тип ( AnsiString / RawByteString или WideString / UnicodeString ). Вообще говоря, даже если вы работаете на Delphi 7 или любой другой версии Delphi до 2007 включительно — я бы рекомендовал всегда писать Unicode-данные в формате WideString во внешние хранилища.

    Обратите внимание, что в качестве счётчика длины используется LongInt , а не Integer — по причинам, указанным выше для типизированных файлов: String , Extended , Integer и Cardinal могут менять свои размеры в зависимости от окружения — поэтому мы используем другие типы, которые гарантированно всегда имеют один и тот же размер.

    Ещё в этом примере показан вариант пример использования метода Read : идея в том, что если будет достигнут конец потока, то вызов метода Read вернёт 0. Т.е. это аналог функции EoF. Альтернативным решением является код Вы также можете создать вспомогательную функцию И тогда этот пример будет эквивалентен примеру для нетипизированных файлов Pascal. Я буду использовать подпрограмму EoS в примерах ниже.

    Запись — набор неоднородных данных: В отличие от типизированных файлов, для нетипизированных файлов нет никаких проблем с записью неоднородных данных — вы просто пишете одно за другим. Данные фиксированного размера самодокументируются, а для динамических данных вы пишете сначала их размер, а затем сами данные. При чтении повторяете всё это в обратном порядке.

    Кстати, я бы вынес запись динамических данных в отдельные служебные подпрограммы: Тогда чтение-запись свелись бы к: Выглядит существенно проще и красивее, не так ли? Иллюстрация силы выделения кода в подпрограммы.

    Вообще, конечно же, более правильный код получится при использовании шаблона «декоратор». Суть заключается в создании класса, который реализует методы вида WriteBufferDyn / ReadBufferDyn , выполняя их над потоком, который ему указали. Например: В этом примере реализовано 3 вещи: во-первых, мы реализовали хранение ссылки на поток данных, над которым будем выполнять операции. Во-вторых, мы реализовали несколько вспомогательных низкоуровневых операций в секции private . В-третьих, мы реализовали высокоуровневые операции Read и Write в секции public . Тогда сохранение и загрузка сводятся к такому простому коду: Ну не красота-ли?

    Имейте в виду, что именно этот подход вам нужно применять в реальных программах. В примерах ниже я буду применять процедуры вроде WriteBufferDyn / ReadBufferDyn исключительно по соображениям «меньше писать».

    Набор (массив) из записей — иерархический набор данных: Для начала хочу сразу же заметить, что странное выражение для поля Salary сделано для обхода бага Delphi. Вообще, там должно стоять просто F.WriteBuffer(Values[Index].Salary, SizeOf(Values[Index].Salary)) , но в настоящий момент это выражение даёт ошибку «Variable required», поэтому используется обходной путь: мы берём указатель и разыменовываем его. Вообще говоря, это NOP-операция. А смысл её заключается в потере информации о типе. Это достаточно частый трюк, когда мы хотим запустить свои шаловливые руки под капот языка, минуя информацию типа, но в данном случае он используется для более благих целей: обхода бага компилятора. Вы можете использовать F.WriteBuffer(Values[Index].Salary, SizeOf(Values[Index].Salary)) , если ваша версия компилятора это позволяет, или просто выбрать другой тип данных (не Currency ).

    В любом случае, надо заметить, что достаточно часто при записи/чтении массива записей новички пытаются сделать такую вещь, как запись элемента целиком ( F.WriteBuffer(Values[Index], SizeOf(Values[Index])) ). Это будет работать для записей фиксированного размера, не содержащих динамические данные (указатели). Ровно как это работает для типизированных файлов. Но если в записях у вас встречаются строки, динамические массивы и другие данные-указатели, то этот подход не будет работать. Собственно, если вы используете типизированные файлы, то компилятор даже не даст вам объявить такой тип данных ( file of String , например, или file of Запись , где Запись содержит String ). Но суть потоков данных — в прямом доступе, минуя информацию типа. Так что по рукам за это вам никто не даст. Вместо этого код будет просто вылетать или давать неверные результаты. А проблема тут в том, что для динамических данных, поле — это просто указатель. Записывая элемент «как есть» вы запишете в файл значение указателя, но не данные, на которые он указывает. Запись в файл произойдёт нормально, но в файле вы не найдёте своих строк. Чтение из файла тоже пройдёт отлично. Но как только вы попробуете обратиться к прочитанной строке — код вылетит с access violation, потому что указатель строки указывает в космос, на мусор.

    Аналогично предыдущим обсуждениям, самый простой способ решения проблемы (но не всегда достаточный) — замена String в записях на ShortString или статический массив символов. Я не буду рассматривать этот вариант, т.к. он сводится к предыдущим примерам с записью данных фиксированного размера.

    Вместо этого в примере я показал уже известную технику: запись длины строки вместе с её данными. Это избавляет вас от всех недостатков ShortString /массива символов, но даёт новый недостаток: теперь вы не можете сохранить данные одной строчкой, вам нужно писать их поле-за-полем.

    Также по аналогии с предыдущим примером я покажу, как будет выглядеть код, если вы введёте класс-декоратор. Само описание класса я опущу для краткости — оно аналогично (но не тождественно) предыдущему примеру: Мы также могли внести методы Read и Write , работающие с массивом из TPerson в класс-декоратор.

    Массив из записей внутри записи — составные данные: Как видите — здесь нет никаких проблем, вы просто соединяете воедино техники из предыдущих примеров. Мы используем технику с записью счётчика длины для динамических данных в двух местах: при записи строк и при записи массивов (поле Related ).

    Кроме того, хотя я мог бы написать весь код в цикле, друг за другом, я всё же выделил новые подпрограммы — исключительно ради удобства. Код теперь выглядит компактно и аккуратно. Он прозрачен и его легко проверить. А если бы я внёс код из подпрограмм в главные циклы, то получилась бы слабочитаемая мешанина кода.

    Заметьте, что вы всё ещё должны писать записи по отдельным полям. И если вы меняете объявление записи — вам лучше бы не забыть поменять код, сериализующий и десериализующий запись.

    И снова: не забывайте, что это только пример. В реальных программах вам следует посмотреть в сторону класса-декоратора. Я не буду приводить тут пример: он делается по аналогии с предыдущими случаями. Просто добавьте новые методы чтения/записи, вот и всё.

    Особенности

    Посмотрим теперь на различные конкретные наследники класса TStream и то, что они нам предлагают.

    Примечание: не все из нижеперечисленных возможностей существуют во всех версиях Delphi. Если у вас старая версия Delphi, то у вас могут отсутствовать некоторые описываемые классы, свойства или методы. В этом случае вам придётся обходится без них или писать аналоги самостоятельно.

    THandleStream

    THandleStream предназначен для работы с файлами в смысле операционной системы.

    Короче говоря, THandleStream является оболочкой к файлам в стиле ОС. Объект этого типа можно связать с THandle , полученный любым способом — скажем, от CreateFile , CreatePipe и так далее: лишь бы на этот описатель можно было вызывать ReadFile и WriteFile .

    Используйте THandleStream в двух случаях:

    1. Функция ОС вернула вам описатель THandle , а код, который вы хотите вызвать, требует TStream . Тогда просто создайте THandleStream , передав в его конструктор описатель от функции ОС, и передайте полученный объект коду. Например:
    2. Вы хотите использовать возможность, которую предоставляет функция ОС, но не объект Delphi. См. первый пример в следующем пункте.

    Сам THandleStream не имеет ограничений и поддерживает все операции: чтение, запись, позиционирование и изменение размера. Однако нижележащий дескриптор объекта ядра может поддерживать не все операции. К примеру, описатель от файла на диске может быть открыт только для чтения, а описатель pipe не поддерживает позиционирование.

    Описатель, которым инициализирован объект, доступен через свойство Handle .

    Обратите внимание, что сам THandleStream никогда не закрывает описатель (ему нельзя передать ответственность за него). Поэтому вы должны закрывать описатель вручную или использовать TFileStream (см. ниже).

    TFileStream

    TFileStream предназначен для работы с файлами на диске.

    TFileStream наследуется от THandleStream , так что он получает все возможности своего предка. TFileStream не имеет ограничений на операции и поддерживает конструктор с передачей описателя. Например: В отличие от THandleStream , TFileStream закрывает открытый описатель файла, вне зависимости от способа его инициализации.

    Кроме того, TFileStream поддерживает перегруженный конструктор, позволяющий открывать файлы. У него есть два параметра: (имя файла) и (режим открытия + режим разделения).

    Допустимые режимы открытия:

    1. fmCreate — создаёт новый файл. Если такой файл уже есть, он удаляется перед созданием. Файл открывается в режиме записи.
    2. fmOpenRead — открывает файл только для чтения.
    3. fmOpenWrite — открывает файл только для записи.
    4. fmOpenReadWrite — открывает файл для чтения-записи.

    Режимы разделения:

    1. fmShareExclusive — запретить совместное использование.
    2. fmShareDenyWrite — запретить другим приложениям запись.
    3. fmShareDenyRead — запретить другим приложениям чтение.
    4. fmShareDenyNone — не вводить ограничения.

    Режимы следует соединять друг с другом операцией or .

    Примечание: у TFileStream есть вариант конструктора с дополнительным параметром, но этот (третий) параметр существует только для обратной совместимости и сейчас игнорируется. Не следует пытаться передавать в него режим разделения файла.

    Типичный пример открытия файла выглядит так: А создания файла — так: При работе с файлами типа логов (для которых необходимы совместное использование или мониторинг) могут использоваться такие вызовы:
    Далеко не все возможности функций открытия файлов ОС доступны через конструктор TFileStream , но зато он является универсальным для любых платформ. Предпочтительно использовать именно его для доступа к файлам вместо функций ОС. Если же вам нужны какие-либо возможности, недоступные через конструктор TFileStream , то используйте TFileStream , инициализировав его описателем файла от системной функции открытия файлов, как указано в примерах выше.

    Если объект TFileStream создавался через конструктор с именем файла, то это имя файла доступно в свойстве FileName , иначе доступно только свойство Handle .

    TMemoryStream

    TMemoryStream реализует потоковую обёртку к данным в памяти программы. Т.е. к буферу в динамической памяти осуществляется последовательный доступ.

    Используйте TMemoryStream , если вам нужен «просто поток» или вам нужен промежуточный буфер-поток.

    TMemoryStream имеет конструктор без параметров, который создаёт пустой объект. Для заполнения потока после создания можно использовать обычные методы записи или же специальные методы LoadFromStream (аналог вызова CopyFrom(Stream, 0) ) и LoadFromFile .

    TMemoryStream не имеет ограничений и поддерживает все операции, включая изменение размера.

    TMemoryStream хранит данные в динамической куче процесса, выделяя память по мере необходимости. Он сам автоматически управляет памятью. Реально памяти может быть выделено больше, чем лежит данных в потоке — т.н. «capacity > size». Это стандартная оптимизация для «побайтовых записей».

    Дополнительной возможностью TMemoryStream является предоставление свойства Memory , позволяющего обратиться к данным потока напрямую, через указатель, минуя последовательные методы чтения/записи. По этой причине вы можете рассматривать TMemoryStream как «переходник» между TStream и нетипизированным указателем.

    Простейший пример использования TMemoryStream (в данном случае — для конвертации строки в TStream ): Примечание: в данном примере более эффективной была бы другая конструкция. Данный пример по сути копирует данные строки в поток. Но поскольку реально нам нужно здесь только чтение, то можно поступить по другому: создать поток, который будет работать прямо поверх данных строки, без их копирования. Это будет настоящий адаптер. Мы посмотрим на такой пример ниже.

    TResourceStream

    TResourceStream предназначен для организации последовательного доступа к ресурсам в исполняемых файлах.

    TResourceStream похож на TMemoryStream . Он тоже работает с памятью программы (только не с кучей, а с ресурсами), он поддерживает методы SaveToStream и SaveToFile , а также свойство Memory .

    Но в отличие от TMemoryStream , TResourceStream поддерживает только методы чтения, но не записи, а также не поддерживает изменение размера. Иными словами, TResourceStream — это read-only.

    Собственно для инициализации у TResourceStream есть два варианта конструктора, которые имеют по три параметра: описатель модуля, имя ресурса и тип ресурса. А разница между ними заключается в способе указания имени ресурса (второго параметра): по ID или по имени.

    Ну и несколько примеров:

    TBytesStream

    TBytesStream хранит данные потока в массиве байтов.

    Используйте TBytesStream как переходник между TBytes и TStream .

    Собственно, TBytesStream аналогичен TMemoryStream , только вместо блока памяти в куче он использует TBytes в качестве хранилища для данных. Он также не имеет ограничений на операции, поддерживая чтение, запись, позиционирование и изменение размера. Он поддерживает методы LoadFromStream , LoadFromFile , SaveToStream и SaveToFile , а также свойство Memory .

    В отличие от TMemoryStream , у TBytesStream есть конструктор, который принимает переменную типа TBytes — это будут начальные данные потока. При этом не производится копирование данных (используется счётчик ссылок динамического массива). Все операции чтения-записи будут оперировать с исходными данными в оригинальной переменной типа TBytes . Однако если вы измените размер потока (либо явно через Size / SetSize , либо неявно через запись данных в конец потока данных), то поток сделает копию данных и будет работать уже с ней. При этом все будущие изменения в потоке не затронут оригинальной переменной типа TBytes .

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

    Дополнительно TBytesStream вводит свойство Bytes оно работает аналогично свойству Memory , только имеет тип TBytes , а не Pointer . Предупреждение: не пытайтесь использовать Length для определения размера данных. Размер хранилища может быть больше актуального размера («Capacity > Size»). Используйте свойство Size для определения размера данных.

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

    TStringStream

    TStringStream предоставляет последовательный доступ к информации, хранящейся в обычной строке.

    Используйте TStringStream для хранения данных в строках. Использование TStringStream даст вам в руки мощные возможности TStream . TStringStream удобен как промежуточный объект, который умеет хранить данные в строке, а также читать и писать их.

    По сути, TStringStream является обёрткой к TBytesStream , которая просто конвертирует строку в байты и обратно. У TStringStream есть несколько вариантов конструкторов, которые инициализируют поток по разным типам строк. В Unicode версиях Delphi конструкторы также позволяют вам указывать кодировку для ANSI строк.

    Методы чтения-записи TStringStream не затрагивают исходную строку, а всегда работают с копией данных (внутреннее хранилище в виде TBytes ).

    Ну и, конечно же, TStringStream предоставляет строко-ориентированные свойства и методы. Во-первых, это методы WriteString и ReadString, которые пишут и читают данные из потока в виде строки. При этом кодировка (в Unicode-ных версиях Delphi) контролируется свойством Encoding . И, равно как и предыдущие классы, TStringStream выставляет наружу хранилище в «родном» формате: DataString .

    Простой пример использования TStringStream как переходника между строками и TStream :
    Заметьте, что несмотря на наличие методов чтения строк и загрузки/сохранения данных из/в файлы, TStringStream не пригоден для работы с текстовыми файлами. Он не работает с BOM и не позволяет прочитать одну строку (в смысле line) от разделителя до разделителя (он читает только указанное количество символов). По этой причине для работы с текстовыми файлами используют вспомогательный класс — TStringList . Этому классу будет посвящена следующая статья (где и будут показаны методы работы с текстом), а здесь же я только приведу примеры шифрования/расшифровки текстового файла, использующие оба этих класса: Конечно, на практике такой пример не имеет большого смысла, потому что гораздо проще просто работать с текстовым файлом как с двоичным — обработав его через TFileStream . Но более удачного и простого примера мне сейчас в голову не приходит, а код выше прекрасно показывает пример соединения трёх классов для работы.

    TStreamAdapter

    Понятие «потока данных» есть не только в Delphi, но и практически в любом другом современном языке. Разумеется, другие языки понятия не имеют, как работать с объектами Delphi, и наоборот: Delphi не знает, как устроены классы и объекты в других языках. К счастью, под Windows у нас есть COM и интерфейсы. С ними умеют работать почти все языки, так что это является де-факто стандартом межязыкового взаимодействия. И, конечно же, не могло быть иначе: для такой популярной концепции как «поток данных» существует свой интерфейс — IStream .

    Иными словами, если вам нужно передать куда-то поток данных — вы используете IStream . Если вам кто-то передаёт поток данных, то это будет IStream .

    Тут возникает маленькая проблемка: ваши Delphi объекты вообще-то не умеют работать с интерфейсом IStream : они работают с классом TStream . Что же делать?

    Для этого в Delphi есть два класса-адаптера, которые конвертируют TStream в IStream и наоборот. При этом они являются тонкими оболочками, которые просто перенаправляют вызовы. Они конвертируют интерфейс, копирования данных потока не происходит: просто вызовы, скажем, класса конвертируются в вызовы интерфейса (и наоборот), работая с данными оригинального потока данных напрямую.

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

    TStreamAdapter поддерживает те же операции, что и оригинальный поток, который в него завёрнут.

    TStreamAdapter может взять на себе ответственность за удаление исходного потока данных, а может оставить её вам. Вот два примера использования TStreamAdapter , иллюстрирующие оба подхода:

    TOleStream

    TOleStream представляет собой обратный класс к TStreamAdapter : переходник от IStream к TStream .

    Используется он аналогично. Единственная разница — нет вопроса владения, поскольку интерфейсы относятся к автоуправляемым типам.

    TOleStream поддерживает те же операции, что и оригинальный поток, который в него завёрнут.

    Пример использования TOleStream :

    Прочие потоки

    Это далеко не все стандартные потоки, реализованные в Delphi. К примеру, есть ещё TWinSocketStream , TBLOBStream , TZCompressionStream и многие-многие другие. Многие из них являются переходниками, но много классов также предоставляют свою собственную функциональность.

    Создание своих классов-потоков

    Как бы много в Delphi ни было классов-наследников, всегда найдётся случай, когда вас не устраивают стандартные классы. В этом случае вам нужно реализовывать свой класс-наследник.

    К примеру, помните пример для TMemoryStream ? Там мы копировали данные строки в поток. Давайте сейчас напишем класс, который позволял бы работать с блоком памяти напрямую, без копирования. Разумеется, такой класс не может поддерживать операцию изменения размера, но чтение, запись и позиционирование — вполне.

    Это несложно, если использовать в качестве базового класса TCustomMemoryStream — он уже умеет читать данные из блока памяти, поддерживает позиционирование, но не умеет писать и никак не управляет нижележащим хранилищем. Тогда мы получаем: Вот список методов, которые вы можете захотеть переопределить в своих классах-наследниках:

    • (protected) function GetSize: Int64; и procedure SetSize(const NewSize: Int64); — для управление размером хранилища. Если вы не зададите свой GetSize , то реализация по умолчанию будет использовать Seek для поиска конца потока и определения размера (равному текущей позиции в конце потока). SetSize по умолчанию просто ничего не делает.
    • (public) function Read(var Buffer; Count: Longint): Longint; и function Write(const Buffer; Count: Longint): Longint; для чтения и записи данных. По умолчанию оба метода абстрактные. В любом наследнике вы должны их замещать.
    • (public) function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; для перемещения по потоку. Реализация по умолчанию вызывает исключение. Вы должны заместить этот метод, если ваши данные поддерживают позиционирование. Обычно это так. Исключение составляют случаи вроде сетевых сокетов.

    Вот и всё. Всего три категории и всего пять методов. Реализуйте их — и у вас будет готовый поток. Все прочие методы и свойства являются переходниками к вышеуказанным. К примеру, метод GetPosition (get-акцессор для свойства Position ) реализован как Result := Seek(0, soCurrent);

    В общем, как видите, создать свой класс-поток — это очень просто.

    Преимущества и недостатки потоков данных

    Плюсы:

    • Де-факто стандарт языка Delphi
    • Являются основой для других (более высокоуровневых) механизмов
    • Имеют готовые оболочки для самых типичных случаев («не надо писать самому» — в отличие от файлов в стиле Pascal)
    • Гибкость
    • Стандартная обработка ошибок на исключениях
    • Поддержка произвольных файлов (нет ограничения на размер)
    • Нет проблем с многопоточностью*
    • Межъязыковая совместимость (через IStream )
    • Поддержка Unicode и кодировок при работе с текстовыми данными (но не текстовыми файлами — нет поддержки BOM)
    • Легко расширяются написанием классов-наследников

    Минусы:

    • Необходимость ручной сериализации данных
    • Ориентированы на побайтовую обработку, слабо пригодны для работы с текстовыми файлами
    • Часто неопытные программисты используют Read и Write вместо ReadBuffer и WriteBuffer , не делая проверку результатов. Часто это приводит к некорректному коду без обработки ошибок
    • Круче кривая обучения: сам TStream не умеет делать ничего. Значит, чтобы делать в программах что-то полезное, нужно изучать многочисленные наследники TStream , чтобы знать кто что умеет и когда что кого нужно применять. К примеру, если вам нужно отправить растр по сети, то вы должны сообразить, что вы можете создать THandleStream для описателя сетевого сокета и использовать его в сочетании с методом SaveToStream объекта растра. Сравните это с файлами в стиле Pascal, где было всего три файловых типа, покрывавших все случаи использования

    Вывод: если вы работаете в Delphi и хотите работать с любыми данными, то потоки данных должны быть вашим первым вариантом. Используйте что-то другое, только если это «другое» больше подходит для вашей задачи (к примеру, динамический массив для типизированных данных; также некоторые люди могут рассматривать файлы в стиле Pascal более подходящими для работы с текстовыми данными).

    (*) — некоторые люди неверно читают это предложение. Обратите внимание, что речь идёт о достоинствах и недостатках по сравнению с другими способами работы с файлами. Например, файлами Паскаля. Поэтому, в этом предложении утверждается, что у вас не будет проблем если вы работаете с двумя разными потоками данных (двумя объектами) в двух разных потоках кода, а вовсе не (как читают это некоторые) то, что вы можете обращаться к одному объекту из разных потоков.

    Источник

    Adblock
    detector

    kristall000

    0 / 0 / 0

    Регистрация: 11.04.2015

    Сообщений: 45

    1

    09.06.2015, 22:02. Показов 4329. Ответов 0

    Метки нет (Все метки)


    В общем, я делаю что то типа входа в систему с паролем. В старте программы прописан код который разворачивает окно программы на весь экран и прогружает обои компьютера на задний фон. В Windows XP путь к обоем «C:Documents and SettingsИльяLocal SettingsApplication DataMicrosoftWallpaper1.bmp» и в конце всегда .bmp даже если мы изначально ставили на обои картинку .jpg формата или даже другого. В этом и проблема: если изначально поставили .jpg то все ок а если .bmp то выходит ошибка ‘Stream read error‘ сразу после ‘bmp.LoadFromFile‘ на 46 строчке кода. Может быть XP плохо конвертирует .bmp в .bmp и нужно программно это сделать?

    Ниже только часть из кода

    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    procedure RelPic;
    var
      bmp: TBitmap;
      jpg:TJPEGImage;
      FileStm: TFileStream;
      pCard: PDWORD;
      TypeName: String;
      reg: TRegistry;
    begin
    bmp := TBitmap.Create;
    reg:=TRegistry.Create;
    reg.LazyWrite:=false;
    reg.RootKey:=HKEY_CURRENT_USER;
    reg.OpenKey('Control PanelDesktop', True);
     
    FileStm := TFileStream.Create(reg.ReadString('Wallpaper'), fmOpenRead or fmShareDenyWrite);
    GetMem(pCard, SizeOf(DWORD));
    if FileStm.Size < 32 then
      begin
      TypeName := 'unknown';
      Exit;
      end;
    FileStm.ReadBuffer(pCard^, SizeOf(DWORD));
    if PWORD(pCard)^  = $4D42 then // bitmap
      TypeName := 'bmp'
      else
      if (pCard^ = $E0FFD8FF) or (pCard^ = $E1FFD8FF) then  // JPEG
      TypeName := 'jpg'
      else
      TypeName := 'unknown';
    FreeMem(pCard);
    FreeAndNil(FileStm);
     
    if TypeName = 'jpg' then begin //этот способ для windows 7,8..... т.к. формат обоев .jpg
        jpg:=TJPEGImage.Create;
        jpg.CompressionQuality:=100;
        jpg.Compress;
        jpg.LoadFromFile(reg.ReadString('Wallpaper'));
        bmp.Assign(jpg);
        FreeAndNil(jpg);
    end;
    if TypeName = 'bmp' then begin //мой случай с Windows XP
        bmp.LoadFromFile(reg.ReadString('Wallpaper'));
    end;
     
        bmp.PixelFormat := pf24Bit; // <<<----- Stream read error
      ScaleBitmap(bmp,screen.Width,screen.Height);
      Form1.Canvas.Draw(0, 0, bmp);
        bmp.SaveToFile(GetWin('%appdata%')+'Yosemite ScreenLockerWallpaper.jpg');
      GBlur(bmp, ini.ReadInteger('Settings', 'BlurValue', 30));
      Form1.Canvas.Draw(bmp.Width, 0, bmp);
        bmp.SaveToFile(GetWin('%appdata%')+'Yosemite ScreenLockerWallpaper_Blur.jpg');
    end;

    __________________
    Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



    0



      msm.ru

      Нравится ресурс?

      Помоги проекту!

      Пожалуйста, выделяйте текст программы тегом [сode=pas] … [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.


      Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
      1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
      2. Как «свернуть» программу в трей.
      3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
      4. Как прочитать список файлов, поддиректорий в директории?
      5. Как запустить программу/файл?
      … (продолжение следует) …


      Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
      Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


      Внимание
      Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
      Повторная попытка — 60 дней. Последующие попытки бан.
      Мат в разделе — бан на три месяца…

      >
      «Stream read error»
      , От чего бы это?

      • Подписаться на тему
      • Сообщить другу
      • Скачать/распечатать тему



      Сообщ.
      #1

      ,
      12.08.09, 11:38

        Привет всем!
        Пишу программу для поиска дублей на диске.
        В ф-ции, приведенной ниже, вылетет ошибка на вызове другой ф-ции.
        Ошибка «Stream read error», причем вылетает не срузу, первый раз где-то на 10 вызове(примерно), во второй на 36-ом, а в 3-й — на 24-ом.
        От чего это может быть и как такое лечится?
        Заренее благодарю.

        ExpandedWrap disabled

          type

          TFileRecord=record

          FileName:string[255];

          Size:integer

          end;

          TFileRecordHash=record

          FileName:string[255];

          Size:integer;

          Hash:string[16];

          end;

          var

          FileTable:array of TFileRecord;

          FileTableHash:array of TFileRecordHash;  //пока не используется

          Duplicates:array of array of TFileRecord;

          RemoveList:array of char;

          Canceled:boolean;

          ……………………

          procedure FindDuplicates;

          var

          i,j:integer;

          ItemClosed:boolean;

          //////////////////////////////////

          ////Побйтно сравнивает два файла//

          //////////////////////////////////

          function CompFiles(Name1,Name2:string):boolean;

          var

          i:integer;

          Buf1,Buf2:byte;

          f1,f2:TFileStream;

          begin

          try

          f1:=TFileStream.Create(Name1,fmOpenRead);

          f2:=TFileStream.Create(Name2,fmOpenRead);

          for i:=0 to f1.Size do

            begin

              f1.ReadBuffer(Buf1,1);

              f2.ReadBuffer(Buf2,1);

              if Buf1<>Buf2 then

                begin

                  result:=false;

                  exit

                end

            end;

          result:=true;

          finally

          f1.Free;

          f2.Free;

          end

          end;

          begin

          try

          for i:=0 to Length(FileTable)-1 do

            if Length(FileTable[i].FileName)<>0 then

              begin

                ItemClosed:=true;

                for j:=0 to Length(FileTable)-1 do

                  if Length(FileTable[j].FileName)<>0 then

                    if i<>j then

                      if FileTable[i].Size=FileTable[j].Size then

                        if CompFiles(FileTable[i].FileName,FileTable[j].FileName) then  //Здесь ошибка

                          begin

                            if ItemClosed then

                              begin

                                SetLength(Duplicates,Length(Duplicates)+1);

                                SetLength(Duplicates[Length(Duplicates)-1],{Length(Duplicates[Length(Duplicates)-1])|+}1);

                                Duplicates[Length(Duplicates)-1,0]:=FileTable[i];

                                ItemClosed:=false

                              end;

                            SetLength(Duplicates[Length(Duplicates)-1],Length(Duplicates[Length(Duplicates)-1])+1);

                            Duplicates[Length(Duplicates)-1,Length(Duplicates[Length(Duplicates)-1])-1]:=FileTable[j];

                            SetLength(FileTable[j].FileName,0)

                          end;

                SetLength(FileTable[i].FileName,0)

              end;

          finally

          SetLength(FileTable,0)

          end

          end;

        Сообщение отредактировано: Alexander N — 12.08.09, 18:43


        MetalFan



        Сообщ.
        #2

        ,
        12.08.09, 11:43

          Full Member

          ***

          Рейтинг (т): 14

          возможно не совпадают размеры файлов. и если уж в цикле от 0, то, имхо до Size-1.
          так же возможно имеет смысл использовать не ReadBuffer, а Read, и проверять, что он возвращает корректно прочитанный размер. либо ловить и обрабатывать исключение с пом try..except


          Profi



          Сообщ.
          #3

          ,
          12.08.09, 12:00

            Цитата Alexander N @ 12.08.09, 11:38

            ExpandedWrap disabled

              for i:=0 to f1.Size do

                begin

                  f1.ReadBuffer(Buf1,1);

                  f2.ReadBuffer(Buf2,1);

                  if Buf1<>Buf2 then

                    begin

                      result:=false;

                      exit

                    end

                end;

            ExpandedWrap disabled

              if f1.Size = f2.Size then

              begin

              for i:=0 to f1.Size — 1 do

                begin

                  f1.ReadBuffer(Buf1,1);

                  f2.ReadBuffer(Buf2,1);

                  if Buf1<>Buf2 then

                  begin

                    result:=false;

                    exit

                  end

                end;

              end else

                result := false;


            leo



            Сообщ.
            #4

            ,
            12.08.09, 12:01

              Мда-а-а, читать файлы по 1 байту это супер :wacko:

              ExpandedWrap disabled

                function CompFiles(const Name1,Name2:string):boolean;

                var

                  FSize,BSize:integer;

                  Buf1,Buf2:array[0..4095] of char;

                  f1,f2:TFileStream;

                begin

                  Result:=false;

                  f1:=TFileStream.Create(Name1,fmOpenRead);

                  f2:=nil;

                  try

                    f2:=TFileStream.Create(Name2,fmOpenRead);

                    FSize:=f1.Size;

                    if f2.Size <> FSize then Exit; //!!! проверяем равенство размера

                    repeat

                      BSize:=FSize;

                      if BSize > SizeOf(Buf1) then BSize:=SizeOf(Buf1);

                      f1.ReadBuffer(Buf1,BSize);

                      f2.ReadBuffer(Buf2,BSize);

                      if not CompareMem(Buf1,Buf2,BSize) then Exit;

                      dec(FSize,BSize);

                    until FSize <= 0;

                    Result:=true;

                  finally

                    f1.Free;

                    f2.Free;

                  end;

                end;

              Добавлено 12.08.09, 12:09
              Да и циклы по FileTable ужасно сделаны — нафига гонять второй цикл от 0, когда достаточно от i+1 ?! И куча вложенных if-ов никчему, т.к. можно все в одну строчку написать

              ExpandedWrap disabled

                for i:=0 to Length(FileTable)-1 do

                  if FileTable[i].FileName <> » then

                  begin

                    ItemClosed:=true;

                    for j:=i+1 to Length(FileTable)-1 do

                      if (FileTable[j].FileName <> ») and (FileTable[i].Size = FileTable[j].Size) and

                         CompFiles(FileTable[i].FileName,FileTable[j].FileName) then

                      begin

                         …

                      end;

                  end;

              Сообщение отредактировано: leo — 12.08.09, 12:11


              Alexander N



              Сообщ.
              #5

              ,
              12.08.09, 12:11

                Цитата leo @ 12.08.09, 12:01

                RSize:=0;

                Вроде в объявлении переменных таковой нет.leo, что вы имели в виду?


                leo



                Сообщ.
                #6

                ,
                12.08.09, 12:13

                  Цитата Alexander N @ 12.08.09, 12:11

                  Вроде в объявлении переменных таковой нет.leo, что вы имели в виду?

                  Эх, на 3 сек. не успел исправить — это не нужно, от другого варианта затесалось…


                  Alexander N



                  Сообщ.
                  #7

                  ,
                  12.08.09, 12:16

                    Цитата leo @ 12.08.09, 12:01

                    FSize:=f1.Size;
                    if f2.Size <> FSize then Exit; //!!! проверяем равенство размера

                    А разве нельзя

                    ExpandedWrap disabled

                      if f1.Size<>f2.Size

                    ?

                    И вложенные условия нужны для того, чтобы если первое услови не выполняется, не вычислять последующие, т. к. прога должна работать быстро.

                    Сообщение отредактировано: Alexander N — 12.08.09, 12:20


                    leo



                    Сообщ.
                    #8

                    ,
                    12.08.09, 12:22

                      Цитата Alexander N @ 12.08.09, 12:16

                      А разве нельзя
                      if f1.Size<>f2.Size

                      Можно, только это «не оптимально» ;), т.к. при при чтении св-ва f1.Size вызывается ф-я АПИ GetFileSize — меня от этих излишеств «коробит» :)

                      Добавлено 12.08.09, 12:26

                      Цитата Alexander N @ 12.08.09, 12:16

                      И вложенные условия нужны для того, чтобы если первое услови не выполняется, не вычислять последующие, т. к. прога должна работать быстро

                      В дельфи и так по умолчанию стоит опция сокращенного вычисления булевских выражений (галка Compleate boolean eval выключена), поэтому как только в цепочке AND встречается false — дальнейшие вычисления прекращаются


                      Alexander N



                      Сообщ.
                      #9

                      ,
                      12.08.09, 12:46

                        Цитата leo @ 12.08.09, 12:22

                        галка Compleate boolean eval выключена

                        Оффтопик, но где эту галку искать. например в d7?

                        Добавлено 12.08.09, 13:06
                        MetalFan, спасибо, с Вашим исправлением прогнал прогу по диску, она больше не слетает(пока ;) ).

                        Цитата Profi @ 12.08.09, 12:00

                        if f1.Size = f2.Size then
                        begin
                        for i:=0 to f1.Size — 1 do
                        begin
                        f1.ReadBuffer(Buf1,1);
                        f2.ReadBuffer(Buf2,1);
                        if Buf1<>Buf2 then
                        begin
                        result:=false;
                        exit
                        end
                        end;
                        end else
                        result := false;

                        Profi, зчем сравнивать файлы по размеру, если это уже делается до вызова ф-ции, и совпаадение размеров — условие вызова это ф-ции?
                        leo, спасибо за ф-цию сравнения, пока не успел попробовать


                        leo



                        Сообщ.
                        #10

                        ,
                        12.08.09, 13:14

                          Цитата Alexander N @ 12.08.09, 12:46

                          Оффтопик, но где эту галку искать. например в d7?

                          Project -> Options -> Compiler -> SyntaxOptions -> Complete boolean eval — по умолчанию выключена, т.е. булевские выражения считаются не полностью, а слево-направо пока не станет ясен результат


                          Alexander N



                          Сообщ.
                          #11

                          ,
                          12.08.09, 13:16

                            Спасибо!


                            Profi



                            Сообщ.
                            #12

                            ,
                            12.08.09, 16:20

                              Alexander N, просто не смотрел реализацию второй функции.

                              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

                              0 пользователей:

                              • Предыдущая тема
                              • Delphi: Общие вопросы
                              • Следующая тема

                              Рейтинг@Mail.ru

                              [ Script execution time: 0,0387 ]   [ 16 queries used ]   [ Generated: 10.02.23, 03:20 GMT ]  

                              IPB

                              > ВНИМАНИЕ!

                              > Stream read error, возникает ошибка

                              сообщение 25.11.2011 19:34

                              Сообщение
                              #1


                              Профи
                              ****

                              Группа: Пользователи
                              Сообщений: 559
                              Пол: Мужской
                              Реальное имя: Бруно

                              Репутация: -  10  +



                              Доброго дня ув. форумчане. Сталкнулся с такой проблемой — моя Delphi программа работает с файлом и при каждом своём запуске считывает информацию с него (формат .bmp). В рандомном порядке я получаю ошибку Stream read error, которая обычно возникает, когда какое-либо другое приложение так же пользуется этим файлом. Но я проверил и ни 1 из приложений не может использовать этот файл — так же просто перезагружал компьютер и первым делом запускал программу — тоже самое, хотя на автозапуске системы никаких лишних программ нету. Буду рад любым мыслям и идеям.

                              ———————

                              Цитата

                              Imagination is more important than knowledge.

                              Albert Einstein

                               Оффлайн  Профиль  PM 

                               К началу страницы 

                              + Ответить 

                              IUnknown

                              сообщение 25.11.2011 22:41

                              Сообщение
                              #2


                              Гуру
                              *****

                              Группа: Пользователи
                              Сообщений: 1 013
                              Пол: Мужской
                              Ада: Разработчик
                              Embarcadero Delphi: Сторонник
                              Free Pascal: Разработчик

                              Репутация: -  627  +



                              Цитата

                              Но я проверил и ни 1 из приложений не может использовать этот файл — так же просто перезагружал компьютер и первым делом запускал программу — тоже самое, хотя на автозапуске системы никаких лишних программ нету.

                              Значит, предположение о том, что что-то держит файл — неверное. Показывай код, выдающий ошибку, либо обращайся к телепатам за помощью. Без исходника ничего сказать невозможно.

                               Оффлайн  Профиль  PM 

                               К началу страницы 

                              + Ответить 

                              Tan

                              сообщение 25.11.2011 22:47

                              Сообщение
                              #3


                              Профи
                              ****

                              Группа: Пользователи
                              Сообщений: 559
                              Пол: Мужской
                              Реальное имя: Бруно

                              Репутация: -  10  +



                              Cпасибо за ответ. Есть картинка 256 x 256, переменная TexFileName содержит название файла, что находится в корневой директории проекта.

                              const
                              TexWidth = 256;
                              TexHeight = 256;
                              ...
                              TexSizeX := TexWidth;
                              TexSizeY := TexHeight;
                              bitmap := TBitmap.Create;
                              bitmap.LoadFromFile(TexFileName);
                              For i := 0 to TexSizeX - 1 do // вот сюда кидает программу когда выдаётся ошибка
                              For j := 0 to TexSizeY - 1 do
                              begin
                              bits [i, j, 0] := GetRValue(bitmap.Canvas.Pixels[i,j]);
                              bits [i, j, 1] := GetGValue(bitmap.Canvas.Pixels[i,j]);
                              bits [i, j, 2] := GetBValue(bitmap.Canvas.Pixels[i,j]);
                              end;

                              ———————

                              Цитата

                              Imagination is more important than knowledge.

                              Albert Einstein

                               Оффлайн  Профиль  PM 

                               К началу страницы 

                              + Ответить 

                              IUnknown

                              сообщение 25.11.2011 22:53

                              Сообщение
                              #4


                              Гуру
                              *****

                              Группа: Пользователи
                              Сообщений: 1 013
                              Пол: Мужской
                              Ада: Разработчик
                              Embarcadero Delphi: Сторонник
                              Free Pascal: Разработчик

                              Репутация: -  627  +



                              Картинка чем создавалась? Фотошопом? Попробуй ее пересохранить другим редактором, GIMP-ом, или ACDSee, к примеру. На RSDN как-то была тема, что сохраненные фотошопом битмапы не грузятся через LoadFromFile, а пересохраненные (их размер — на 2 байта меньше, чем у фотошопленных) — прекрасно грузятся.

                               Оффлайн  Профиль  PM 

                               К началу страницы 

                              + Ответить 

                              Tan

                              сообщение 26.11.2011 2:48

                              Сообщение
                              #5


                              Профи
                              ****

                              Группа: Пользователи
                              Сообщений: 559
                              Пол: Мужской
                              Реальное имя: Бруно

                              Репутация: -  10  +



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

                              Сообщение отредактировано: Tan — 26.11.2011 2:48

                              ———————

                              Цитата

                              Imagination is more important than knowledge.

                              Albert Einstein

                               Оффлайн  Профиль  PM 

                               К началу страницы 

                              + Ответить 

                              1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)

                              Пользователей: 0

                              500Gb HDD, 6Gb RAM, 2 Cores, 7 EUR в месяц — такие хостинги правда бывают

                              Связь с администрацией: bu_gen в домене octagram.name

                              Понравилась статья? Поделить с друзьями:
                            • Stream memory fix error gta san andreas как исправить
                            • Stream extract error мобайл легенд
                            • Stream extract error mobile legends что делать
                            • Stream error sometimes скачать
                            • Stream error internal server error