Программирование всегда было достаточно сложной задачей. Эта книга поможет вам легко преодолеть возникающие трудности с помощью библиотеки мощных алгоритмов, полностью реализованных в исходном коде Delphi. Вы узнаете, как выбрать способ, наиболее подходящий для решения конкретной задачи, и как добиться максимальной производительности вашего приложения. Рассматриваются типичные и наихудшие случаи реализации алгоритмов, что позволит вам вовремя распознать возможные трудности и при необходимости переписать или заменить часть программы. Подробно описываются важнейшие элементы алгоритмов хранения и обработки данных (списки, стеки, очереди, деревья, сортировка, поиск, хеширование и т.д.). Приводятся не только традиционные решения, но и методы, основанные на последних достижениях объектно-ориентированного программирования.
Книга предназначена для начинающих программистов на Delphi, но благодаря четкой структуризации материала и богатой библиотеке готовых алгоритмов будет также интересна и специалистам.
Этот документ создан на основе компиляции нескольких документов, а также на основе перевода с английского страниц-учебников, которые в обилии можна встретить на многочисленных Web-узлах в Интернете, посвященных программированию на PHP и связи его с MySQL.
Данный труд не ставит целью заменить собой мануалы по PHP и MySQL. Дело в том, что большинство материалов, в том числе и руководства к данным программным продуктам пока распространяются на английском языке. Качественные пособия на русском языке встречаются редко.
Данный букварь просто поможет Вам сделать первые шаги в изучении PHP и способах взаимодействия его с БД MySQL. В этой книге Вы найдете ответы на свои основные вопросы.
Данное руководство представляет собой введение в JavaScript. Я начал составлять его в качестве онлайнового руководства, где Вы бы имели возможность тут же проверять все описанные примеры на практике. Но поскольку подобное руководство стало со временем большим, понадобилось создать для него печатный вариант. Действительно, весьма утомительно читать большие куски текста, сидя перед монитором. Но вместе с тем очевидно, что печатное руководство не может в полной мере заменить онлайновый вариант.
Environmental Audio (дословно окружающий звук)- это новый стандарт звука, разработанный фирмой Creative Labs, создающий эффекты окружающей среды реального мира на компьютере. Environmental Audio сегодня ужк много больше простого surround -звука и 3D моделирования. Это и настоящее моделирование окружающей среды с помощью мощных эффектов с учётом размеров комнаты, её звуковых особенностей, реверберации, эхо и многих других эффектов, создающих ощущение реального аудио мира.
Как работает Environmental Audio
Эффекты окружающей среды моделируются при помощи технологии E-mu Environmental Modeling, поддерживаемой аудиопроцессором EMU10K1, установленного на серии звуковых карт SBLive! Технология Environmental Audio разработана с учётом работы на наушниках, двух или четырёх колонках. Чип EMU10K1 раскладывает любой звуковой поток на множество каналов, где накладывает эффекты в реальном времени. За счёт этого создаются уже новые звуки, такие, как они должны быть в природе. На стадии обработки звука кроме его пололжения в пространстве должны быть учтены, как минимум, два фактора: размер помещения и реверберация, так как человеческое ухо слышит не просто оригинальный звук, а звук с учётом дистанции, местоположения и громкости. Стандарт Environmental Audio обрабатывает все эти условия для получения высококачественного реального звука.
Environmental Audio использует координаты X, Y, Z, а также реверберацию и отражения звука. Эти координаты используются при базовой подготовки каналов аудио источника и эффектов "окраски" звуковой сцены. Основная мощность аудиопроцессора расходуется на обработку каждого звукового источника по всем каналам и на добаление эффектов в реальном времени. Как уже говорилось, для создания ощущения реального звука нужно учитывать как минимум 3 фактора: расстояние до источника звука, размер звукового помещения и реверберацию.
Environmental Audio Extensions (EAX)
Это API, разработанный фирмой Creative Labs для достижения реальных звуковых эффектов в компьютерных играх. EAX- это расширение API DirectSound3D от фирмы Microsoft На 18 Октября 1999 года единственной звуковой картой, поддерживающей этот стандарт является Sound Blaster Live! (в разных модификациях). На сегодня Creative выпустила три версии этого стандарта.
DirectSound3D управляет местоположением в 3D пространстве игры источников звука и слушателя. Например, игра может использовать DirectSound3D для создания раздельных источников звука для каждого существа в игре, получая, таким образом, звуки выстрелов и голоса в разных местах 3D-мира. Эти звуки, также как и слушатель, могут перемещаться в пространстве. Разработчики игр могут использовать такие звуковые возможности, как палитра направлений (звук в одном направлении может идти громче, чем в другом), эффект Допплера (звук может нарастать, достигнув слушателя, и потом спадать, как бы удаляясь в пространство).
EAX улучшает DirectSound3D созданием виртуального окружающего аудио мира вокруг источников звука и слушателя. Эта технология эмулирует реверберации и отражения, идущие со всех сторон от слушателя. Эти эффекты создают впечатление, что вокруг слушателя существует реальный мир со своими параметрами, как то: размер помещения, отражающие и поглощающие свойства стен и другие. Программисты игр могут создавать различные акустические эффекты для разных помещений. Таким образом, игрок, который играет в EAX игру может слышать разницу в звуке при переходе из коридора в пещеру.
В дополнении к созданию окружающих эффектов, EAX 1.0 может изменять параметры различных источников звука. При изменении местоположения источника звука относительно слушателя автоматически изменяются параметры реверберации.
Что касается программирования, то здесь EAX предоставляет следующие возможности.
* Выбор среди большого числа "пресетов" для моделирования эффектов окружающей среды.
* Возможность изменять параметры пресетов окружающей среды для каждого источника в отдельности.
* Автоматическое изменение критических параметров, применяемых к позиции. Когда источник звука движется по отношению к слушателю, EAX автоматически изменяет параметры отражения звука и реверберации для создания более реальных звуковых эффектов при движении источника звука через 3D звуковой мир.
Occlusions и Obstructions
Эффект occlusions создаёт впечатление, что источник звука находится в другой комнате, в другом месте, за стеной. Это свойство позволяет изменять параметры передачи звуковой характеристики для получения эффекта различных материалов стен и их толщину. Например, программа может использовать это свойство для создания звука, идущего из-за двери, или из-за стены.
Эффект obstructions позволяет эмулировать звуковые препятствия, создавая ощущение, что источник звука находится в той же комнате, но за препятствием. Например, можно сделать так, что звук будет идти из-за большого камня, находящегося в той же пещере, что и слушатель.
Геометрическое моделирование и EAX
Геометрическая модель сцены используется как в графических целях, так и для создания 3D звука. Для создания геометрической модели компьютер должен иметь данные о физических свойствах мира: какие объекты где расположены, какие звуконепроницаемые, какие звукопоглощающие и так далее. После того, как эта информация получена, производится расчёт некоторого количества слышимых отражений и поглощений звука от этих объектов для каждого источника звука. Это приводит к затуханиям звука, из-за препятствий, звуконепроницаемых стен и так далее. Расчёты отражений методом "зеркала" широко используются для создания акустики зданий. Этот метод подразумевает, что звук отражается прямо (как от зеркала) без преломлений и поглощений. На самом же деле, вместо того, чтобы в реальном времени рассчитывать все отражения и особенности среды (что на самом деле процесс трудоёмкий) используются заранее рассчитанные упрощённые модели геометрических аудио сред, которые отличаются от графических представлений о среде. То есть в игре используются одновременно отдельная среда для визуальных эффектов и более простая для звуковых эффектов. Это создаёт проблемы, как, например, если бы вы захотели передвинуть часть стены в комнате, то вам пришлось бы создавать новую среду для звука. В настоящее время над геометрическим моделирование звука ведутся работы во многих звуковых лабораториях.
EAX для разработчиков
EAX не требует того, чтобы источники звука привязывались к графическому представлению об окружающей среде. Но при желании разработчик, который хочет создать звуковые эффекты "повышенной реальности", которые максимально близки к графическому представлению о сцене может использовать дополнительное управление ранними отражениями, преломлениями и поглощениями. При создании своих эффектов EAX использует статические модели среды, а не её геометрические параметры. Эти модели автоматически рассчитывают реверберации и отражения относительно слушателя с учётом размеров помещения, направления звука и других параметров, которые программист может добавлять, для каждого источника звука. Поэтому EAX намного проще других стандартов, так как он не требует описания геометрической среды сцены, а использует подготовленные заранее модели. Игра может менять звуковые модели при переходе от одного места к другому для создания реальных эффектов. Я хочу рассмотреть это подробней. Допустим, у вас есть сцена в игре ввиде каменной пещеры. Есть два способа получить высокореалистичные эффекты. Первый из них- рассчитать геометрическую модель и использовать её как аудио маску для сцены, причём новые технологии будут позволять делать это в реальном времени. Второй способ- взять готовый пресет и, при необходимости, изменить его для получения более качественных эффектов. Разумеется, первый способ даст больший реализм, чем второй, но и потратит ресурсов в несколько раз больше. А если учитывать лень программистов, то в этом случае EAX наиболее благоприятный вариант.
Различия между EAX 1.0, 2.0 и 3.0
EAX 1.0
* Поддерживает изменение места в игре реверберации и отражений.
* Имеет большое количество пресетов.
* Позволяет (ограниченно) изменять реверберацию окружения.
* Позволяет автоматически изменять интенсивность реверберации, в зависимости от положения источника звука относительно слушателя.
EAX 1.0 строит звуковую сцену на основе заранее созданных пресетов, учитывая дистанцию между источниками звука и слушателем. Соответственно, EAX 1.0 предоставляет большой набор пресетов "на каждый случай жизни". Также имеется возможность изменять параметры поздней реверберации (дэмпинг, уровень) и автоматическое изменение уровня в зависимости от расстояния. Благодаря этому происходит улучшенное восприятие расстояния до источника.
EAX 2.0
* Обновлена реверберационная модель.
* Добавлены эффекты звуковых преград (Obstructions) и поглощений (Occlusions).
* Отдельное управление начальными отражениями и поздними реверберациями. Продолжительный контроль размеров помещений. Улучшенная дистанционная модель для автоматического управления реверберациями и начальными отражениями, основанными на местоположении источника звука относительно слушателя.
* Возможность учитывать звуковые свойства воздуха (поглощение звука).
* Теперь для использования эффектов Environmental Audio не не требуется описание геометрии помещения.
EAX 2.0 построен на возможностях первой версии и создаёт ещё более реалистичные эффекты засчёт поддержки преграждения и отражения звука, а также на улучшенной технологии определения направления звука.
EAX 3.0
* Контроль за ранними реверберациями и отражениями для каждого источника звука.
* Динамический переход между окружающими моделями.
* Улучшенная дистанционная модель для автоматического управления реверберацией и начальными отражениями в зависимости от положения источников звука относительно слушателя.
* Расчёты Ray-Tracing (отражение лучей) для получения параметров отражения для каждого источника звука.
* Отдельные отражения для дальних эхо.
* Улучшенное дистанционное представление, призванное заменить статические реверберационные модели.
EAX 3.0 совмещает вторую версию с более мощными возможностями. Новый уровень реализма достигается засчёт поддержки местных отражений, изолированных отражений, продолжительных переходов между звуковыми сценами и другими особенностями.
Вывод: по всему вышесказанному можно судить о том, что на сегодня EAX является очень перспективным и конкурентоспособным стандартом. Любой программист, несведующий в особенностях 3D звука сможет создавать реальные эффекты для своих игр с помощью пресетов. Что касается качества 3D звука, то оно вне конкуренции. Сейчас большинство игр не поддерживает (или поддерживает криво) такие эффекты, как преграждение и поглощение звука. Первой игрой, полностью поддерживающей EAX 2.0 обещает быть Unreal Tournament, если его не опередят. Там будет видно.
P.S. Я специально не стал сравнивать EAX с другими стандартами, как, например, A3D. Для этого нужны игры, поддерживающие одновременно и то и другое в полной форме. На сегодня таких игр нет.
Вторая часть серии статей "Использование регулярных выражений в PHP" посвящена решению ряда проблем обработки сложных текстов с помощью "продвинутых" операторов регулярных выражений.
Несмотря на то, что термины данные и информация используются взаимозаменяемо, между ними есть существенная разница. Данные существуют реально. Данные — - это список температур, перечень недавних продаж или опись товара, имеющегося в наличии. Информация — это прогнозы. Информация — это предсказание погоды, прогноз прибылей и убытков и тенденции сбыта. Данные записываются в виде нулей и единиц, в то время как информация обрабатывается мозгом.
Между данными и информацией располагается приложение: механизм, который преобразует одно в другое и наоборот. Например, при покупке книги в Интернете это приложение преобразует вашу информацию — название книги, идентификатор, информацию о банковском счете — в данные: номер заказа, цену со скидкой, характеристики транзакции с использованием кредитной карточки и количество оставшихся в наличии экземпляров книги. Аналогичным образом, приложение преобразует данные в запрос на выборку со склада, отметку об отгрузке и номер отслеживания — информацию, необходимую для реализации продажи.
В действительности сложность создания приложения прямо пропорциональна преобразованиям, которые оно выполняет. Гостевая книга Web-сайта, передающая имя и адрес в поля базы данных, устроена элементарно. С другой стороны, онлайновый магазин, который передает большое количество видов информации в модель данных коммерческой сделки и преобразует данные в информацию для реализации процесса принятия решений, достаточно сложен с точки зрения разработки. Искусство программирования заключается в умелом манипулировании данными и информацией — мастерство, схожее с фиксацией света в живописи.
Как было сказано в первой части, регулярные выражения являются одним из самых мощных средств манипулирования данными. Регулярные выражения лаконично описывают форму данных и раскладывают их на составляющие. Например, следующее регулярное выражение можно использовать для обработки температуры, заданной в градусах по Цельсию или по Фаренгейту: /^([+-]?[0-9]+)([CF])$/.
Регулярное выражение сравнивает начало строки (отображается знаком "крышка" (^), за которым идет знак "+", знак "-", или ничего ([+-]?), за которым следует целое число ([0-9]+), обозначение шкалы — Цельсия или Фаренгейта ([CF]) — и заканчивается концом строки (обозначается знаком доллара $).
В данном регулярном выражении операторы начала строки и конца строки представляют собой примеры операторов нулевой ширины или совпадений по положению, а не по символам. Круглые скобки также не указывают на символы. Зато, если заключить шаблон в круглые скобки, то будет извлечен текст, соответствующий шаблону. Следовательно, если текст полностью сопоставим с шаблоном, то первая пара круглых скобок выдаст строку, представляющую собой положительное или отрицательное целое число, например, +49, а вторая пара круглых скобок - или букву C, или F.
В первой части серии представлено понятие регулярного выражения и были описаны PHP-функции для сравнения текста с шаблонами, а также для извлечения совпадений. А теперь давайте углубимся в изучение регулярных выражений и посмотрим на некоторые "продвинутые" операторы и средства.
Круглые скобки опять приходят на помощь
В большинстве случаев пара круглых скобок используется для описания части шаблона и получения текста, соответствующего этой части. Однако от круглых скобок не всегда требуется получение части шаблона. Как и в сложной арифметической формуле, круглые скобки можно использовать для группировки условий.
Приведу пример. Догадаетесь, какому типу данных соответствует данное выражение?
/[-a-z0-9]+(?:\.[-a-z0-9]+)*\.(?:com|edu|info)/i
Как можно догадаться, это регулярное выражение определяет имена Интернет-сайтов (только для доменов .com, .edu, и .info). Отличием является использование дополнительного оператора ?:. Квалификатор части шаблона ?: отключает функцию извлечения данных, и тем самым дает круглым скобкам возможность обозначать последовательность действий. Например, в данном случае фраза (?:\.[-a-z0-9]+)* соответствует нулю или более элементам строки, например, ".ibm." Аналогично, фраза \.(?:com|edu|info) обозначает последовательность символов, за которой идет одна из строк com, edu, или info.
Отключение функции извлечения информации может показаться бессмысленным, если не подумать о том, что извлечение информации требует дополнительной обработки. Если программа обрабатывает большое количество данных, то отказ от извлечения может быть целесообразным. Кроме того, если вы имеете дело со сложным регулярным выражением, то отключение функции извлечения информации в некоторых частях шаблона может упростить извлечение тех частей шаблона, которые реально нужны.
Примечание: Модификатор i в конце регулярного выражения делает все сопоставления с шаблоном нечувствительными к регистру. Следовательно, подмножество a-z будет сопоставимо со всеми буквами, независимо от регистра.
В PHP есть и другие модификаторы частей шаблона (subpattern). Используя отладчик регулярных выражений, показанный в первой части данной серии (повторно показан в листинге 1), попробуйте сопоставить регулярное выражение ((?i)edu) со строками "EDU," "edu," и "Edu." Если в начале части шаблона задать модификатор (?i), то сопоставление с шаблоном не будет зависеть от регистра. Чувствительность к регистру восстанавливается, как только заканчивается данная часть шаблона. (Сравните с модификатором / ... /i, который применяется ко всему шаблону.)
Листинг 1. Простой отладчик регулярных выражений
Еще один полезный модификатор части шаблона - это (?x). Он позволяет добавлять в шаблон пробелы, что упрощает чтение регулярных выражений. Таким образом, часть шаблона ((?x) edu | com | info) (обратите внимание на пробелы между операторами дизъюнкции, которые добавлены для удобочитаемости) аналогична (edu|com|info). Для того, чтобы добавлять пробелы и комментарии в регулярное выражение, можно использовать глобальный модификатор / ... /x, см. листинг ниже.
Листинг 2. Добавление пробелов и комментариев
Как видно из листинга, при необходимости модификаторы можно объединять. Если необходимо включить в регулярное выражение символ пробела при использовании модификатора (?x), используйте метасимвол \s для поиска любого пробельного символа и \ (обратный слеш с пробелом) для поиска одного пробела, например, ((?x) hello \ there).
Оглядываемся вокруг
В подавляющем большинстве случаев регулярные выражения используются для проверки или декомпозиции входной информации на отдельные "лакомые кусочки", которые записываются в архив данных или сразу же обрабатываются приложением. Общепринятыми сферами применения являются: обработка полей форм, парсинг XML-кода и анализ протоколов.
Еще одна область применения регулярных выражений - форматирование, нормализация или улучшение читаемости данных. Вместо того чтобы использовать регулярные выражения для поиска и извлечения текста, при форматировании они применяются для поиска и вставки текста в надлежащее местоположение.
Вот пример полезного применения форматирования. Предположим, что Web-форма передает приложению значение зарплаты с округлением до целого доллара. Так как зарплата хранится в виде числа целого типа, то перед сохранением переданных данных приложение должно удалять из них знаки пунктуации. Однако при извлечении данных из хранилища, возможно, понадобится изменить их формат и сделать удобочитаемыми с помощью разделителей. В листинге 3 показано, как простой PHP-запрос преобразует сумму в долларах в число.
Листинг 3. Преобразование суммы в долларах в число
Вызов функции preg_replace() заменяет знак доллара, любой пробельный символ и все запятые -- на пустую строку, возвращая то, что предположительно является целым числом. Если проверка функцией is_numeric() подтверждает правильность входных данных, их можно сохранить.
А теперь давайте выполним обратную операцию - добавим к числу знак денежной единицы и запятые-разделители сотен, тысяч и миллионов. Для добавления запятых в определенных позициях можно написать программу для поиска этих компонентов, а можно воспользоваться операторами посмотри вперед и посмотри назад. Модификатор части шаблона ?<= обозначает посмотри назад (то есть влево) от текущей позиции. Модификатор ?= означает "посмотри вперед" (то есть вправо) от текущей позиции.
[pagebreak]
Итак, какие позиции нам нужны? Любое место в строке, при условии, что есть как минимум один символ слева и одна или более групп по три символа справа, не считая десятичной точки и количества центов. Соблюдая это правило и используя два модификатора, анализирующих символы справа и слева от определенной позиции и являющихся операторами нулевой ширины, мы можем достичь цели с помощью следующей инструкции:
Как работает это регулярное выражение? Начиная с первого символа строки и обрабатывая каждый символ, регулярное выражение отвечает на вопрос: "Есть ли хотя бы один символ слева и одна или несколько групп из трех символов справа?" Если да, то наш оператор нулевой ширины заменяется запятой.
Большинство сложных сопоставлений можно реализовать, используя стратегию, аналогичную приведенной выше. Например, вот еще один вариант использования оператора "посмотри вперед", который решает широко распространенную дилемму.
Листинг 4. Пример использования оператора "посмотри вперед" ("предвидение")
Оператор preg_replace() преобразует строку данных, разделенных запятыми, в строку данных, разделенных знаком табуляции. Предусмотрительным образом, он не заменяет запятые в строке, заключенной в кавычки.
Это регулярное выражение при каждом обнаружении запятой (на это указывает запятая в самом начале регулярного выражения) проверяет утверждение: "Впереди не было кавычек или было четное количество кавычек". Если утверждение верно, то запятую можно заменить знаком табуляции (the \t).
Если Вам не нравятся операторы «посмотри вперед» и «посмотри назад» или вы работаете с таким языком, в котором их нет, можно добавить запятые в число и с помощью обычного регулярного выражения. Однако для реализации такого решения потребуется много итераций.
Листинг 5. Добавление запятых
Давайте пройдем по коду. Сначала параметр зарплаты очищается от знаков пунктуации для моделирования ситуации чтения целого числа из базы данных. Затем выполняется цикл в поисках позиций, где за одним числовым символом ((\d) идут три числовых символа ((\d\d\d\): если обнаруживается граница слова, заданная как \b, цикл прекращается. Граница слова -- это еще один оператор нулевой ширины, который соответствует следующим позициям:
* Перед первым символом строки, если это буква слова.
* За последним символом строки, если это буква слова.
* Между буквой слова и небуквенным символом, непосредственно за буквой слова.
* Между небуквенным символом и буквой слова, непосредственно за небуквенным символом.
Таким образом, примерами правильных границ слова являются пробел, точка и запятая.
Благодаря внешнему циклу регулярное выражение перемещается слева направо в поисках цифры, за которой идут три цифры и граница слова. При обнаружении совпадения между двумя частями шаблона добавляется запятая. Цикл должен продолжаться до тех пор, пока оператор preg_replace() находит совпадения, что задано в условии $old != $pretty_print.
Жадность и лень
Регулярные выражения обладают большими возможностями, иногда даже слишком большими. Например, давайте рассмотрим, что произойдет, если регулярное выражение ".*" будет обрабатывать строку "The author of 'Wicked' also wrote 'Mirror, Mirror.'" Вероятно, вы предполагаете, что preg_match() вернет два совпадения, и с удивлением обнаружите, что результат всего один: 'Wicked' also wrote 'Mirror, Mirror.'
Почему? Если не задать иное, то такие операторы как * (ноль или более) и + (один или более) -- "жадные". Если сопоставление с образцом может продолжаться, то они и будут его продолжать до тех пор, пока не будет возвращен максимальный результат из возможных. Для сохранения минимальных совпадений необходимо принудительно заставлять определенные операторы быть "ленивыми". "Ленивые" операторы находят самое короткое совпадение и на этом останавливаются. Чтобы сделать оператор более "ленивым", добавьте суффикс в виде знака вопроса. Пример показан в листинге 6.
Листинг 6. Добавление суффикса в виде знака вопроса
Регулярное выражение ".*?" расшифровывается следующим образом: "найти кавычку, за которой идет ровно столько символов с последующей кавычкой.
Однако иногда оператор * может быть слишком "ленивым". Например, посмотрите на следующий фрагмент кода. Что он делает?
Листинг 7. Простой отладчик регулярных выражений
Что вы загадали? "123"? "1"? Нет результата? На самом деле результатом будет Array ( [0] => [1] => ), означающий, что совпадение было найдено, но никаких данных извлечено не было. Почему? Вспомните, что оператор * ищет совпадения с нулем или более символов. В данном случае, выражение [0-9]* находит совпадение с нулем символов от начала строки, и обработка заканчиваетс.
Для решения данной проблемы добавьте оператор нулевой ширины для привязки совпадения, который заставляет регулярное выражение продолжать сопоставления; /([0-9]*\b/.
Советы и рекомендации
С помощью регулярных выражений можно решать как простые, так и сложные задачи при обработке текста. Начните с небольшой группы операторов и по мере того, как вы будете набираться опыта, расширяйте свой словарь. В качестве вознаграждения за ваши старания -- некоторые советы и рекомендации.
Создание переносимых регулярных выражений с помощью классов символов
Вам уже знакомы метасимволы, например, \s - соответствует любому пробельному символу. Кроме того, большинство реализаций регулярных выражений поддерживает предопределенные классы символов, которые более просты в использовании и переносимы с одного письменного языка на другой. Например, класс символов [:punct:] замещает все символы пунктуации в данном языке. Вместо [0-9] можно использовать [:digit:] и более переносимое замещение [:alpha:] вместо [-a-zA-Z0-9_]. Например, можно убрать все знаки пунктуации, используя:
Класс символов представляет собой более сжатую форму по сравнению с подробным описанием всех символов пунктуации. Полный перечень классов символов можно найти в документации по версии языка PHP.
Как исключить то, что вы не ищете
Как показано в примере с данными, разделенными символом табуляции, в качестве значений, разделенных запятыми (CSV), иногда проще и точнее задать список тех вариантов, которые не нужно находить (сопоставлять). Последовательность, начинающаяся со знака "крышка" (^) будет соответствовать любому символу, не принадлежащему данной последовательности. Например, для проверки правильности телефонных номеров для США можно использовать регулярное выражение /[2-9][0-9]{2}[2-9][0-9]{2}[0-9]{4}/. Используя набор ограничений можно написать регулярное выражение в более явном виде /[^01][0-9]{2}[^01][0-9]{2}[0-9]{4}/. Оба регулярных выражения работают, хотя смысл последнего, вероятно, более понятен.
Пропуск новой строки
Если во входных данных несколько строк, стандартного регулярного выражения будет недостаточно, так как сканирование прекращается на начале новой строки, которая обозначается $. Однако, если воспользоваться модификаторами s или m, то регулярное выражение будет обрабатывать входные данные по-другому. Первый модификатор рассматривает строковую последовательность как одну строку, где точка указывает на начало новой строки (обычно она этого не делает). Второй рассматривает строковую последовательность как несколько строк, где ^ и $ соответствуют началу и концу любой строки, соответственно. Приведем пример. Если задать $string = "Hello,\nthere";, то оператор preg_match( "/.*/s", $string, $matches) параметру $matches[0] присвоит значение Hello,\nthere. (При удалении s будет выдано Hello.)
Поиск по шаблону является настолько обычным занятием в разработке программного обеспечения, что для облегчения этой задачи была создана специальная технология — регулярные выражения. Узнайте, как можно использовать ее при написании кода, прочитав эту статью.
Все устройства получают входную информацию, выполняют какие-либо операции и выдают результат. Например, телефон во время разговора преобразует звуковую энергию в электрический сигнал и обратно. Двигатель потребляет топливо (пар, расщепление атомных ядер, бензин, мышечные усилия) и преобразует его в энергию. Блендер поглощает ром, лед, лайм и кюрасао и взбалтывает их в коктейль Mai Tai. (Или, если вам хочется чего-то изысканного, сделайте Bellini из шампанского и грушевого сока. Блендер – замечательное универсальное устройство.)
Так как программное обеспечение преобразует данные, то каждое приложение фактически является устройством (хоть и виртуальным, так как у него нет физических составляющих). Например, компилятор в качестве входной информации получает исходную программу и преобразует ее в двоичный исполняемый код. Программа прогнозирования погоды генерирует предсказания на основе результатов прошлых (исторических) замеров, а графический редактор обрабатывает пикселы, применяя правила к отдельным пикселам или их группам, чтобы, например, сделать изображение более четким или изменить его стиль.
Так же, как и любое другое устройство, программное обеспечение предназначено для работы с определенным исходным материалом, например, набором чисел, данными XML-схемы или протоколом. Если программе задать некорректную входную информацию — неподходящую по форме или типу, то существует большая вероятность того, что результат будет непредсказуемым и, возможно, даже катастрофическим. Как говорится: "Мусор заложишь - мусор получишь".
На самом деле для решения всех нетривиальных задач необходимо отделять правильные данные от некорректных и отклонять некорректные данные во избежание ошибок в результатах. Это, конечно же, актуально и для Web-приложений, написанных на языке PHP. Неважно, получены ли входные данные из формы для ввода с клавиатуры или в результате выполнения программного запроса Asynchronous JavaScript + XML (Ajax), прежде чем начать какие-либо вычисления, программа должна проверить входную информацию. Возможно, что числовые значения должны находиться в пределах определенного диапазона чисел или представлять собой только целые числа. Возможно, значение должно соответствовать определенному формату, например, почтового индекса. Например, почтовый индекс в США представляет собой пять цифр плюс дополнительный префикс "Plus 4", состоящий из дефиса и 4 дополнительных цифр. Возможно, другие строки также должны состоять из определенного количества символов, например, две буквы для указания аббревиатуры штата США. Строковые данные доставляют особенно много проблем: PHP-приложение должно быть начеку по отношению к злонамеренным программам-агентам, вложенным в SQL-запросы, код JavaScript или любой другой код, которые способны изменить поведение приложения или обойти защиту.
Однако каким образом программа может определить, являются ли входные данные числом или соответствуют ли определенным требованиям, например, к почтовому индексу? На самом деле для реализации проверки путем сопоставления с шаблоном необходим небольшой парсер, создающий конечный автомат, считывающий входные данные, обрабатывающий маркеры, отслеживающий состояние и выдающий результаты. Однако создание и обслуживание даже самого простого парсера может оказаться непростым делом.
К счастью, анализ на основе сопоставления с шаблоном настолько широко распространен в компьютерных технологиях, что с течением времени (примерно с момента появления UNIX®) были разработаны специальные технологии и, конечно же, механизмы обработки, чтобы облегчить рутинную работу. Регулярное выражение (regex) описывает шаблоны посредством лаконичных и удобочитаемых обозначений. Получив регулярное выражение и данные, механизм regex сообщает, совпадают ли эти данные с шаблоном, и если совпадение было обнаружено, что именно совпало.
Вот небольшой пример использования регулярного выражения, взятый из UNIX-утилиты, работающей в режиме командной строки, которая ищет заданный шаблон в содержимом одного или нескольких текстовых файлов UNIX. Команда grep -i -E '^Bat' ищет последовательность символов beginning-of-line (начало строки), обозначаемое "крышкой", [^]), за которым следуют буквы b, a, и t верхнего или нижнего регистра (ключ -i указывает на то, что при сопоставлении с шаблоном регистр не учитывается, таким образом, например, B и b - тождественны). Следовательно, для файла heroes.txt:
Листинг 1. heroes.txt
Вышеупомянутая команда grep выдаст два совпадения:
Batman
Batgirl
Регулярные выражения
PHP предлагает два программных интерфейса регулярных выражений: один -- для интерфейса переносимых операционных систем (POSIX), а второй - для регулярных выражений, совместимых с языком Perl (PCRE). В общем и целом второй интерфейс является более предпочтительным, так как PCRE сам по себе мощнее, чем POSIX, и предоставляет все операторы, используемые в языке Perl. Более подробная информация по обращению к regex-функциям POSIX представлена в документации по языку PHP (см. раздел Ресурсы). В данной статье мы сосредоточим свое внимание на свойствах PCRE.
Регулярные выражения PHP PCRE содержат операторы, позволяющие путем сопоставления находить конкретные символы или другие операторы, определенные местоположения, например, начало и конец строки, начало или конец слова. Регулярные выражения также позволяют описывать альтернативы, которые можно задать альтернативы типа "или"-"или"; повторения фиксированной, изменяемой или неопределенной длины; наборы символов (например, "любая буква от a до m"); и классы, или типы символов (печатаемые символы, знаки препинания). Специальные операторы также разрешают использовать группировку — возможность применить оператор к целой группе других операторов.
В таблице 1 показаны некоторые типичные операторы регулярных выражений. Для создания сложных выражений можно последовательно объединять элементарные операторы из таблицы 1 (и другие).
Таблица 1. Типичные операторы регулярных выражений
Оператор Значение
. (точка) Любой одиночный символ
^ (крышка) Пустая последовательность в начале строки или цепочки
$ (знак доллара) Пустая последовательность в конце строки
A Буква A верхнего регистра
a Буква a нижнего регистра
\d Любая цифра
\D Любой нецифровой символ
\w Любая буква или цифра; синоним - [:alnum:]
[A-E] Любая заглавная буква из A, B, C, D или E
[^A-E] Любой символ, за исключением заглавных букв A, B, C, D или E
X? Найти совпадение по отсутствию или наличию одной заглавной буквы X
X* Ни одной или любое количество заглавных букв X
X+ Одна или несколько заглавных букв X
X{n} Ровно n заглавных букв X
X{n,m} Не менее n и не более m заглавных букв X; если опустить m, то выражение будет искать не менее n заглавных букв X
(abc|def)+ По меньшей мере одно вхождение последовательности abc и def
В следующем примере показано типичное использование регулярного выражения. Например, для web-сайта необходимо, чтобы каждый пользователь регистрировался. Имя пользователя должно начинаться с буквы и содержать от 3 до 10 буквенно-цифровых символов. Для проверки имени пользователя на соответствие ограничениям при отправке данных в приложение можно использовать следующее регулярное выражение: ^[A-Za-z][A-Za-z0-9_]{2,9}$.
Знак "крышка" соответствует началу строки. Первый набор [A-Za-z] соответствует любой букве. Второй набор [A-Za-z0-9_]{2,9} соответствует последовательности, содержащей от 2 до 9 букв, цифр или символов подчеркивания. Знак доллара ($) соответствует концу строки.
На первый взгляд, знак доллара может показаться лишним, однако его использование важно. Если его пропустить, то условиям данного регулярного выражения будет отвечать любая строка, которая начинается с буквы, содержит от 2 до 9 буквенно-цифровых символов и любое количество других символов. Иными словами, если бы не было знака доллара как привязки к концу строки, то подошла бы недопустимо длинная строка с подходящим началом, например, "martin1234-cruft" .
Программирование на языке PHP и регулярные выражения
В PHP есть функции для поиска совпадений в тексте, замены каждого совпадения на другой текст (похоже на операцию "найти и заменить") и поиска совпадений среди элементов списка. Вот эти функции:
Чтобы показать, как работают эти функции, давайте создадим небольшое PHP-приложение, которое будет просматривать список слов на соответствие определенному шаблону. Слова и регулярные выражения будут вводиться из обычной web-формы, а результаты отображаться в браузере посредством функции simple print_r(). Эта программка пригодится, если возникнет желание проверить или отладить регулярное выражение.
PHP-код показан в листинге 2. Все входные данные берутся из обычной HTML-формы. (Для краткости эту форму и PHP-код, отслеживающий ошибки, опустим.)
Листинг 2. Сравнение текста с шаблоном
Вначале с помощью функции preg_split() строка из слов, разделенных запятыми, преобразуется в отдельные элементы. Данная функция разбивает строку в тех местах, которые соответствуют условиям регулярного выражения. В данном случае регулярное выражение представляет собой просто "," , (запятая - разделитель списка слов, указанных через запятую). Слэш в начале и в конце просто показывает начало и конец regex.
Третий и четвертый аргументы функции preg_split() необязательны, но полезны. Добавьте в третий аргумент число n целого типа, если необходимо вернуть только первые n совпадений, или -1, если необходимо вернуть все совпадения. Если в качестве четвертого аргумента задать идентификатор PREG_SPLIT_NO_EMPTY, то функция preg_split() не будет возвращать пустые результаты.
Затем каждый элемент списка слов, разделенных запятыми, корректируется (убираются начальные и конечные пробелы) с помощью функции trim() и сравнивается с заданным регулярным выражением. Функция preg_grep() существенно упрощает процесс обработки списка: просто укажите в качестве первого аргумента шаблон, а в качестве второго - массив слов для сравнения. Функция возвращает массив совпадений.
Например, если в качестве шаблона задать регулярное выражение ^[A-Za-z][A-Za-z0-9_]{2,9}$ и список слов разной длины, то можно получить результат, показанный в листинге 3.
Листинг 3. Результат работы простого регулярного выражения
Кстати, с помощью дополнительного маркера PREG_GREP_INVERT можно инвертировать операцию preg_grep() и найти элементы, которые не совпадают с шаблоном (аналогично оператору grep -v в командной строке). Заменяя 22 строку на $matches = preg_grep( "/${_REQUEST[ 'regex' ]}/", $words, PREG_GREP_INVERT ) и используя входные данные из листинга 3, мы получим Array ( [1] => 1happy [2] => hermanmunster ).
Разбор строк
Функции preg_split() и preg_grep() очень удобны. Первая из них может разбирать строку на подстроки, если подстроки разделяются определенным шаблоном. Функция preg_grep() позволяет быстро отфильтровать список.
Но что произойдет, если строку нужно разобрать на составные части, используя одно или несколько сложных правил? Например, в США номера телефонов обычно выглядят следующим образом: "(305) 555-1212," "305-555-1212," или "305.555.1212." Если убрать пунктуацию, то количество символов сократится до 10 цифр, что легко можно определить с помощью регулярного выражения \d{10}. Однако код и префикс (каждый из которых состоит из трех цифр) телефонного номера США не могут начинаться с нуля или единицы (так как нуль и единица используются как префиксы для междугородных звонков). Вместо того чтобы разбивать числовую последовательность на отдельные цифры и создавать сложный код, для верификации можно использовать регулярное выражение.
Фрагмент кода позволяющий решить эту задачу, показан в листинге 4.
Листинг 4. Проверка американского телефонного номера
Давайте пройдем по этому коду:
* Как показано в таблице 1, в регулярных выражениях используется ограниченный набор специальных символов, например, квадратные скобки ([ ]) для наименования последовательности. Если надо найти такой символ в тексте, необходимо "выделить" специальный символ в регулярном выражении, поставив перед ним обратный слэш (\). Когда символ выделен, можно задать его посик, как и любого другого символа. Если нужно найти символ точки, например, в полном составном имени хоста, то напишите \.. При желании строку можно подать в функцию preg_quote() которая выполняет автоматическую изоляцию всех специальных символов регулярных выражений, как показано в строке 1. Если поставить echo() $punctuation после первой строки, то вы должны увидеть \(\)\.-.
* В строке 2 из телефонного номера убираются все знаки пунктуации. Функция preg_replace() заменяет все символы из $punctuation — операторы из набора [ ] - пустой строкой, эффективно устраняя такие символы. Возвращаемая новая строка присваивается переменной $number.
* В строке 4 определен шаблон верифицируемого телефонного номера США.
* Строка 5 реализует сопоставление, сравнивая телефонный номер, который теперь состоит только из цифр, с шаблоном. Функция preg_match() возвращает 1, если есть совпадение. Если совпадения нет, функция preg_match() возвращает нулевое значение. Если во время обработки возникла ошибка, то функция возвращает значение False (ложно). Таким образом, чтобы проверить удачное завершение, необходимо посмотреть, было ли возвращено значение 1. В противном случае проверьте итоговое значение функции preg_last_error() (если используется PHP версии 5.2.0 или выше). Если оно не равно нулю, то, возможно, был превышен лимит вычислений, например, разрешенная глубина рекурсии регулярного выражения. Обсуждение констант и ограничений, применяемых в регулярных выражениях PHP, представлено на странице, посвященной функциям регулярных выражений PCRE (см. раздел Ресурсы).
Извлечение данных
Во многих случаях необходимо только получить ответ на вопрос: "Соответствуют ли данные шаблону?" – например, при проверке данных. Однако чаще регулярные выражения используются для подтверждения соответствия и получения информации о совпадении.
Вернемся к примеру с телефонным номером. Пусть при соответствии шаблону нам необходимо сохранить код, префикс и номер линии в отдельных полях базы данных. Регулярные выражения могут запоминать совпадающие с шаблоном данные с помощью оператора capture. Оператор capture обозначается круглыми скобками и может использоваться в любой части регулярного выражения. Операции capture можно делать вложенными для поиска подсегментов в извлеченных сегментах данных. Например, чтобы из 10-значного номера телефона извлечь код города, префикс и номер линии, можно использовать следующую строку:
/([2-9][0-9]{2})([2-9][0-9]{2})([0-9]{4})/
Если входные данные соответствуют шаблону, первые три цифры захватываются первой парой круглых скобок, следующие три цифры - второй парой, а последние 4 цифры - последним оператором. Модификация вызова функции preg_match() возвращает извлеченные данные.
Листинг 5. Возврат извлеченных данных функцией preg_match()
Если в качестве третьего аргумента функции preg_match() указать переменную, например, в нашем коде, $matches, то в качестве ее значения будет выступать список извлеченных результатов. Нулевой элемент списка (с индексом 0) - это все совпадение целиком; первый элемент - совпадение, относящееся к первой паре круглых скобок, и так далее.
Вложенные операторы capture извлекают сегменты и подсегменты фактически любой глубины. Сложность с вложенными операторами capture состоит в том, чтобы определить, в какой части массива соответствий находится каждое соответствие, например, $matches. Действует следующее правило: подсчитайте порядковый номер открывающей скобки в регулярном выражении — этот номер и будет индексом нужного совпадения в массиве соответствий.
В листинге 6 показан пример (немного надуманный) извлечения частей городского адреса.
Листинг 6. Код для извлечения городского адреса
Опять все совпадение целиком хранится по индексу 0. А где хранится номер улицы? Если считать слева направо, номер улицы проверяется \d+. Это вторая открывающая круглая скобка слева, следовательно, значением $matches[2] будет 123. В $matches[4] оказывается название города, а в $matches[6] - почтовый индекс.
Продвинутые технологии
Обработка текста – широко распространенная задача, и PHP предоставляет ряд функций, упрощающих выполнение большого числа операций. Обратите внимание на следующее:
* Функция preg_replace() может работать как с одной строкой, так и с массивом строк. Если вызвать preg_replace() для массива строк, замена будет выполнена во всех элементах массива. В этом случае код preg_replace() возвращает массив измененных строк.
* Как и во всех остальных реализациях PCRE, здесь для осуществления замены можно прибегать к сравнению с вложенным шаблоном. Для наглядности давайте рассмотрим проблему стандартизации формата телефонного номера. Заменим все знаки пунктуации точками. Наше решение показано в листинге 7.
Листинг 7. Замена знаков пунктуации точками
Сопоставление с шаблоном и, в случае совпадения, перевод в стандартный телефонный номер выполняется за один шаг.
Берете две сетевые карточки (желательно взять все таки одинаковые, раз вы еще не имеете достаточного опыта, и делате все впервые), вставляете их в компьютеры, и соединяете их кабелем Category 5. Для начала советую взять хаб (или свич), и воткнуть в него по, одинаково обжатому, кабелю от каждого из компьютеров. Так проще определить "на глаз" есть ли линк. (Т.к. у всех хабов и свичей есть лампочки, чего нельзя сказать о сетевушках, особенно дешевых).
Согласно RFC1918 для частных сетей зарезервированы три следующих диапазона адресов:
Первый блок адресов называется "24-х битный блок", второй - "20-ти битный блок" и третий - "16-ти битный блок". Первый блок адресов представляет из себя сеть класса A, второй - это 16 сетей класса B и третий - 256 сетей класса C (именно этот блок нас больше всего и интересует...).
То, адреса какого типа вы будете выдавать, зависит только от Вас. Но, обычно, для локальных сетей принято выдавать адреса вида 192.168.x.x
Итак, в вашей сети два компьютера и Вы хотите соединить их в сеть.
Делается все очень просто!
Далее, включаем компьютеры и инсталлируем сетевушки (в идеале они сами определятся ОС).
Теперь заходим в "Сетевое окружение". Думаю найти будет не очень сложно.... писать что и где - не буду, т.к. в разных версиях Windows все находится в немного разных местах. Итак, вы попали в окно, где присутствует такая вещь, как "Протокол Интернета TCP/IP".
Сетевой карте первого компьютера назначаете IP-адрес 192.168.0.1 и Subnet Mask (Маска Подсети) 255.255.255.0.
Сетевой карте второго компьютера назначаете IP-адрес 192.168.0.2 и Subnet Mask такую же, как и у первого - 255.255.255.0
Если потом Вы захотите подключить третий компьютер, то назначьте ему IP-адрес 192.168.0.3 и такую же Subnet Mask, как и у остальных.
Соответственно IP-адрес для четвертого компьютера - 192.168.0.4 (Маска Подсети - 255.255.255.0)
Для пятого компьютера - 192.168.0.5 (255.255.255.0) и так далее...
Но, учтите, что адреса вида 192.168.x.0 и 192.168.x.255 (то есть такие как 192.168.0.0 и 192.160.0.255 или 192.168.1.0 и 192.168.1.255 и т.п.) применять нельзя.
Таким образом, у каждой сетевой карты, а следовательно и у каждого компьютера будет свой индивидуальный IP адрес.
После настройки и перезагрузки компьютеров, работу сети вы можете проверить запустив программу ping,
указав в качестве параметра IP адрес компьютера, ответ от которого вы хотите услышать.
Пример для Windows [думаю, если вы будете делать сеть под unix - вы и сами во всем разберетесь, так что примеров для unix`а не будет :) ]
(С компьютера с IP-адресом 192.168.0.1 запускаете ping)
Start => Run => command => ping...
Пуск => Выполнить => command => ping...
В появившемся окне набираете
ping 192.168.0.2, чтобы протестировать связь с компьютером с IP-адресом 192.168.0.2
Если Вы все сделали правильно, то получите ответ от компьютера до которого хотели "достучаться" и время, которое потребовалось, на получение ответа.
Результат должен быть похож на что-то такое:
Pinging 192.168.0.2 with 32 bytes of data:
Reply from 192.168.0.2: bytes=32 time<10ms TTL=32
Reply from 192.168.0.2: bytes=32 time<10ms TTL=32
Reply from 192.168.0.2: bytes=32 time<10ms TTL=32
Reply from 192.168.0.2: bytes=32 time<10ms TTL=32
Время ответа в данном случае меньше 10 миллисекунд.
Если вы получили ответ Request timed out ("Превышено время ожидания ответа"), то что-то Вы все таки сделали не так... Либо не работает сеть (т.е. вы неправильно соединили компьютеры кабелем или не так настроили сетевые карточки), либо вы ошиблись IP-адресом компьютера, который хотели "пропинговать", либо ответ шел дольше чем 750 миллисекунд (Это максимальный период времени, который ping, по умолчанию, ждет ответа от "пингуемого" компьютера).
Проверьте, правильно ли вы указали IP-адрес, все ли провода подключены, включен ли другой компьютер. В случае, если все равно ничего не получается, попробуйте заменить сетевой провод на заведомо рабочий. Например поставьте компьютеры рядом и соедините их коротким cross-over (превернутым или "нуль-хабным") кабелем для сети 10Base-T (100Base-TX).
Для увеличения времени ожидания используйте ключ -w.
Например, чтобы увеличить время ожидания до 5 секунд, надо ввести команду:
ping -w 5000 192.168.0.2
Для увеличения количества попыток есть ключ -n.
Например, чтобы опросить компьютер 100 раз, надо ввести команду:
ping -n 100 192.168.0.2
Для задания длины пакетов, которыми опрашивается компьютер используется ключ -l.
Например, чтобы опросить компьютер пакетами, размером с 1 Кбайт, надо ввести команду:
ping -l 1024 100 192.168.0.2
--------------------------------------------------------------------------------
Итак, предположим, что сеть "завелась". Давайте ее потестируем!
Пишем (с компьютера с IP-адресом 192.168.0.1):
ping -l 16384 -w 5000 -n 100 192.168.0.2
Ответ должен будет быть приблизительно такой (подождите, пока пройдут все 100 пакетов):
...
Ответ от 192.168.0.2: число байт=16384 время=3мс TTL=64
Ответ от 192.168.0.2: число байт=16384 время=3мс TTL=64
Ответ от 192.168.0.2: число байт=16384 время=3мс TTL=64
Если процент потерь меньше 5% - сетью пользоваться можно. Если больше 10% - пора искать причины. Нормальное время оборота 16-килобайтного пакета для 100-мегабитной сети - 3-8 мс. Для 10-мегабитной, соответственно - 30-80.
В этой статье описываются полезные функции и процедуры, помогающие эффективно работать с различными типами данных в системе "1С:Предприятие 7.7".
* Обработка значений
* Форматирование
* Список значений
* Таблица значений
* Таблица
* Период и дата
* Календари и праздники
* Справочники
* Документы
* Предопределённые функции
* Налоговый учёт
Обработка значений в 1С
Форматирование данных в 1С
Список значений в 1С
Таблица значений в 1С
Таблица или печатная форма в 1С
Периоды и даты в 1С
Календари и праздники в 1С
Справочники в 1С
Документы в 1С
Предопределённые функции и процедуры в 1С
Налоговый учёт и первое событие в 1С
Резюме
В статье описаны функции и процедуры, используемые в программе "1С:Предприятие 7.7" для работы со справочниками, документами, списками значений, таблицами значений и с прочими агрегатными типами данных. Образцы практического применения описанных средств Вы сможете найти в статьях "Отчёты для 1С" и "Обработки для 1С".
В данной статье рассмотрены принципы, помогающие компилятору Delphi генерировать более оптимальный с точки зрения скорости код. Если Вы не хотите вникать в подробности, в конце статьи есть «свод правил», которые рекомендуется соблюдать при написании программ.
Компилятор Delphi относится к разряду оптимизирующих. Но насколько качественно проводится оптимизация? Как «помочь» компилятору создать более быстрый код? Давайте разберемся с этим на экспериментах.
Оптимизация константных выражений
Пример 1:
С точки зрения оптимизации код можно упростить еще на этапе компиляции до
Но написанный выше листинг преобразуется в
С одной стороны компилятор не «сообразил», что значение переменной «a» можно преобразовать в константу и сложить с другой константой (которая, заметим, подставлена именно как константа) на этапе компиляции, с другой стороны был применен весьма хитрый трюк с LEA (об этом ниже). Тем не менее, код
в любом случае быстрее и короче.
Пример 2:
Скомпилированный код будет выглядеть
А ведь значение, присвоенной переменной «а» являлось константой и наш пример можно было бы переписать как:
Пример 3:
После компиляции получаем:
Т.е. компилятор преобразовал код так, как он был написан, а ведь можно было бы просто записать:
Оптимизация алгебраических выражений
Пример 4:
После компиляции эти переменные будут удалены, причем с предупреждением
Пример 5:
Код скомпилируется как есть! Таким образом мы обманули компилятор псевдо использованием переменных. Delphi не исправляет нашей «кривости», поэтому эта задача ложится исключительно на плечи программиста.
Пример 6:
Данный код можно оптимизировать до
И этого Delphi за нас не сделает.
Пример 7:
В данном примере первую строчку можно безболезненно удалить, что Delphi делать умеет.
Пример 8:
В данном случае можно избавится от одной операции умножения, присвоив значение выражения a*b временной переменной. Анализ ассемблерного листинга показывает, что компилятор именно так и поступает. Тем не менее, поменяв второе подвыражение на ((b*a)>0), компилятор принимает выражения за разные и генерирует умножение для обоих случаев, не смотря на то, что результат одинаков.
Оптимизация арифметических операций
Сложение и вычитание
Применение инструкции LEA вместо ADD позволяет производить сумму 3х операндов (двух переменных и одной константы) за один такт. Трюк заключается в том представление ближних указателей эквивалентно их фактическому значению, поэтому результат, возвращенный LEA равен сумме ее операндов. При возможности Delphi производит такую замену.
Деление
Операция деления требует гораздо больше тактов процессора, нежели умножение, поэтому замена деления на умножение может значительно ускорить работу. Существуют формулы, позволяющие выполнять такое преобразование. Тем не менее, Delphi не использует такую оптимизацию. Деление на степень двойки можно заменять сдвигом вправо на n бит, но даже в этом случае получаем следующий код:
Здесь учитывается особенность самой операции div – округление в большую сторону. Поэтому, если можно пренебрегать округлением, используйте c:=a shr 1 вместо с:=a div 2.
Умножение
Умножение на степень двойки можно заменять сдвигами битов. Delphi заменяет умножение сдвигами при умножении на 4,8,16 итд. При умножении на 2 производится суммированием переменной с собой.
Умножать на 3,5,6,7,8,10 и т. д. можно и без операции умножения – расписав выражение по формуле (a shl n)+a, где n – показатель степени двойки. Например, при умножении на 3 n=1. Delphi при возможности прибегает к этому трюку. Заметим, операнд LEA умеет умножать регистр на 2,4,8, что также при возможности используется компилятором. Например, умножение на 3 преобразуется в инструкцию
Оптимизация case of
Анализ скомпилированного кода показывает, что Delphi проводит утрамбовку дерева. Т.е. значения case сортируются и выбор нужного элемента производится при помощи двоичного поиска.
В случае, если элементы case of выстраиваются в арифметической прогрессии, компилятор формирует таблицу переходов. Т.е. создается массив указателей с индексами элементов, поэтому выбор нужно элемента выполняется за одну итерацию независимо от количества элементов.
Оптимизация циклов
Разворачивание циклов – не производится. Разворачивание циклов весьма спорный момент в оптимизации, поэтому принять грамотное решение может только человек. Delphi не производит разворачивания ни больших, ни маленьких циклов.
Слияние циклов – не производится. Если два цикла, следующие друг за другом имеют одинаковые границы итерационной переменной, разумно оба цикла объединить в один.
Вынесение инвариантного кода за пределы цикла – не выносится. Наиболее распространенный недочет – условие цикла записывается как:
Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1 и потом уже сверять. Настоятельно рекомендуется переписывать подобный код как
Весь код VCL написан с нарушением этого правила. Очевидно, что проще подобного рода оптимизацию встроить в компилятор, нежели переписывать VCL :)
Замена циклов с предусловием на циклы с постусловием – производится. Циклы с постусловием имеют главное преимущество над другими видами циклов (с предусловием и с условием в середине) – они содержат всего одно ветвление. Delphi производит такую замену.
Замена инкремента на декремент – не производится. Более того, даже декрементный цикл компилируется в неоптимальный код, т.к. не используется флаг ZF. Вместо этого происходит сравнивание значения регистра с 0.
Удаление ветвлений – не производится.
Вывод:
1. Не используйте переменные для временного хранения констант или обязательно объявляйте «магические» числа как const, либо подставляйте в код непосредственные значения
2. Неиспользуемыми объявлениями и присвоениями можно безболезненно пренебрегать – Delphi умеет их вычищать.
3. Внимательно следите за использованием переменных, в частности лишним присвоениям их значений друг другу. Такого рода оптимизации Delphi делать не умеет.
4. Используйте свернутые математические выражения. (например, (3*a - a) /2 упрощается до a). Delphi не умеет упрощать математические выражения. (Да и что говорить, даже MathCAD не всегда грамотно умеет делать такие преобразования).
5. Не используйте конструкции типа a:=10*sin(45*pi/180); Delphi не вычислит эту константу на этапе компиляции, напротив, будет послушно вызывать sin и pi по ходу выполнения программы! В случае, если угол является переменной, по крайней мере pi можно заменить константой 3,1415...
6. Delphi прекрасно справляется с выражениями, полностью составленных из констант – они вычисляются на этапе компиляции.
7. Внимательно следите за условиями и их границами. Компилятор Delphi не умеет обнаруживать заведомо ложных условий. Также он не умеет удалять заведомо лишние условия. Например, (a>0) and (a<15616) and (a<>0)
8. Если в условии несколько раз проверяется одно и тоже выражение, следите, чтобы оно было выражено во всех конструкциях одинаково. В противном случае скомпилированный код будет не оптимален. Например, if ((a*b)>0) and ((a*b)<1024) then... При перестановке во втором случае b*a смысл выражения не изменится, но код будет иметь уже на одну операцию умножения, а две. Можно временно присвоить проверяемое выражение временной переменной, а затем уже проверять полученное значение.
9. Сообщение «Combining signed and unsigned types – widened both operands» сообщает не только о потенциальной ошибке – также вследствие преобразования мы теряем производительность. Например, z – объявлена как ineteger. условие if z>$abcd6123 then z:= $abcd6123; несмотря на его правильность вызовет данное предупреждение. Сгенерированный код будет, выполнять преобразования величин до 64-х бит, и дальнейшее уже сравнение 64-х битных операндов. Если изменить тип z на cardinal, мы избавимся от предупреждения и получим 3 строки кода, вместо 8 !
10. Delphi умеет оптимизировать сложение, умножение и частично деление. При делении на степень двойки, если не важно округление до большего, рекомендуется пользоваться shr 1 вместо div 2.
11. В case of при возможности используйте элементы, расположенные в арифметической прогрессии. Тем не менее, даже при невыполнении данного условия мы получим качественный код после утрамбовки дерева.
12. Выносите инвариантный код за тело цикла. Наиболее частая ошибка – for i:=1 to length(str) do... Дело в том, что при каждой итерации будет вызываться функция length, что пагубно скажется на производительности. Рекомендуется длину строки заранее присвоить переменной. Также не включайте в тело цикла код, заведомо не зависящий от изменения итерационной переменной.
Сравнивая Delphi с компиляторами Visual C++, WATCOM, Borland C++ (тестирование данных компиляторов приведено в [1]) приходим к выводу, что Delphi по своим оптимизирующим свойствам аналогичен Borland C++ (а кто сомневался? ;) ). Учитывая, что Borland C++ по итогам сравнения оказался последним, делаем несложный вывод. Весьма печален и тот факт, что большинство кода VCL написано с точки зрения «красоты» кода, а не его оптимальности с точки зрения скорости. Например, не соблюдается правило 12.
Событие - это какое-либо действие, осуществляемое пользователем либо браузером. Например, когда мы щелкаем (кликаем) на ссылке - осуществляется событие, его перехватывает специальный обработчик и перенаправляет нас на нужную страницу; при наведении курсора (указателя мыши) на ссылку в строке состояния (обычно находится в нижней части окна браузера) отображается адрес, так как обработчик "наведения курсора на объект (в данном случае на ссылку)" помещает туда этот адрес и так далее...
а). onmouseout и onmouseover
Для начала наведем курсор на кнопку "Сброс" или "Отправить" и обратим внимание на строку состояния. Вы должны увидеть там надпись с объяснением значения кнопки. Теперь отведите курсор и строка состояния очистится.
Содержимое строки состояния хранится в переменной window.status. Переменная это некоторый объект (можно представить себе ящик), значение которого (содержимое которого) можно изменять. Изменение значения осуществляется операцией присваивания (=), а выглядит она следующим образом:
имя_переменной = "значение"; (какие использовать кавычки: одинарные или двойные значения не имеет)
Поэтому для того, чтобы изменить надпись в строке состояния, нам нужно присвоить переменной window.status нужное нам значение (подсказку к кнопке), а когда курсор будет убран - присвоить пустую строку("").
Вся задача сводится к тому, чтобы определить когда пользователь наводит курсор на кнопку, а когда убирает его. Для этого нужно "сказать" обработчикам этих событий выполнить нужное нам действие. Обработчик события "наведение курсора" - onmouseover, а "отведение курсора" - onmouseout.
Очень многие тэги имеют атрибуты, начинающиеся с on (onclick, onmouseout, onfocus и т. д.). Значение этих атрибутов и есть, задача которую необходимо выполнить соответствующему обработчику. То есть для обработчика onmouseout есть атрибут onmouseout, для onmouseover - onmouseover. И в итоге мы получаем следующее определение кнопок:
Как Вы видите, атрибут onmouseover имеет в качестве значения JavaScript-код: window.status='Щелкните для отправки данных', как только курсор достигает кнопки, обработчик события mouseover - onmouseover, смотрит, что хранится в атрибуте onmouseover и выполняет необходимое действие(присвоение значения переменной window.status). Аналогично действует и обработчик события onmouseout: как только курсор уводиться от кнопки (то есть выполняется событие mouseout), то обработчик события - onmouseout выполняет код, содержащийся в атрибуте onmouseout.
То же самое и со второй кнопкой.
б). <a href="Java Scriptfunction()">Function</a>
Теперь щелкните на ссылке и откроется окошко с подсказкой. Но заметьте, что окно небольших размеров и у него нет панели инструментов - такого силами html не сделаешь! В данном примере мы имеем окно размером 300x200, без панелей инструментов, содержащее документ help.html.
Для того чтобы создать такое окошко существует следующий JavaScript-код:
где:
1. helpWindow - это имя переменной (как window.status, только в данном случае имя выбирается произвольно). Эта переменная нужна для последующей работы с окном (например, закрыть его нестандартным способом - с помощью JavaScript-сценария).
- Зачем? Не проще ли указывать просто имя открывающейся страницы, например help.html?
- А если у нас две копии одной и той же страницы? Или две страницы с одинаковым именем(например, в разных папках)? Вот для того, чтобы не перепутать страницы и свободно работать именно с тем окном с которым предполагаешь и сделанно, так чтобы "окно" присваивалось переменной, так как имя переменной можно выбрать любое.
2. window.open(что-то) - это функция. Функции выполняют определенную задачу, в данном случае window.open() открывает новое окно.
3. help.html - это страница, которую нужно открыть.
4. "" - В кавычках должно быть имя окна, но оно нам не пригодится, поэтому там нулевая строка. В следующих кавычках указываются "параметры открываемого окна": оно не должно содержать панели инструментов (toolbar=0) и должно быть шириной 300 пикселей, а высотой - 200 (width=300,height=200, соответственно).
Теперь все, что нужно это по событию "щелчка" выполнить этот код, однако есть два "но". Во-первых строка очень длинная, чтобы присвоить ее какому-либо атрибуту - будет некрасиво смотреться, да и вызывать неудобства при чтении кода страницы. Во-вторых, первоначальное предназначение ссылки - это переход на другую страницу, но нам переходить никуда не нужно, нам нужно выполнить вместо этого JavaScript-код.
Первое решается написанием функции. Как я уже говорил функция выполняет некоторую задача, причем для использования функции достаточно указать ее имя. Можно не только использовать предопределенные(функции, которые имеется в языке и без нас), но и писать их самому. Обычно функции определяются(пишутся) в разделе HEAD документа, в котором используются:
"function" значит, что дальше будет написана наша функция; help() - это имя функции (оно будет указываться вместо тела функции (между { и })).
Вторая проблема решается тоже довольно просто. Адрес страницы указывается в атрибуте href, а нам надо выполнить JavaScript код вместо перехода по ссылке:
"Java Script" означает, что дальше должен идти JavaScript-код, и что он должен выполняться вместо стандартного перехода по ссылке. В данном случае наш JavaScript-код - это созданная нами функция help().
в). onclick
Нам не остается ничего более, как закрыть окно со справкой. Для этого воспользуемся кнопкой "Закрыть". Но нас интересует то, как работает эта кнопка, а имеет она следующий вид:
Дабы закрыть окно мы сделали щелчок(Click) на кнопке "Закрыть", а щелчок это нажатие и отпускание кнопки мыши, причем и то и другое должно быть произведено на одном и том же элементе (например, на кнопке). Кликая, на кнопке мы активируем обработчик события onClick, который выполняет для нас JavaScript-код, прописанный в атрибуте onclick нашей кнопки. Помните мы открывали окно? Мы писали window.open(), а здесь тоже самое только window.close(). window - это текущее окно, а close() - закрыть.
Это одно из наиболее частоупотребляемых событий.
г). onfocus и onblur
Ну что ж будем ближе подбираться к вводу требуемой от нас информации.
Как я уже говорил: элемент получает фокус когда на нем происходит нажатие кнопки мыши, или на него осуществляется переход посредством клавиши табуляции (Tab), а теряет, когда фокус получает другой элемент. Наше поле для ввода получит фокус тогда, когда пользователь решиться ввести информацию. При этом было бы удобно, чтобы текст с подсказкой ("Введите Ваше имя") автоматически выделялся и посетитель мог его удалить одним нажатием на del, а не удалять по одному символу, или выделять текст вручную. Удобство пользователя свято. Итак, для этого мы воспользуемся событием focus. Для выделения текста используется метод select(). То есть атрибутом к полю надо прописать: onfocus="this.select();", this обозначает, что выделение должно происходить именно в этом текстовом поле (а можно прописать путь и к другому, но это не целесообразно).
А теперь попробуйте ничего не изменяя (или все удалив) убрать фокус (например, щелкнув в любом месте окна, или нажав Tab). Это событие blur, я прописал в нем выполнение функции: onblur="check();". Сама же функция имеет следующий вид (в разделе head страницы с формой между <script> и </script>):
document.forms[0].name.value - это то, что введено в текстовое поле. document - это текущий документ, forms[0] - первая форма на нашей страницы (отсчет с нуля), name = имя поля (задается атрибутом name (<input name="name">)), а value это и есть нужное нам значение (то, что введенно в текстовое поле, к которому м ы и написали путь). Введенное в поле мы присваиваем переменной val (var значит, что дальше идет имя переменной), чтобы в дальнейшем каждый раз не писать весь путь целиком.
Следующее это оператор if(если). Он выглядит следующим образом:
Мы сравниваем содержание переменной val с пустой строкой ('') и с начальной строкой ('Введите Ваше имя'). Обратите внимание, что сравнение не как в математике(с одним =), а сравниваются двума ==. || - или. Функция alert выводит окно с ошибкой (то, что в скобках - это текст ошибки). То есть:
И на всякий случай приведу целиком строку с кодом поля input:
д). onreset и onsubmit
Допустим, что пользователь заполнил форму неправильно, и он хочет очистить все поля формы одновременно: для этого он воспользуется стандартной кнопкой reset. Но что если пользователь щелкнет на ней по случайности (рука дрогнет, или в суете спутает с кнопкой submit), а форма была огромная, и он долго мучился ее заполнять..., ему будет грустно, и еще он будет долго материться. Поэтому хорошо бы у него дополнительно спросить: действительно ли он хочет очистить форму.
Вообще событие reset обрабатывается до очистки формы. А чтобы отменить очистку вообще, нам просто напросто надо вернуть обработчику события значение false, то есть прописать в соответствующем атрибуте: "return false;", а чтобы продолжить очистку: "return true;". А теперь вспомним функцию, которая выдает вопрос пользователю на подтверждение чего-либо, эта функция: "confirm('Вопрос?');". При нажатии "Ok", эта функция заменяется на true, а при нажатии "Cancel" на false. То есть все что от нас требуется, это прописать в теге <form> атрибут: onreset="return confirm('Вы действительно хотите сбросить форму?');". Тогда при нажатии "Ok" там на самом деле будет "return true;", и форма будет очищена, а если "Cancel", то "return false;" и очистка формы будет отменена.
Подобным образом действует событие submit, которое возникает при попытке отправить форму. Поскольку в нашем случае форму отправлять никуда не надо, то у меня просто написано: onsubmit="return false;". И сколько бы Вы не щелкали ничего не изменится (разве что Вы JavaScript отключите).
Обычно же обработчик onsubmit используется для верификации формы (то есть проверки на заполнение всех необходимых полей, правильность их заполнения, скажем, проверка по определенному шаблону и т. п.), для этого создается функция, которая выполняет все действия. Функция должна содержать операторы "return true" и "return false", которые позволяют, заменить функцию на true или false, соответственно, в зависимости от результатов проверки (если успешно, то true, если нет false). Но верификация данных это обширная тема, которая не может быть рассмотрена здесь. Один из примеров базовой верификации я привел, когда объяснял событие blur - подобную функцию можно использовать и здесь. Тогда <form> будет содержать атрибут: onsubmit="return function();".
Но не забывайте, что нельзя ограничиваться одной лишь проверкой языком JavaScript, ибо его поддержка может быть отключена у посетителя, и тогда все Ваши труды по защите...
е). onmousedown и onmouseup
Еще одна пара событий не нашла достойного места на странице, но я ее реализовал в виде кнопки "Button". Причем это не обычная html-кнопка, она реализована в виде двух картинок. Исходная кнопка хранится в файле с именем npressed.jpg, а нажатая - pressed.jpg. Чтобы достичь эффекта нажатия кнопки нам необходимо, чтобы при при щелчке на ней(this) значение атрибута src (путь к картинке) тега <img> менялось на pressed.jpg, а при отпускании обратно на npressed.jpg. Нажатие кнопки обрабатывает onmousedown, а отпускание - onmouseup, то есть здесь все просто:
ж). onload, onunload и onabort
Обработчик события onload активируется, когда начинает загружаться графическая часть страницы (все тексты, графика и т.п.). onload является атрибутом тега <body>. Если честно я не вижу сколько бы реального применения этому событию, зато я нашел, что можно сделать с onunload. onunload это тоже атрибут тега <body>. Событие unload происходит когда мы пытаемся покинуть данную страницу (переходим по ссылке на другую, закрываем окно браузера, обновляем страницу и т. д.). Откройте еще раз окошко справки. Допустим, что пользователь прочитал справку, и хочет отправить форму, или уйдет с нашего сайта, но при этом он забыл закрыть это маленькое окошко с подсказкой, мы на выходе закроем его сами. А для этого тег <body> (у начального документа) у нас будет выглядеть следующим образом:
Вы должны бы помнить, что helpWindow это имя нашего окна (ведь именно этой переменной мы присваивали его открытие: helpWindow = window.open("help.html", "", "toolbar=0,width=300,height=200")), а метод close() закрывает это окно. Некоторые нехорошие люди используют это событие, чтобы когда посетители уходили с его страницы, появлялись какие-либо окна, так называемые pop-up.
onabort - атрибут тега <img>. Когда пользователь отменяет загрузку изображения происходит событие abort. Но отмена загрузки изображения может не входить в наши плане. И в качестве предупреждения у нас может быть написано нечто похожее на
ЗАКЛЮЧЕНИЕ
Есть некоторые события о которых я здесь нарочно не упомянул, потому что не нашел им достойного применения, но Вам они возможно пригодятся, поэтому я уделю им немного внимания.
onchange - обработчик события, который активируется, когда Вы изменяете содержимое текстового элемента или текстовой области (<TEXTAREA>) (например, когда Вы вводите или удаляете очередную букву какого-либо текстового поля).
onselect - обработчик события выбора текста. То есть это событие происходит, когда пользователь пытается выделить текст в текстовом элементе или текстовой области.
onerror - обработчик события error, которое возникает при ошибке загрузки документа или изображения (то есть onerror это атрибут тегов <body> и <img>). Оно возникает при синтаксической ошибке JavaScript-кода (но Вы ведь не будете специально делать в нем ошибки), либо ошибкой времени выполнения (например, если Ваш скрипт выполняет какие-либо вычисления и у Вас по ошибке получится так, что некое число будет делиться на ноль, а это недопустимо - это и есть одна из ошибок времени выполнения).
Так же не забывайте, что я привел лишь по одному примеру из десятков возможных на каждое событие. Здесь главное Ваша фантазия и навыки. Например, события mouseout, mouseover, mouseup, mousedown часто используются для создания выпадающих меню и других визуальных эффектов, но это весьма трудный материал, который требует более глубоких знаний, причем не одного JavaScript.
В практической работе приходится проводить импорт данных в "1С:Предприятие" из различных внешних приложений, обслуживающих базы данных. Встроенный язык программы "1С:Предприятие" и стандартизированные методы построения баз данных позволяют создавать универсальные обработки по импорту данных из внешних источников. Написание универсальной обработки, зачастую, - это сложный, но и достаточно увлекательный процесс. Сложным я могу назвать его потому, что на этапе разработки нужно заранее предусмотреть все возможные варианты импортирования и обработки данных. А увлекательным процесс написания универсальных обработок по импорту данных в 1С мне видится потому, что постоянно ощущаешь огромные выгоды от использования обработки в последующей работе.
Файловая система
Для начала давайте посмотрим, как проводится работа с файловой системой из программы 1С:Предприятие.
Импорт данных в 1С из MS Excel
Программу Microsoft Excel сегодня можно называть стандартом работы с электронными таблицами. Именно поэтому довольно часто приходится встречаться с ситуациями, когда массивы данных хранятся именно в файлах формата MS Excel. Для того, чтобы умело импортировать данные из файлов формата MS Excel в базу данных 1С:Предприятия, вы можете воспользоваться следующими примерами.
Импорт данных в 1С из текстового файла.
Основными преимуществами текстовых файлов являются их маленький размер и простота хранения данных внутри файла. Пожалуй, именно поэтому многие данные до сего дня передаются посредством текстовых файлов. Учитывая существующую потребность, вам могут потребоваться умения в импортировании данных в 1С из текстового файла. И нижеприведённые примеры помогут вам в приобретении необходимых знаний.
Самым распространённым случаем передачи данных текстовым файлом является способ выгрузки данных из системы Клиент-Банк. В примере, приведённом ниже, вы можете увидеть, каким образом производится загрузка данных о банковских операциях в 1С из текстового файла, в который предварительно выгружены данные из системы Банк-Клиент.
Импорт данных из файла формата DBF
Старый добрый формат dBase по-прежнему используется для хранения массивов данных. Чего уж говорить, если обычная версия 1С:Предприятия (не SQL) сама хранит базы данных в файлах с расширением DBF? Формат файлов DBF, на мой взгляд, очень удобен для передачи данных в базу 1С, поскольку преимуществами файла формата dBase являются маленький размер и матричная система хранения данных.
Программист 1С может встретиться с необходимостью импортировать данные в 1С из файла DBF, в который экспортированы данные о банковских операциях из системы Банк-Клиент. В нижеприведённом примере вы можете увидеть пример обработки файла DBF, из которого в 1С импортируется банковская выписка.
Импорт данных из файла формата XML
С недавних пор набирает популярность новый формат передачи многомерных массивов данных, который носит название eXtensible Markup Language или XML. Возможности нового языка разметки, используемого для хранения данных, огромны настолько, что его стали использовать даже для. передачи данных в программу 1С:Предприятие. Поэтому современный специалист по платформе 1С обязан уметь импортировать данные в 1С из файла, имеющего формат XML.
//процедура формирует печатную таблицу с данными, содержащимися в файле формата XML
Процедура Сформировать()
Перем Файл,Каталог;
Если ФС.ВыбратьФайл(0,Файл,Каталог,"Открытие документа XML","Документы XML|*.xml")=0 Тогда
Возврат;
КонецЕсли;
//таблица
Т=СоздатьОбъект("Таблица");
Т.ИсходнаяТаблица("");
Т.ВывестиСекцию("Шапка");
//документ
Анализатор=СоздатьОбъект("AddIn.XMLParser");
Д=Анализатор.СоздатьДокумент();
Д.Загрузить(Каталог+Файл);
//узлы
Узел=Д.ВыбратьУзел("Данные");
Для i=1 По Узел.КоличествоПодчиненных() Цикл
ТекУзел=Узел.ПолучитьПодчиненныйПоНомеру(i);
Состояние(ТекУзел.Текст);
//строка
тЭлемент=ТекУзел.Наименование;
тЗначение=ТекУзел.Значение;
Т.ВывестиСекцию("Строка");
КонецЦикла;
//таблица
Т.Опции(1,1,1,0);
Т.ПовторятьПриПечатиСтроки(1,1);
Т.ТолькоПросмотр(1);
Т.Показать("XML");
КонецПроцедуры
Ну вот собственно и все. Надеюсь данная статья оказалась вам полезна.
В статье продемонстрированы программные методы экспортирования данных из программы "1С:Предприятие 7.7".
Экспорт данных из 1С в Текстовой файл TXT, CSV
Экспорт данных из 1С в файл dBase формата DBF
Экспорт данных из 1С на лист MS Excel
Управление MS Word из 1С
Методы работы с MS Word через OLE активно использованы в конфигурации "Договоры". Для определения числового кода текстовых констант MS Word использована обработка "Константы VBA".
В этой статье описываются полезные функции и процедуры, помогающие эффективно работать с различными типами данных в системе "1С:Предприятие 7.7".
Форматирование данных в 1С
Список значений в 1С
Таблица значений в 1С
Таблица или печатная форма в 1С
Периоды и даты в 1С
Календари и праздники в 1С
[pagebreak]
Справочники в 1С
Документы в 1С
Предопределённые функции и процедуры в 1С
Налоговый учёт и первое событие в 1С
Резюме
В статье описаны функции и процедуры, используемые в программе "1С:Предприятие 7.7" для работы со справочниками, документами, списками значений, таблицами значений и с прочими агрегатными типами данных. Образцы практического применения описанных средств Вы сможете найти в статьях "Отчёты для 1С" и "Обработки для 1С".
Конечно же вы попадали в такую ситуацию, когда приложение, разработанное вами ранее, могло быть снова использовано в рамках другого проекта. Вначале вы конечно же подумали, что это не создаст никаких проблем. Всего-то необходимо скопировать код из одного каталога в другой! Со временем вы осознали, что проекты могут различаться между собой различными параметрами, пусть даже самыми незначительными. Например, это может быть e-mail адрес на который отсылаются сообщения. В таком случае вам ничего не остается, как открыть множество файлов в редакторе и изменить их содержимое, вставляя нужный e-mail при помощи функции найти/заменить. Эта статья расскажет вам о том, как можно избавить себя от подобной работы, а так же порекомендует ряд дополнительных средств для создания и чтения конфигурационных файлов.
Повторное использование кода
Компьютер был изобретен для того, чтобы избавить человека от лишней работы. Развитие компьютерных технологий привело к тому, что человек стал стремиться все меньше времени проводить за компьютером. Допустим, вы программист. Не будь компьютера, вы бы остались без работы. Но в то же время вы стараетесь с помощью компьютера упростить свою ежедневную работы, с этой целью вы используете, например, функцию автозавершения кода в редакторе. Мы хотим подвести вас к той мысли, что код созданный вами, должен быть организован так, чтобы работы по его модификации были сведены к минимуму. Чаще всего это удается, когда вы создаете код, автоматизирующий рутинные операции, такие как создание и прорисовка формы, а так же отправка e-mail. Однако не стоит забывать, что функции для выполнения рутинных операций никогда не бывают на 100% идентичными в различных приложениях. Один формуляр не похож на другой, а сообщения электронной почты предназначены разным адресатам. Однако логика на уровне приложения остается прежней, функции различаются между собой только некоторыми параметрами. Таким образом, вы должны ясно представлять свою цель – разработать код, параметры которого можно было бы определять извне.
Модульная организация
Для решения этой задачи, планируя структуру приложения, вы должны позаботиться о модульности. То есть вам необходимо поместить часто используемые функции или классы в отдельный файл, который будет подключаться через require_once. В этом случае файлы приложения не будут наполнены избыточным кодом. Допустим, вы часто осуществляете запись в лог-файл. В таком случае было бы неплохо код, выполняющий эту операцию, заключить в рамки класса или функции. Будет еще лучше, если вы воспользуетесь уже готовым классом, взятым из какой-нибудь библиотеки исходных кодов, например PEAR.
Параметры процедурального кода
После того, как вы проанализировали код, выделили повторяющиеся фрагменты, распределили их по классам и функциям, необходимо подумать о выделении необходимых параметров, значения которых будут устанавливаться извне. Если речь идет о процедуральном коде, самым простым решением является использование глобальных переменных, которые необходимо определить в отдельном файле. Это позволит в дальнейшем без проблем изменять их значения.
Листинг 1 демонстрирует функцию, которая занимается отправкой e-mail. В ее теле содержится только одна php-функция - mail(). Таким образом, мы избавляемся от необходимости каждый раз указывать получателя при отправке сообщения. Следующая переменная, которую мы определяем, обозначает префикс, предшествующий теме сообщения. Конфигурационный файл, подключаемый через require_once, мог бы выглядеть следующим образом.
Listing 1
Есть способ лучше
Даже если рассмотренный выше способ и является действенным, однако это не самое лучшее решение. По мере того как код вашего приложения будет усложняться, вырастет и число опций, тогда могут возникнуть следующие проблемы:
Глобальные переменные, которые мы используем, могут породить конфликты в пространстве имен.
В том случае, если конфигурационные файлы редактируются не программистом, а дилетантом, в системе могут возникнуть синтаксические ошибки, например из-за незакрытых кавычек.
Для того, чтобы получить доступ к различным переменным, необходимо обращаться к массиву $_GLOBALS.
Вместо php-модулей существуют другие форматы, которые могут быть легко поняты и изменены дилетантами, а так же php-скриптами. Мы имеем в виду два формата: этого широко используемые операционной системой Windows ini-файлы, а так же формат XML.
PHP уже содержит функцию parse_ini_file(), которая без проблем читает ini-файлы. Такой файл имеет очень простую структуру. Каждой опции может быть присвоено только одно значение, а в качестве оператора присваивания используется знак равенства. Конфигурационный файл из предыдущего примера выглядел бы следующим образом в ini-формате.
После считывания ini-файла, имя которого передается в качестве параметра функции parse_ini_file(), мы получаем ассоциативный массив, имеющий вид:
В листинге 2 находится функция отправки почты, основанная на ini-файлах:
Listing 2
Если вы уже прочитали документацию по функции parse_ini_file(), вы кончено же заметили, что она может принимать и второй параметр. Он необходим, если вы хотите разделить ini-файл на несколько разделов или секций. Предположим, вам необходимо сохранить несколько настроек электронной почты. Тогда ini-файл будет выглядеть следующим образом:
"
Если вы при вызове parse_ini_file() передаете true в качестве второго параметра, в этом случае php будет искать в файле секции, а затем вернет многомерный массив, в котором каждой секции (errors и contact) будет соответствовать определенный набор значений:
Особые значения в ini-файлах
При использовании ini-файлов вы должны иметь в виду, что некоторые особые значения могут быть представлены строками. Допустим, вы определяете значение опции как true или yes (без кавычек), в таком случае они автоматически конвертируются в число 1, а false или no – в пустую строку. К сожалению, при этом не генерируется никакой ошибки. Поэтому не пытайтесь использовать no для сокращенного обозначения Норвегии.
Listing 3
Безопасность
Вы должны понимать то, что если конфигурационный файл используется для хранения важных данных, например паролей, необходимо позаботиться о том, чтобы содержимое такого файла не попало в web-браузер. Простейший выход из положения заключается в том, чтобы хранить конфигурационные файлы вне корневой директории сайта, например здесь: /etc/myApp/config
Если этого сделать нельзя, в таком случае можно изменить расширение файла. Для конфигурационного файла в формате модуля php необходимо всегда выбирать расширение .php. В этом случае сервер проанализирует php-файл, а пользователь увидит пустую страницу. С ini-файлами такое не пройдет, однако сервер Apache предоставляет возможность защитить данные. Просто поместите в каталог, где хранятся ini файл с именем .htaccess В него нужно поместить следующие строки:
Теперь сервер перестанет выдавать файлы с расширением ini, а опции приложения будут скрыты от пользователей.
Другие средства
Кончено же вы не являетесь единственным разработчиком, который сталкивается с проблемой обеспечения гибкости настроек веб-приложения. Поэтому некоторые программисты уже разработали библиотеки классов, которые переводят работу с конфигурационными файлами на абстрактный уровень, а так же упрощают запись и чтение различных форматов конфигурационных файлов.
PEAR::Config
Одним из классов, который может пригодится при чтении и записи конфигурационных файлов является PEAR::Config [3]. Как и все классы PEAR, PEAR::Config инсталлируется при помощи PEAR-Installer по команде
Этот класс является многоформатным, поскольку работает с конфигурационными файлами в форматах XMIL, ini, Apach-Style (гибрид XML и ini), а также php-массивами. Достоинством данного класса является то, что API для взаимодействия со всеми форматами одинаков. Т.е. логика работы с конфигурационными файлами в формате XML ничем не отличается от логики работы с ini-файлами. Вследствие этого необходимо, чтобы все форматы имели одинаковую структуру. Конфигурационные файлы, с которыми работает PEAR::Config, состоят, как и ini-файлы из секций.
Изменим снова наш пример. Сначала мы создаем объект Config, а затем вызываем его метод parseConfig(). Поскольку метод позволяет считывать различные форматы файлов, при вызове его необходимо передавать параметр, уточняющий формат. Для конфигурационных файлов в формате ini в качестве такого параметра используется строка iniFile. После считывания файла, мы не получаем опции в виде массива, вместо этого создается объект-контейнер, который дает доступ ко всем настройкам. Хотя во многих случаях бывает желательно получить опции в форме массива. Для этого используется метод toArray(). Листинг 4 демонстрирует считывание ini-файла:
Listing 4
С первого взгляда это может показаться несколько запутанным. Однако преимущество данного подхода заключается в том, что один и тот же метод используется для чтения всех форматов файлов, поддерживаемых PEAR::Config. Измененные опции могут быть также сохранены в любом формате:
Листинг 5 содержит код, где серия опций помещается в массив, который затем сохраняется в формате XML. Если вы хотите побольше узнать о PEAR::Config необходимую информацию вы сможете найти в документации по PEAR[5] или в DevShed-Tutorial [6].
Listing 5
patConfiguration
Альтернативным классом для работы с конфигурационными файлами является patConfiguration[7], однако он предназначен исключительно для работы с файлами в формате XML. После скачивания архива, его необходимо распаковать. Сам класс находится в директории include. patConfiguration предварительно определяет Tag-Set, который затем наполняется данными. К тому же этот класс предоставляет возможность указать тип опции: целое число, число с плавающей точкой, булевское значение. Типичный конфигурационный файл, созданный patConfiguration, имеет следующую структуру:
После создания объекта класса, может быть вызван метод parseConfigFile(). Доступ к опциям осуществляется через getConfigValue(). В качестве параметра этот метод может принимать путь к нужной опции. Вернемся к нашему примеру. Допустим, мы хотим получить e-mail адрес, на который высылается сообщение об ошибке. В этом случае используется путь errors.email. Если путь не указан, тогда все параметры передаются в массив. Листинг 6 демонстрирует код, который можно использовать для считывания файлов.
patConfiguration 2.0.0
В данный момент многоформатная версия patConfiguration находится в стадии разработки. Возможно, при публикации статьи эта версия уже станет доступной. Впрочем, самую новую версию для разработчиков вы можете скачать с сайта snaps.php-tools.net/downloaden.
В этом примере вы уже заметили, что внутри тега указывается тип значения. Названия типов идентичны тем, что используются в php-функции settype(). Если тип не указан, тогда значение интерпретируется как строка. Для часто используемых опций можно определить отдельный тег.
Наряду с функцией getConfigValue, существует функция setConfigValue(), с помощью которой можно изменить значение опции. Затем конфигурационный файл может быть заново записан с помощью writeConfigFile() (см листинг 7).
patConfiguration предлагает также серию дополнительных возможностей. Например, наряду с тегами, существует возможность определять атрибуты и пространства имен (Namespace), а к тегу можно привязать внешний файл, таким образом, опции будут распределены по нескольким файлам. Кроме этого patConfiguration включает систему кэширования, благодаря которой пропадает необходимость в многократном считывании конфигурационного файла.
Дополнительную информацию вы сможете найти на PHP Application Tools-Homepage и в patConfiguration-Tutorial на DevShed [8].
Listing 7
Заключение
Забота о гибкости настроек приложения может сберечь много времени, особенно если его компоненты предполагается использовать в других проектах. Вы потратите еще меньше времени, если доверите работу с конфигурационными файлами одному из готовых классов. Выбор между PEAR::Config и patConfiguration зависит от задачи. Преимуществом PEAR::Config является поддержка различных форматов конфигурационных файлов, в то время как patConfiguration прекрасно работает с XML, так же предоставляет ряд дополнительных возможностей. Однако с появлением версии 2.0.0 этот пакет будет иметь одинаковый API для считывания ini и wddx файлов. PHP-массив поддерживаются уже в текущей версии.
Жесткие диски (винчестеры), как электромеханические устройства, являются одним из самых ненадежных компонентов современного компьютера. Несмотря на то, что в большинстве случаев срок службы последних соизмерим, и даже превосходит время их эксплуатации до момента морального устаревания и замены более новыми моделями, все же отдельные экземпляры выходят из строя в течение первых месяцев эксплуатации. Выход жесткого диска из строя - самое худшее, что может случиться с вашим компьютером, так как при этом часто необратимо теряются накопленные на нем данные. Если резервная копия по какой-то причине отсутствует, то суммарный ущерб от поломки заметно превышает номинальную стоимость современных винчестеров.
Многие фирмы, пользуясь ситуацией, предлагают свои услуги по восстановлению информации с вышедшего из строя накопителя. Очевидно, это обходится недешево и целесообразно только тогда, когда на диске находилось что-то действительно ценное. В противном случае легче просто смириться с потерей.
Ремонт жестких дисков требует специального оборудования и практически невозможен в домашних условиях. Так, например, для вскрытия контейнера необходима особо чистая от пыли комната. Казалось бы, положение безнадежно и нечего даже помышлять о восстановлении поломанного диска в домашних условиях. Но, к счастью, не все поломки настолько серьезны, и во многих случаях можно обойтись для ремонта подручными (а иногда чисто программными) средствами.
Один из самых частых отказов винчестеров фирмы western digital (а также и некоторых других) выглядит следующим образом: жесткий диск не опознается bios, а головки при этом отчетливо стучат. Скорее всего, по какой-то причине не работает блок термокалибровки, и устройство не может обеспечить нужный зазор между головкой и рабочей поверхностью "блина". Обычно это происходит при отклонении от нормального температурного режима эксплуатации, например, в зимнее время, когда жесткие диски в плохо отапливаемых помещениях "выстывают" за ночь (при температуре 18...210С жесткий диск часто может исправно функционировать и с испорченным механизмом термокалибровки). Попробуйте дать поработать винчестеру в течение нескольких часов, чтобы он прогрелся, при этом рано или поздно винчестер попадает в необходимый диапазон температур и работоспособность (возможно, временно) восстанавливается. Разумеется, первым делом нужно скопировать всю информацию, поскольку работоспособность такого диска уже не гарантируется. То же можно рекомендовать и в отношении устаревших моделей без термокалибровки; часто они оказываются зависимыми от температурного режима, и с ростом износа винчестера эта зависимость проявляется все сильнее.
Вторым по распространенности отказом является выход из строя модуля диагностики при полной исправности остальных компонентов. Как это ни покажется парадоксальным, но полностью рабочий винчестер не проходит диагностику. При этом в регистре ошибок (порт ox1f1 для первого жесткого диска) могут содержаться значения, приведенные ниже:
Диагностические ошибки
Бит Содержимое Источник ошибки
7 0 Ошибка master диска
1 Ошибка slave диска
2-0 011 Ошибка секторного буфера
100 Ошибка контрогльной суммы, не устранимая избыточным кодированием
101 Ошибка микроконтроллера
Разные biosы могут различно реагировать на такую ситуацию, но все варианты сводятся к одному - жесткий диск не определяется и не "чувствуется". Однако на уровне портов ввода/вывода устройство функционирует отлично. Заметим, что существуют такие материнские платы (особенно среди новых моделей), которые, обнаружив ошибку микроконтроллера винчестера, просто отключают питание жесткого диска. Несложно написать для испорченного таким образом винчестера драйвер, который обеспечит работу с диском через высокоуровневый интерфейс int 0x13. Например, следующая процедура обеспечивает посекторное чтение и запись через порты ввода/вывода для первого жесткого диска в chs режиме.
lba mode для упрощения понимания не поддерживается. Необходимую техническую информацию обычно можно найти на сайте производителя вашего жесткого диска.
Этот фрагмент может служить вполне работоспособным ядром для драйвера 16-ти разрядного режима. Для упрощения понимания не включена задержка после каждого обращения к порту. В зависимости от соотношений скорости вашего процессора и контроллера диска эта задержка может и не потребоваться (в противном случае рекомендуется читать регистр статуса ox1f7, дожидаясь готовности контроллера). При этом не следует спешить с заменой такого жесткого диска на новый, с подобной неисправностью можно успешно работать не год и не два. Последнее, правда, лишь при условии, что все используемое программное обеспечение не будет конфликтовать с нестандартным драйвером. Писать драйвер, скорее всего, придется вам самому, поскольку не известно ни одной коммерческой разработки в этом направлении, а все любительские разработки выполнены в основном "под себя". Так, например, драйвер от kpnc hddfix3a поддерживает только винчестеры primary master до пятисот мегабайт и не работает в среде windows 95 (разработан на год раньше ее появления).
Более легкий, но не всегда осуществимый путь - запретить тестирование жестких дисков biosом или, по крайней мере, игнорировать результаты такового. Как это осуществить, можно прочесть в руководстве на материнскую плату (или обратиться за помощью к службе технической поддержки фирмы-производителя, поскольку в руководствах пользователя такие тонкости нередко опускают). Например, попробуйте установить "halt on" в "never" или перезаписать flach bios, модифицировав его так, чтобы тот не выполнял подобную проверку. Если Вам повезет, жесткий диск заработает! Однако иногда все же происходят и аппаратные отказы. Например, у винчестеров фирм samsung и conner отмечены случаи отказа модуля трансляции мультисекторного чтения/записи. Если это не будет обнаружено внутренним тестом устройства, то такой жесткий диск вызовет зависание операционной системы на стадии ее загрузки. Для предотвращения этого достаточно добавить в config.sys ключ multi-track=off и отключить аналогичные опции в blose. При этом, проиграв в скорости, все же можно заставить жесткий диск сносно работать. Понятно, что эксплуатировать восстановленный таким образом диск длительное время нерационально по причине потери быстродействия. Лучше приобрести новый, на который и скопировать всю информацию. С другой стороны, такой жесткий диск все же остается полностью рабочим и успешно может служить, например, в качестве резервного.
На том же connere эпизодически выходит из строя блок управления позиционированием головок, так что последние уже не могут удержаться на дорожке и при обращении к следующему сектору немного "уползают". При этом считывание на выходе дает ошибочную информацию, а запись необратимо затирает соседние сектора. Бороться с этим можно позиционированием головки перед каждой операцией записи/чтения, обрабатывая за один проход не более сектора. Понятно, что для этого необходимо вновь садиться за написание собственного драйвера. К счастью, он достаточно простой (можно использовать аппаратное прерывание от жесткого диска int 0x76 irq14, вставив в тело обработчика команду сброса контроллера. В данном случае подразумевается, что контроллер используемого жесткого диска проводит рекалибровку головки во время операции сброса. Некоторые модели этого не делают. В этом случае придется прибегнуть к операции позиционирования головки (функция ОхС дискового сервиса 0x13). Первые модели от вторых можно отличить временем, требуемым на сброс контроллера. Понятно, что электроника "сбрасывается" мгновенно, а позиционирование головки требует хоть и не большого, но все же заметного времени. Современные модели с поддержкой кэширования этого часто не делают или "откладывают" операции с головкой до первого к ней обращения. Разумеется, в этом случае кэширование придется выключить. Большинство bios позволяет это делать без труда, и нет нужды программировать контроллер самостоятельно. В другом случае вышедший из строя блок позиционирования (трансляции) подводит головки вовсе не к тому сектору, который запрашивался. Например, головки могли физически сместиться с оси, "уползая" в сторону. Разумеется, этот дефект можно скорректировать программно, достаточно проанализировать ситуацию и логику искажения трансляции. Многие модели позиционируют головку, используя разметку диска, что страхует от подобных поломок (к сожалению, сейчас от такого подхода большинство фирм отказались, выигрывая в скорости).
Конечно, все описанные программные подходы в действительности не устраняют неисправность, а только позволяют скопировать с казалось бы уже нерабочего винчестера ценные и еще не сохраненные данные. При этом ни к чему писать универсальный драйвер для win32 и защищенного режима. Вполне можно ограничиться dos-режимом. Для копирования файлов последнего должно оказаться вполне достаточно, конечно за исключением тех случаев, когда диск был отформатирован под ntsf или другую, не поддерживаемую ms-dos, систему. К счастью, для многих из них есть драйверы, которые позволяют "видеть" подобные разделы даже из "голой" ms-dos. В крайнем случае, можно ограничиться посекторным копированием на винчестер точно такой же топологии. При этом совершенно не имеет значения используемая файловая система и установленная операционная система.
Посекторно скопировать диск на винчестер с иной топологией трудно, но возможно. Дело в том, что многие современные контроллеры жестких дисков позволяют пользователю менять трансляцию произвольным образом. Для этого необходимо приобрести винчестер, поддерживающий lba-режим (а какой из современных жестких дисков его не поддерживает?). При этом он может быть даже большего объема, нежели исходный, но это никак не помешает копированию. Другой вопрос, что без переразбиения скопированный таким образом диск не "почувствует" дополнительных дорожек и следует запустить norton disk doctor, который устранит эту проблему.
Достаточно часто нарушается вычисление зон предком-пенсации. Дело в том, что плотность записи на разных цилиндрах не одинакова, так как линейная скорость растет от центра диска к периферии. Разумеется, гораздо легче постепенно уплотнять записи, нежели искать некий усредненный компромисс. На всех существующих моделях плотность записи изменяется скачкообразно и на последних моделях программно доступна через соответствующие регистры контроллера. При этом значения, выставленные в bios, практически любой жесткий диск (с интерфейсом ide) просто игнорирует. Предыдущие модели не имели с этим проблем, и только винчестеры, выпущенные в течение последних двух лет, склонны к подобным поломкам. Скорее, даже не к поломкам, а к сбоям, в результате которых искажается хранимая где-то в недрах жесткого диска информация. Если контроллер позволяет ее программно корректировать, то считайте, что ваш жесткий диск спасен. Конечно, придется пройти сквозь мучительные попытки угадать оригинальные значения, однако это можно делать и автоматическим перебором до тех пор, пока винчестер не начнет без ошибок читать очередную зону. Помните, что любая запись на диск способна нарушить низкоуровневую разметку винчестера, после чего последний восстановлению не подлежит и его останется только выкинуть. Производите только чтение секторов!
Если же контроллер не позволяет программно управлять предкомпенсацией, то еще не все потеряно. Попробуйте перед каждым обращением делать сброс контроллера, а точнее, его рекалибровку (команда ixh). В некоторых случаях это срабатывает, поскольку с целью оптимизации скорости обмена предкомпенсацией обычно управляет не один блок. И, кроме того, иногда контроллер кэша не учитывает предкомпенсацию, а его сброс реализует последнюю аппаратно. К сожалению, это по большей части догадки и результаты экспериментов автора, так как техническая документация фирм-производителей по этому поводу не отличается полнотой, а местами содержит противоречия. Можно испытать и другой способ - попробовать перезаписать микрокод контроллера (команда 92h). Конечно, это доступно только для специалистов очень высокого класса, но ведь доступно! Заметим, что не все контроллеры поддерживают такую операцию. С другой стороны, это и хорошо, так как уменьшает вероятность сбоя и не дает некорректно работающим программам (вирусам в том числе) испортить дорогое устройство. Жесткие диски от samsung обладают еще одной неприятной особенностью - часто при подключении шлейфа "на лету", при включенном питании, они перестают работать. Внешне это выглядит так: индикатор обращения к диску постоянно горит, но диск даже не определяется biosom, или определяется, но все равно не работает. Близкое рассмотрение показывает, что на шине пропадает сигнал готовности устройства. В остальном контроллер остается неповрежденным. Разумеется, если не обращать внимание на отсутствие сигнала готовности, то с устройством можно общаться, делая вручную необходимые задержки (поскольку физическую готовность устройства уже узнать не представляется возможным, приходится делать задержки с изрядным запасом времени). При этом, к сожалению, придется отказаться от dma-mode (а уж тем более ultra-dma) и ограничиться pio 1 (с небольшим риском - pio 2) режимом. Конечно, писать соответствующий драйвер вам придется опять самостоятельно. Разумеется, скорость обмена в режиме pio 1 по сегодняшним меркам совершенно неудовлетворительна и не годится ни для чего другого, кроме как копирования информации со старого на новый винчестер, но некоторые "нечистоплотные" продавцы компьютерной техники как-то ухитряются устанавливать подобные экземпляры на продаваемые машины. Будьте осторожны! Учитывая, что написание подобных драйверов для win32 - трудоемкое занятие, большинство ограничивается поддержкой одной лишь ms-dos, и вовсе не факт, что компьютер, демонстрирующий загрузку win95, содержит исправный, а не реанимированный подобным образом жесткий диск.
У жестких дисков фирмы samsung при подключении "налету" может появляться другой неприятный дефект - при запросах на чтение контроллер периодически "повисает" и не завершает операцию. В результате "замирает" вся операционная система (впрочем, windows nt с этим справляется, но, вероятно, не всегда). На первый взгляд может показаться, что с этого винчестера несложно скопировать ценные файлы, но при попытке выполнить это выясняется, что диск "зависает" все чаще и чаще и копирование растягивается до бесконечности. Однако если выполнить сброс контроллера, то можно будет повторить операцию. Это можно сделать аппарат -но, подпаяв одну кнопку на линию сброса и статуса. Последнее нужно для указания на ошибочную ситуацию, чтобы операционная система повторила незавершенную операцию. Если этого не сделать, то часть секторов не будет реально прочитана (записана). Или можно выполнять сброс автоматически, например, по таймеру. Чтобы не сталкиваться с подобной ситуацией, никогда не следует подсоединять/отсоединять винчестер при включенном питании. Очень часто это приводит к подобным ошибкам, хотя производители других фирм, по-видимому, как-то от этого все же защищаются, ибо аналогичной ситуации у них практически не встречается. Все же не стоит искушать судьбу... От аппаратных ошибок теперь перейдем к дефектам поверхности. Заметим сразу, что последнее встречается гораздо чаще и проявляется намного коварнее. Обычно это ситуация, в которой мало что можно предпринять. Но достичь главной цели - спасти как можно больше уцелевших данных - довольно часто удается. Возьмем такую типичную ситуацию как ошибка чтения сектора. Маловероятно, чтобы сектор был разрушен целиком. Чаще всего "сыплется" только какая-то его часть, а все остальные данные остаются неискаженными. Существуют контроллеры двух типов. Первые, обнаружив расхождение контрольной суммы считанного сектора, все же оставляют прочитанные данные в буфере и позволяют их извлечь оттуда, проигнорировав ошибку чтения. Вторые либо очищают буфер, либо просто не сбрасывают внутренний кэш, в результате чего все равно прочитать буфер невозможно. На практике обычно встречаются последние. При этом сброс кэша можно инициировать серией запросов без считывания полученных данных. Кэш при этом переполняется, и наиболее старые данные будут вытолкнуты в буфер. Остается их только прочесть. Конечно,-это крайне медленно, но, к сожалению, универсальной команды сброса кэша не существует. Разные разработчики реализуют это по-своему (впрочем, иногда это можно найти в документации на чипы, используемые в контроллере). western digital сообщает в техническом руководстве что при длинном чтении сектора без повтора контроль сектора не выполняется и он будет-таки целиком помещен в буфер. Кстати, так и должно быть по стандарту. Увы, остальные фирмы от него часто отклоняются по разным соображениям. Остается определить, какие же из прочитанных данных достоверные, а какие нет (если этого не видно "визуально" - например, в случае текстового или графического файлов)? Разумеется, в подобных рамках задача кажется неразрешимой, но это не совсем так. Дело в том, что можно произвести не только короткое, но и длинное чтение (ox22h req ploin long with retry), для чего можно использовать следующую процедуру. При этом кроме собственно данных читаются также и корректирующие коды. Автоматическая коррекция не выполняется (хотя некоторые контроллеры это реализуют аппаратно и не могут отключить автокоррекцию; в документации этот момент, кстати, не уточняется). Как правило, используются корректирующие коды Рида-Соломона, хотя последнее не обязательно. Математические законы позволяют не только определить место возникновения сбоя, но и даже восстановить несколько бит. При больших разрушениях можно определить только место сбоя, но достоверно восстановить информацию не удается.
Модуляция при записи такова, что все биты, стоящие справа от сбойного, уже не достоверны. Точнее, не все, а только в пределах одного пакета. Обычно за один раз записывается от 3 до 9 бит (необходимо уточнить у конкретного производителя) и содержимое остальных пакетов, как правило, остается достоверным. Самое интересное, что зачастую сбойный пакет можно восстановить методом перебора! При этом можно даже рассчитать, сколько вариантов должно получиться. Учитывая хорошую степень "рассеяния" корректирующих кодов можно сказать, что не очень много. И таким образом можно восстановить казалось бы безнадежно испорченные сектора, а вместе с ними и файлы, расположенные "поверх" последних.
Выше были перечислены наиболее типичные случаи отказов жестких дисков, которые поддавались чисто программному восстановлению если уж не винчестера, то хотя бы хранимых на нем данных. Разумеется, что иногда жесткий диск выходит из строя полностью (например, при неправильно подключенном питании, скачках напряжения) от вибрации или ударов, а то и просто из-за откровенного заводского брака. Есть один старый проверенный способ - найти жесткий диск такой же точно модели и заменить электронную плату. К сожалению, последнее из-за ряда конструктивных особенностей все реже и реже бывает возможно, а уж дефекты поверхности этот способ и вовсе бессилен вылечить. Поэтому, берегите свой жесткий диск и почаще проводите резервное копирование. Помните, что самое дорогое это не компьютер, а хранимая на нем информация!