Delphi весь текст word

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Сегодня, в последний рабочий день недели, практически весь день провозился над передачей данных из Delphi в Word. Так как подозрение есть, что работа продолжится то решил кое-какие моменты по работе с Microsoft Word в Delphi запечатлеть и у себя в блоге. Написать такую мини-шпаргалку (тем более, что по Excel уже кое что есть).

Для начала, немного общих моментов по работе с MS Office в Delphi. И первое, что мы сделаем — это создадим объект Word.Application. Создается этот объект абсолютно также, как и объект Excel.Application:

uses ComObj;
var Word: variant;
[...]
procedure CreateWord(const Visible: boolean);
begin
  Word:=CreateOleObject('Word.Application');
  Word.Visible:=Visible;
end;

Всё достаточно просто. Далее мы можем работать с объектом следующим образом:

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

Рассмотрим оба варианта, т.к. оба они имеют как свои плюсы, так и недостатки.

Чтобы создать новый документ необходимо выполнить метод Add у коллекции Documents, т.е.:

[...]
Word.Documents.Add
[...]

и после этой операции уже начинать работать с документам обращаясь к нему по индексу или имени в коллекции. Также, можно создать новый документ по шаблону (*.dot). Для этого необходимо выполнить тот же метод Add, но с одним входным параметром — путем к файлу-шаблону:

[...]
Word.Documents.Add(TamplatePath:string);
[...]

Чтобы получить список всех открытых в данный момент документов Word можно воспользоваться следующим листингом:

[...]
var List: TStringList;
    i: integer;
begin
  List:=TStringList.Create;
  for i:=1 to Word.Documents.Count do
    List.Add(Word.Documents.Item(i).Name);
end;
[...]

Обратите внимание, что нумерация начинается с 1, а не с нуля. Чтобы активировать любой документ из коллекции для работы, необходимо выполнить метод Activate:

Word.Documents.Item(index).Activate

где index — номер документа в коллекции.

Теперь можно приступать к записи и чтению документа. Для работы с текстов в документе Word, как и в Excel для работы с ячейками таблицы, определен объект Range. Именно методы этого объекта и дают нам возможность работы с текстом. Для начала рассмотрим работу двух основных методов: InsertBefore и InsertAfter.

Как следует из название — первый метод вставляет текст в начало содержимого Range, а второй — в конец. При этом сам объект Range может содержать как весть документ (Document) так и какую-либо его часть. Например, в следующем листинге я вставлю строку в начало документа и затем методом InsertAfter буду добавлять несколько строк текста в конец документа:

[...]
Word.ActiveDocument.Range.InsertBefore('Hello World');
Word.ActiveDocument.Range.InsertAfter('текст после Hello World');
Word.ActiveDocument.Range.InsertAfter('окончание строки в документа');
[...]

При выполнении этих трех операции Range содержал весь документ.

Если работать со всем документом неудобно, а необходимо, например выделить фрагмент с 50 по 100 символ и работать с ним, то можно воспользоваться функцией Range, которая вернет нам необходимый объект Range:

var MyRange: variant;
begin
  MyRange:=WordActiveDocument.Range(50,100);
  MyRange.InsertBefore('Привет');//всё, что было после 50-го символа сдвинулось вправо
end;

Это что касается записи текста. Решение обратной задачи — чтения текста из документа ещё проще. Достаточно воспользоваться свойством Text у объекта Range:

[...]
ShowMessage(Word.ActiveDocument.Range.Text) //весь текст в документе
[...]

Также для чтения документа можно воспользоваться коллекцией документа Words (слова). За слово принимается непрерывный набор символов — цифр и букв, который оканчивается пробелом.

Перечисляются слова документа точно также как и при работе с коллекцией документов, т.е. первое слово имеет индекс 1 последнее — Word.Count.

[...]
  ShowMessage(Word.ActiveDocument.Words.Item(Word.ActiveDocument.Words.Count).Text)
[...]

В данном случае я вывел на экран последнее слово в документе.

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

2. Работа с документами Word в Delphi. Открытие готового документа и замена текста.

Чтобы открыть заранее заготовленный документ Word в Delphi достаточно воспользоваться методом Open у коллекции Documents, например так:

var FilePath: string;
[...]
  Word.Documents.Open(FilePath)
[...]

Метод Open можно вызывать с несколькими аргументами:

  • FileName: string — путь и имя файла;
  • ConfirmConversions: boolean — False — не открывать диалоговое окно «Преобразование файла» при открытии файла, формат которого не соответствует формату Word (doc или docx)
  • ReadOnly:boolean — True — открыть документ в режиме «Только для чтения»
  • AddToRecentFiles: boolean — True, чтобы добавить документ в список недавно открытых документов.
  • PasswordDocument: string — пароль для открытия документа
  • PasswordTemplate: string — пароль для открытия шаблона
  • Revert : boolean — True, чтобы вернуться к сохраненному документу, если этот документ открывается повторно.
  • WritePasswordDocument: string — пароль для сохранения измененного документа в файле
  • WritePasswordTemplate:string — пароль для сохранения изменений в шаблоне
  • Format:integer — формат открываемого документа.

Обязательным параметром метода Open является только FileName, остальные — могут отсутствовать. Если же Вам необходимо воспользоваться несколькими параметрами, то их необходимо явно указывать при вызове метода, например:

[...]
  Word.Documents.Open(FileName:=FilePath, ReadOnly:=true)
[...]

В этом случае документ открывается в режиме «Только для чтения». При таком способе вызова (с явным указанием аргументов) положение аргументов может быть произвольным.

Что касается последнего аргумента — Format, то он может принимать  целочисленные значения (применительно к версиям Microsoft Word 2007 и выше) от 0 до 13. При этом, для того, чтобы открыть «родные» вордовские документы (doc) достаточно использовать значения 0 или 6.

Теперь, когда документ открыт его необходимо преобразовать. Обычно я делаю следующим образом: в тех местах документа, в которые необходимо вставить текст я расставляю либо закладки, либо простые строки текста, например, обрамленные символом $ или #. И затем просто выполняю поиск и замену подстрок следующим образом:

function FindAndReplace(const FindText,ReplaceText:string):boolean;
  const wdReplaceAll = 2;
begin
  Word.Selection.Find.MatchSoundsLike := False;
  Word.Selection.Find.MatchAllWordForms := False;
  Word.Selection.Find.MatchWholeWord := False;
  Word.Selection.Find.Format := False;
  Word.Selection.Find.Forward := True;
  Word.Selection.Find.ClearFormatting;
  Word.Selection.Find.Text:=FindText;
  Word.Selection.Find.Replacement.Text:=ReplaceText;
  FindAndReplace:=Word.Selection.Find.Execute(Replace:=wdReplaceAll);
end;

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

Вот, наверное, самые-самые простые методы работы с Word в Delphi. Кстати, пишу пост и, думаю, что у кого-то из читателей может возникнуть вопрос: причём тут Delphi в Internet и Word в Delphi? :) Честно говоря, приведенный выше фрагменты кода можно использовать для нужд в Internet с натяжкой, например, при автосоставлении небольших отчётов по чему-либо. А вообще, в недалеком будущем, есть в планах поразбираться с Тезаурусом Word и попробовать составить небольшой синонимайзер для собственных нужд — он-то и пригодится нам в Internet :)

3.1
8
голоса

Рейтинг статьи

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

    msm.ru

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

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

    !
    user posted image

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

    Соблюдайте общие правила форума

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


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


    Полезные ссылки:
    user posted image MSDN Library user posted image FAQ раздела user posted image Поиск по разделу user posted image Как правильно задавать вопросы


    Выразить свое отношение к модераторам раздела можно здесь: user posted image Rouse_, user posted image Krid

    >
    Delphi и Word
    , Не могу выделить текст

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



    Сообщ.
    #1

    ,
    07.09.05, 10:23

      Народ!!!
      Пишу на Delphi программу которая по шаблону будит генерить страници в Word’е.
      Вроде все получаеться, мне осталось скопировать страницу и размножить ее(програмно естественно).
      Но я не могу выделить весь текст,что бы его потом скопировать.
      Сам вопрос: как выделить весь текст?


      Modest Wizard



      Сообщ.
      #2

      ,
      07.09.05, 10:33

        ExpandedWrap disabled

          Var Word:Variant;

          begin

              Word:=CreateOleObject(‘Word.Application’);

              Word.Documents.Open(‘c:myfile.doc’,emptyparam,emptyparam,emptyparam,emptyparam,emptyparam,emptyparam,emptyparam,emptyparam,emptyparam,1251);

              Word.Selection.WholeStory;

              Word.Visible:=true;

          end;

        Guru

        Rouse_



        Сообщ.
        #3

        ,
        07.09.05, 10:35

          Moderator

          *******

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

          ExpandedWrap disabled

            unit Unit1;

            interface

            uses

              Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

              Dialogs, ComObj, StdCtrls;

            type

              TForm1 = class(TForm)

                Button1: TButton;

                procedure Button1Click(Sender: TObject);

                procedure FormDestroy(Sender: TObject);

              private

                { Private declarations }

              public

                { Public declarations }

              end;

            var

              Form1: TForm1;

              W: Variant;

            implementation

            {$R *.dfm}

            procedure TForm1.Button1Click(Sender: TObject);

            begin

              W := CreateOleObject(‘Word.Application’);

              W.Documents.Open(FileName := ‘c:tmp.doc’, ReadOnly:=False);

              W.Visible := True;

              W.ActiveDocument.Range(Start := 10, End := 40).Select;

            end;

            procedure TForm1.FormDestroy(Sender: TObject);

            begin

              W.Quit(SaveChanges:=True);

            end;

            end.


          Kryak



          Сообщ.
          #4

          ,
          07.09.05, 11:00

            Посибки :)


            Shaden

              


            Сообщ.
            #5

            ,
            14.09.05, 03:44

              Junior

              *

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

              Извините, а как скопировать, допустим, с третьей по шестую строку текста, содержащего 10 строк и вставить скопированный текст после десятой строки?
              Спасибо заранее.

              Сообщение отредактировано: Shaden — 14.09.05, 03:45

              Master

              Krid



              Сообщ.
              #6

              ,
              23.09.05, 16:23

                Moderator

                ******

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

                Цитата Shaden @ 14.09.05, 03:44

                Извините, а как скопировать, допустим, с третьей по шестую строку текста, содержащего 10 строк и вставить скопированный текст после десятой строки?

                ExpandedWrap disabled

                  const

                   wdGoToLine=3;

                   wdGoToAbsolute=1;

                   wdLine = 5;

                   wdExtend=1;

                   wdGoToLast=-1;

                  var

                   W,Sel: Variant;

                  procedure TForm1.Button1Click(Sender: TObject);

                  begin

                    W := CreateOleObject(‘Word.Application’);

                    W.Documents.Open(FileName := ‘c:tmp.doc’, ReadOnly:=False);

                    W.Visible := True;

                    Sel:=W.Selection;

                    Sel.GoTo(What:=wdGoToLine, Which:=wdGoToAbsolute, Count:=3);

                    Sel.MoveDown(Unit:=wdLine, Count:=3, Extend:=wdExtend);

                    Sel.Copy;

                    Sel.GoTo(What:=wdGoToLine, Which:=wdGoToLast);

                    Sel.EndKey( Unit:=wdLine);

                    Sel.TypeParagraph;

                    Sel.Paste;

                    Sel:=Unassigned;

                  end;


                yurant



                Сообщ.
                #7

                ,
                11.11.05, 09:36

                  Junior

                  *

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

                  А можно ли выделить весь текст БЕЗ открытия
                  Ворда и отобразить все это, скажем, в Мемо?


                  vet



                  Сообщ.
                  #8

                  ,
                  11.11.05, 15:03

                    Full Member

                    ***

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

                    Открывать-то придется, но если его не делать видимым, то никто ничего и не узнает :)

                    ExpandedWrap disabled

                      var

                       Word: variant;

                      begin

                       Word := CreateOleObject(‘Word.Application’);

                       Word.Documents.Open(‘C:666.doc’);

                       Word.Selection.WholeStory;

                       Word.Selection.Copy;

                       Memo.PasteFromClipboard;

                       Word.Quit;

                       Word := Unassigned;

                      end;

                    Вообще-то узнает, так как будет задержка на открытие документа


                    dron-s



                    Сообщ.
                    #9

                    ,
                    30.11.05, 05:10

                      yurant
                      можно так

                      ExpandedWrap disabled

                        var

                          word:variant;

                        begin

                         Word := CreateOleObject(‘Word.Application’);

                         Word.Documents.Open(‘C:333.doc’);

                         RichEdit1.Text := word.ActiveDocument.Range.Text;

                         word.Quit;

                      это без буфера обмена…


                      valyan



                      Сообщ.
                      #10

                      ,
                      20.07.10, 16:02

                        Full Member

                        ***

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

                        Здравствуйте. Вопрос таков — в документе порядка 100 страниц, на каждой странице есть информация о человеке, эта информация — переменная величина (для каждого человека), так вот как выделить эти строки (информацию о человеке)


                        VahaC



                        Сообщ.
                        #11

                        ,
                        20.07.10, 16:13

                          Имхо лучше всего делать вот так:

                          Берем весь текст из ворда и пихаем его к примеру в RichEdit

                          Цитата dron-s @ 30.11.05, 05:10

                          yurant
                          можно так

                          ExpandedWrap disabled

                            var

                              word:variant;

                            begin

                             Word := CreateOleObject(‘Word.Application’);

                             Word.Documents.Open(‘C:333.doc’);

                             RichEdit1.Text := word.ActiveDocument.Range.Text;

                             word.Quit;

                          а потом парсим текст в этом RichEdit`е с помощью Pos, PosEx и иже с ними

                          Сообщения были разделены в тему «Вставка текста в середину страницы. Word»


                          evg_reg35



                          Сообщ.
                          #12

                          ,
                          05.11.10, 17:37

                            Добрый вечер. Подскажите пожалуйста. Для удобства решил написать программу, но не получается.
                            Идея в следующем. Открыт какойто текст в Word. При прочтении текста появляется необходимость у некоторых слов изменить заливку (именно заливку, а не цвет букв). Хочется чтобы было так: читаю книгу в WORD, выделяю мышкой текст в Wordе (удерживая определенную клавишу), отпускаю мышку и заливка текста меняется.
                            Нашел в иннете что заливка текста меняется функцией WordTextBox.Fill, но чтото не работает, может нужно подключить какойто модуль в раздел uses
                            Ну хотябы пока научится по нажатию кнопки менять заливку выделенного слова в книге?

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

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

                            • Предыдущая тема
                            • Delphi: Система, Windows API
                            • Следующая тема

                            Рейтинг@Mail.ru

                            [ Script execution time: 0,0361 ]   [ 16 queries used ]   [ Generated: 13.04.23, 22:37 GMT ]  

                            Студворк — интернет-сервис помощи студентам

                            Здравствуйте, нужна помощь в решении следующей задачи:
                            Нужно скопировать достаточно большой фрагмент текста из вордовского документа А вордовский документ Б, не потеряв форматирования. В копируемых фрагментах текста присутствуют списки, но нет таблиц.
                            Должны быть определены хотябы места вставки(т.е. копирование всего документа А в определенное место в документе Б), в идеале конечно хотелось бы определить и места копирования. К примеру:
                            Найти и копировать из документа А ВСЕ что заключено между тегами [Copy][/Copy] в документ Б в место заключенное между тегами [Paste][/Paste] с заменой тэгов вставки.
                            Но в реализации я вас не ограничиваю если есть другие варианты с удовольсвием приму и их.
                            Помогите пожалуйста, заранее спасибо

                            Добавлено через 11 часов 21 минуту
                            ап, —…—

                            Добавлено через 4 минуты
                            Вот почва для размышлений: код копирует весь документ 1 в определенное место в документе 2, но единственная проблема в том что он полностью заменяет весь текст в документе 2 на весь текст из документа 1.
                            А нужно чтобы одно ключевое слово из документа 2 заменилось на весь текст документа 1…
                            Надеюсь вы меня поняли

                            Pascal
                            1
                            2
                            3
                            4
                            5
                            6
                            7
                            8
                            9
                            10
                            11
                            12
                            13
                            14
                            15
                            16
                            17
                            18
                            19
                            20
                            21
                            22
                            23
                            24
                            25
                            26
                            27
                            28
                            
                            Function FindAndReplace(ADocument1, ADocument2:TFileName; Str:string):boolean;
                              Var WordDoc: OleVariant;
                              const wdReplaceAll=1;
                              Begin
                               WordDoc:=CreateOLEObject('Word.Application');
                               WordDoc.Visible:=false;
                               WordDoc.Documents.Add(ADocument1);
                               WordDoc.Documents.Add(ADocument2);
                               WordDoc.Documents.Item(2).Activate;
                              WordDoc.Selection.Find.MatchSoundsLike := False;
                              WordDoc.Selection.Find.MatchAllWordForms := False;
                              WordDoc.Selection.Find.MatchWholeWord := False;
                              WordDoc.Selection.Find.Format := False;
                              WordDoc.Selection.Find.Forward := True;
                              WordDoc.Selection.Find.ClearFormatting;
                              WordDoc.Selection.Find.Text:=Str;
                              WordDoc.Selection.Find.Replacement.Text:=WordDoc.Documents.Item(1).Range.Text;
                              FindAndReplace:=WordDoc.Selection.Find.Execute(Replace:=wdReplaceAll);
                              WordDoc.ActiveDocument.SaveAs(ADocument2);
                               WordDoc.Quit;
                               WordDoc:=Unassigned;
                              End;
                            procedure TForm1.Button1Click(Sender: TObject);
                            begin
                            FindAndReplace('C:/1.doc','C:/2.doc', '#paste');
                            end;
                             
                            end.

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

                            Текст

                            Сначала о самом простом
                            — добавлении в документ Word нужной строки текста. Поместим на форму компоненты
                            WordDocument, WordApplicationи WordParagraphFormat с
                            палитры Servers. Нас интересуют в первую очередь свойство Range
                            компонента WordDocument и свойство Selection компонента
                            WordApplication
                            . Классики утверждают, что они являются ссылкой на объекты
                            Range
                            и Selection. Range представляет из себя, проще говоря,
                            кусок текста, это может быть как весь текст документа, так и любая его часть.
                            Его пределы задаются двумя (или меньше) параметрами типа OleVariant.
                            Например:

                            var range1, range2, range3, a, b : OleVariant;
                            ...
                            range1:=WordDocument1.Range;
                            a:=5;
                            b:=15;
                            range2:=WordDocument1.Range(a,b);
                            range3:=WordDocument1.Range(a);
                            

                            Первый наш объект включает в себя весь текст документа, у второго мы ограничили
                            пределы 5-м и 15-м символами, третий представляет из себя весь последующий текст
                            документа, начиная с 5-го символа. Объект имеет несколько полезных методов,
                            например, с его помощью можем добавить текст в документ:

                            range2.InsertAfter('MS Word');
                            

                            Это
                            мы вставили текст после выделенного Range. Точно также можем вставить
                            текст и перед ним, для этого служит метод InsertBefore(). Текст,
                            заключенный в объекте Range, можем получить так:
                            WordDocument1.Range(a,b).Text; Кроме
                            того, с помощью Range можем изменить шрифт в пределах объекта. Пример:

                            a:=5;
                            b:=15;
                            WordDocument1.Range(a,b).Font.Bold:=1;
                            WordDocument1.Range(a,b).Font.Size:=14;
                            WordDocument1.Range(a,b).Font.Color:=clRed;
                            

                            Если
                            хотим отменить выделение жирным шрифтом, присваиваем 0. Аналогично можно сделать
                            шрифт курсивом, подчеркнутым — наберите WordDocument1.Range.Font., и
                            среда сама подскажет, какие могут быть варианты. Методы Select, Cut, Copy и
                            Paste работают как в обычном тексте. С помощью Paste можем на место выбранного
                            Range вставить не только строки, но и рисунок, находящийся в буфере
                            обмена.

                            WordDocument1.Range(a,b).Select;
                            WordDocument1.Range(a,b).Cut;
                            WordDocument1.Range(a,b).Copy;
                            WordDocument1.Range(a,b).Paste;
                            

                            С
                            помощью Range можем найти в документе нужную строку. Пусть в тексте содержится
                            слово «picture». Например, нам на его место надо будет вставить рисунок.

                            var a, b, vstart, vend: OleVariant;
                             j, ilengy: Integer;
                            ...
                            ilengy:=Length(WordDocument1.Range.Text);
                            for j:=0 to ilengy-8 do begin
                             a:=j;
                             b:=j+7;
                             if WordDocument1.Range(a,b).Text='picture' then begin
                             vstart:=j;
                             vend:=j+7;
                             end;
                            end;
                            WordDocument1.Range(vstart,vend).Select;
                            

                            Такая
                            процедура находит и выделяет нужный кусок текста.Теперь про Selection, представляющий из себя выделенный фрагмент
                            документа. Если выделения нет, это текущая позиция курсора в документе. С его
                            помощью можем вставить что-либо на место выделенного фрагмента, сделать
                            выравнивание, изменить шрифт. Он также имеет методы InsertAfter() и
                            InsertBefore()
                            :

                            WordApplication1.Selection.InsertAfter("text1");
                            WordApplication1.Selection.InsertBefore("text2");

                            Форматирование выделенного текста происходит аналогично Range, например:

                            WordApplication1.Selection.Font.Bold:=1;
                            WordApplication1.Selection.Font.Size:=16;
                            WordApplication1.Selection.Font.Color:=clGreen;
                            

                            Для
                            выравнивания проще воспользоваться компонентом WordParagraphFormat.
                            Сначала только нужно «подключить» его к выделенному фрагменту текста:

                            WordParagraphFormat1.ConnectTo(WordApplication1.Selection.ParagraphFormat);
                            WordParagraphFormat1.Alignment:=wdAlignParagraphCenter;
                            

                            Значения его свойства Alignment может принимать значения
                            wdAlignParagraphCenter, wdAlignParagraphLeft, wdAlignParagraphRight
                            , смысл
                            которых очевиден. Имеются и методы Cut, Copy и Paste, которые в пояснениях вряд
                            ли нуждаются:

                            WordApplication1.Selection.Cut;
                            WordApplication1.Selection.Copy;
                            WordApplication1.Selection.Paste;
                            

                            Убираем выделение с помощью метода Collapse.
                            При этом необходимо указать, в какую сторону сместится курсор, будет ли он до
                            ранее выделенного фрагмента или после:

                            var vcol: OleVariant;
                            ...
                            vcol:=wdCollapseStart;
                            WordApplication1.Selection.Collapse(vcol);
                            

                            При
                            этом выделение пропадет, а курсор займет позицию перед фрагментом текста. Если
                            присвоить переменной значение wdCollapseEnd, то курсор переместится назад. Можно
                            просто поставить в скобках «пустышку»:

                            WordApplication1.Selection.Collapse(EmptyParam);
                            

                            Тогда
                            свертывание выделения производится по умолчанию, к началу выделенного текста.
                            РисункиЛогично было бы предположить, что рисунки документа будут представлять из себя
                            коллекцию, аналогичную таблицам, и мы, обратившись к конкретной картинке, сможем
                            менять ее свойства — обтекание, размер и т.д. Однако ничего подобного в WordDocument не обнаруживается. Потому возможности управления встраиваемыми в
                            документ изображениями сильно ограничены. Простейший метод вставить в документ рисунок — по упомянутым причинам он же и
                            единственный — скопировать его в Word из буфера обмена. Предположим, рисунок у
                            нас находится в компоненте DBImage. Сначала нужно загнать его в буфер обмена:

                            Clipboard.Assign(DBImage1.Picture);
                            

                            Теперь для его вставки следует воспользоваться методом Paste объектов Range или
                            Selection: WordApplication1.Selection.Paste или WordDocument1.Range(a,b).Paste.
                            Оставить для рисунка достаточное количество пустых строк и попасть в нужное
                            место — это уже наша забота. Если он попадет посреди текста, вид будет довольно
                            противный — при такой вставке обтекание текстом рисунка происходит как-то
                            странно. Можно приготовить для отчета шаблон, где заменяем рисунком какое-либо
                            ключевое слово. О том, как найти в документе нужный текст, см. выше.
                            А
                            теперь о несколько ином способе вставки рисунка, который устраняет проблемы с
                            обтеканием и дает нам возможность перемещать его по документу, масштабировать и
                            задавать отступы между рисунком и текстом. Способ, собственно, тот же — копируем
                            из буфера обмена, но не прямо в документ, а в «рамку» — текстовую вставку. В ней
                            может находиться не только текст, но и картинка, чем и воспользуемся.
                            «Рамки» образуют коллекцию Frames, нумеруются целым индексом, пробегающим
                            значения от 1 до WordDocument1.Frames.Count. Добавим в документ рамку,
                            изменим ее размер и вставим рисунок:

                            Clipboard.Assign(DBImage1.Picture);
                            vstart:=1;
                            vend:=2;
                            WordDocument1.Frames.Add(WordDocument1.Range(vstart,vend));
                            i:=1;
                            WordDocument1.Frames.Item(i).Height:=DBImage1.Height;
                            WordDocument1.Frames.Item(i).Width:=DBImage1.Width;
                            WordDocument1.Frames.Item(i).Select;
                            WordApplication1.Selection.Paste;
                            

                            Здесь
                            для простоты предполагается, что размер DBImage равен размеру самой
                            картинки, а также что до этого рамок у нас в документе не было. Обратить
                            внимание следует на несколько моментов. Размер рамки надо задавать до того, как
                            копировать в нее рисунок. Иначе она будет иметь размер по умолчанию, под который
                            замасштабируется и наша картинка. При попытке изменить размер рамки задним
                            числом размер картинки уже не изменится. Кроме того, параметр Range при
                            добавлении рамки часто никакой роли не играет. Рамка изначально все равно
                            появится в левом верхнем углу документа, а указанный кусок текста при этом не
                            пострадает. Но это только в том случае, если он не выделен. Если в документе
                            есть выделение, рамка появится вместо выделенного фрагмента. Таким образом можем
                            ее вставить в нужное место взамен какого-то ключевого слова.
                            При желании можем ее подвигать в документе и «вручную». Для этого служат
                            свойства горизонтального и вертикального позиционирования, которые задают ее
                            отступ от левого верхнего «угла» документа:

                            i:=1;
                            WordDocument1.Frames.Item(i).VerticalPosition:=30;
                            WordDocument1.Frames.Item(i).HorizontalPosition:=50;
                            Отступ между краями рамки и текстом задается следующим образом:
                            WordDocument1.Frames.Item(i).HorizontalDistanceFromText:=10;
                            WordDocument1.Frames.Item(i).VerticalDistanceFromText:=10;
                            

                            А
                            теперь о масштабировании. Для этого достаточно длину и ширину рамки умножить на
                            одно и то же число. Например:

                            WordDocument1.Frames.Item(i).Height:=DBImage1.Height*1.5;
                            WordDocument1.Frames.Item(i).Width:=DBImage1.Width*1.5;
                            

                            При
                            этом наша картинка в полтора раза пропорционально растянется. Точно также можно
                            и уменьшить, но делить, как и множить, следует на одно число. Растягивать длину
                            и ширину по-разному у меня лично не получалось. Задавать размер опять-таки надо
                            еще до вставки рисунка. Ну и, наконец, удаление рамки:

                            WordDocument1.Frames.Item(i).Delete;
                            

                            Списки Списки в документе образуют коллекцию Lists, к отдельному списку
                            обращаемся WordDocument1.Lists.Item(i), где i целое число от 1 до
                            WordDocument1.Lists.Count
                            … на этом все. Нет методов, позволяющих не то
                            что создать новый список, а даже добавить пункт к уже существующему. Ничего
                            страшного, настоящие герои всегда идут в обход:)) Сейчас мы все же проделаем и
                            то, и другое. Все что нам понадобится — свойство Range отдельного списка, то
                            есть его текст без разделения на пункты, а также возможность его выделить:

                            WordDocument1.Lists.Item(i).Range.Select;
                            

                            Для
                            этого в любом случае потребуется заготовка. Неважно, вставлена она в общий
                            шаблонный документ или хранится в отдельном файле. Заготовку делаем так:
                            выбираем в меню Формат/Список, и сохраняем, если это отдельный шаблон списка. У
                            нас появляется пустой список без текста с одним маркером. Далее вспоминаем, как
                            мы делали списки вручную — писали текст, нажимали «Enter», появлялся новый
                            элемент списка. Теперь то же самое, только программно. Предположим, у нас уже
                            открыт документ с заготовкой, и мы хотим внести в список пункты «Item 1» и «Item
                            2»:

                            var i: Integer;
                             vcol: OleVariant;
                            ...
                            i:=1;
                            vcol:=wdCollapseEnd;
                            WordDocument1.Lists.Item(i).Range.Select;
                            WordApplication1.Selection.Collapse(vcol);
                            WordApplication1.Selection.InsertAfter('Item 1');
                            WordDocument1.Lists.Item(i).Range.Select;
                            WordApplication1.Selection.Collapse(vcol);
                            WordApplication1.Selection.InsertAfter(#13);
                            WordDocument1.Lists.Item(i).Range.Select;
                            WordApplication1.Selection.Collapse(vcol);
                            WordApplication1.Selection.InsertAfter('Item 2');
                            WordDocument1.Lists.Items(i).Range.Select;
                            WordApplication1.Selection.Copy;
                            

                            То
                            есть мы вставляем в документ текст первого пункта списка, он попадает на свое
                            место. Потом посылаем в Word символ перехода строки, он честно переходит и тем
                            самым сам создает нам второй пункт списка, куда и вставляем нужную строку. Ну и
                            так далее, нужное количество раз. Последние две строки нужны, если список
                            заготовлен в отдельном файле — после их выполнения список оказывается в буфере
                            обмена. Здесь выгода в том, что можем иметь заготовки списков разных стилей и по
                            ходу дела выбирать, какой список создать. Затем открываем документ, где должен
                            быть список, выделяем с помощью Range нужный кусок, копируем из буфера обмена
                            через WordDocument1.Range(a,b).Paste. Чтобы не испортить файл с заготовкой,
                            можем сразу после открытия пересохранить его под другим именем, а можем просто
                            выйти из него без сохранения изменений

                            var vsave: OleVariant;
                            ...
                            vsave:=wdDoNotSaveChanges;
                            WordDocument1.Close(vsave);
                            

                            Константа сохранения изменений может принимать значения

                            Символьное
                            обозначение


                            Шестнадцатеричное

                             

                            wdSaveChanges

                            $FFFFFFFF

                            wdDoNotSaveChanges

                            $00000000

                            wdPromptToSaveChanges

                            $FFFFFFFE

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

                            var i,j: Integer;
                            ...
                            i:=1;
                            j:=1;
                            WordDocument1.Lists.Item(i).ListParagraphs.Item(j).Range.Text:='Item 1';

                            Так что можно с помощью переходов строки создать нужное количество элементов, а затем их заполнить:

                            WordDocument1.Lists.Item(i).Range.Select;
                            WordApplication1.Selection.Collapse(vcol);
                            WordApplication1.Selection.InsertAfter(#13);
                            j:=1;
                            WordDocument1.Lists.Item(i).ListParagraphs.Item(j).Range.Text:='Item 1';
                            j:=2;
                            WordDocument1.Lists.Item(i).ListParagraphs.Item(j).Range.Text:='Item 2';
                            

                            Это
                            было в предположении, что у нас один элемент списка в заготовке уже есть. Ну
                            вот, в общем-то, и все про текст, списки и картинкиСтатистика документовВ
                            данном небольшом материале рассматривается вопрос подсчета статистики файлов
                            *.doc и *.rtf. Такой вопрос у меня возник, когда пришлось сделать небольшую базу
                            данных по учету документов, куда надо было заносить и статистику документа —
                            число знаков, слов и т.п. Открывать каждый раз Word, считать статистику и
                            забивать ее в форму ввода было лень, так что пришла в голову мысль это дело
                            автоматизировать. Информации по данному вопросу найти так и не удалось, так что
                            основным источником знаний служили заголовочный файл Word2000.pas и
                            справка по Visual Basic for Applications. Ну и, конечно, множество разных
                            экспериментов. Сразу
                            оговорюсь, что я не профессиональный программист, так что в тонкости интерфейсов
                            вникать не будем — сам в них не особо разбираюсь. Потому, не мудрствуя лукаво,
                            просто поместим на форме компоненты WordApplication и WordDocument
                            с палитры Servers. Для работы используются свойства и методы этих
                            компонентов. Встроенная статистика Word подсчитывает статистику обычного текста, обычных и
                            концевых сносок. Для подсчета статистики используется метод компонента
                            WordDocument ComputeStatistic()
                            . Он имеет один параметр, характеризующий,
                            что именно считать, представляющий из себя шестнадцатеричную константу.
                            Константы описаны в заголовочном файле Word2000.pas, он лежит обычно в
                            /Delphi/Ocx/Servers
                            .


                            Шестнадцатеричная

                            Символьное обозначение

                            Смысл

                            $00000000

                            wdStatisticWords

                            Количество слов

                            $00000001

                            wdStatisticLines

                            Количество строк

                            $00000002

                            wdStatisticPages

                            Количество страниц

                            $00000003

                            wdStatisticCharacters

                            Знаки без пробелов

                            $00000004

                            wdStatisticParagraphs

                            Количество разделов

                            $00000005

                            wdStatisticCharactersWithSpaces

                            Знаки с пробелами

                            Это было основное, что
                            надо знать. Ну а теперь по порядку. Поместив на форму упомянутые компоненты, видим, что свойств и методов у них
                            совсем мало. В первую очередь следует определиться с методом ConnectKind
                            компонента WordApplication. Оно может принимать различные значения, но мы
                            оставим присваемое по умолчанию значение ckRunningOrNew. Это означает, что
                            соединение происходит с уже работающим сервером, при его отсутствии запускается
                            новый. Как правило, это вполне устраивает. Первым делом откроем документ. Предварительно надо объявить переменную FileName,
                            она будет типа OleVariant, которой присвоим строку с именем файла.

                            WordApplication1.Connect;
                            WordApplication1.Documents.Open(FileName,
                            EmptyParam,EmptyParam,EmptyParam,
                            EmptyParam,EmptyParam,EmptyParam,
                            EmptyParam,EmptyParam,EmptyParam,
                            EmptyParam,EmptyParam);
                            WordDocument1.ConnectTo(WordApplication1.ActiveDocument);
                            

                            Обратите внимание на количество параметров-«пустышек».
                            Их число больше того, которое обычно
                            приводится в книжках. Ну, в моих, во всяком случае. Объясняется это тем, что
                            «книжные» функции предназначены для MS Word 97, а такая запись для работы с Word
                            2000 и Word XP.
                            «Plain Text»
                            Объявив нужное количество переменных типа LongInt (в очень большом файле или при
                            суммировании по нескольким документам в принципе может оказаться больше знаков,
                            чем пределы обычного целого типа), можем уже и приступать к подсчету. Например,
                            посчитаем число слов, знаков с пробелами и без пробелов обычного текста, а также
                            количество страниц в документе. Результаты сохраним соответственно в «длинных»
                            переменных

                            WCount:=WordDocument1.ComputeStatistics($00000000);
                            CCount:=WordDocument1.ComputeStatistics($00000003);
                            SCount:=WordDocument1.ComputeStatistics($00000005);
                            PCount:=WordDocument1.ComputeStatistics($00000002);
                            

                            Открыв нужный документ в Word’е и вызвав диалог подсчета статистики, нетрудно
                            увидеть, что значения переменных равны параметрам вордовской статистики со
                            сброшенным флажком «Учитывать все сноски».
                            Сноски
                            Сноски в документах могут быть обычные и концевые. То есть если первые
                            располагаются внизу данной страницы, то концевые — строго в конце документа.
                            Кроме того, они могут отличаться и нумерацией — автоматической или заданной
                            пользователем. Начнем с обычных сносок как с самого простого. В терминологии
                            объектной модели Word — Footnotes. Сначала надо вычислить количество самих
                            сносок:

                            ifcount:=WordDocument1.DefaultInterface.Footnotes.Count;
                            

                            Подсчет статистики текста в сноске производится так:

                            FWCount:=WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Range.ComputeStatistics($00000000);
                            

                            Здесь
                            ifoot — целое число, «нумерующее» сноску. Для того, чтобы учесть сами номера
                            сносок, сделаем так:

                            FWCount:=FWCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Reference.ComputeStatistics($00000000);
                            

                            Это
                            мы посчитали для примера количество слов в сноске с номером ifoot и ее метке —
                            при пользовательской нумерации в качестве «номера» может быть целое предложение.
                            Далее начинаем перебирать их одну за другой. При этом следует учесть, что кроме
                            статистики сносок необходимо получить и статистику их «номеров». То есть:

                            for ifoot:=1 to ifcount do begin
                             FWCount:=FWCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Range.ComputeStatistics($00000000);
                             FCCount:=FCCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Range.ComputeStatistics($00000003);
                             FSCount:=FSCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Range.ComputeStatistics($00000005);
                             FCCount:=FCCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Reference.ComputeStatistics($00000003);
                             FSCount:=FSCount+WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Reference.ComputeStatistics($00000005)+1;
                             if WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Reference.Text<>IntToStr(ifoot)
                             then begin
                             FWCount:=FWCount+
                             WordDocument1.DefaultInterface.Footnotes.Item(ifoot).Reference.ComputeStatistics($00000000);
                             end;
                            end;
                            

                            Прибавление единицы появляется оттого, что сумма статистики сносок и номеров не
                            совпадает с тем, что выдает встроенная статистика Word. Между номером сноски и
                            текстом сноски Word ставит пробел, который почему-то не учитывается. Условный
                            оператор определяет, как пронумерована данная сноска — по умолчанию или нет. В
                            последнем случае следует проверить количество слов в обозначении сноски. Такая
                            схема дает результат, совпадающий со показаниями встроенной статистики. Кроме
                            того, цикл у нас идет от 1 — так начинается нумерация сносок в MS Word, да и
                            практически всех остальных объектов тоже.Теперь перейдем к концевым сноскам. Теоретически все то же самое, только вместо
                            слова «Footnotes» пишем «Endnotes». И тут наталкиваемся на сюрприз — почему-то
                            оно считает неточно. Я в данном случае поступил так: сохраняю документ под
                            другим именем, переконвертирую концевые сноски в обычные и далее все, как
                            сказано выше. Сохранение документа:

                            WordDocument1.SaveAs(FileName, FileFormat),
                            

                            где в скобках стоят два параметра типа OleVariant — имя файла и шестнадцатеричная
                            константа, задающая формат файла. Некоторые константы:


                            Шестнадцатеричная


                            Символьное
                            обозначение


                            Смысл

                            $00000000

                            wdFormatDocument

                            Документ Word

                            $00000004

                            wdFormatDOSText

                            Простой текст

                            $00000006

                            wdFormatRTF

                            Файл RTF

                            Полный список констант
                            формата можно найти все в том же файле Word2000.pas. И еще один интересный
                            момент — если просто поставить в скобки обе константы, работать не будет.
                            Следует предварительно объявить две переменных, присвоить им соответствующие
                            значения и только потом сохранять. Ну, а
                            теперь, собственно, можем вернуться к сноскам. Конвертирование концевых сносок в
                            обычные происходит так:

                            WordDocument1.DefaultInterface.Endnotes.Convert;
                            

                            Теперь мы имеем документ, в котором содержатся только обычные сноски. С ними
                            никаких проблем не возникает, пример, как с ними работать, см. выше. Если
                            интересует статистика отдельно разных типов сносок, считаем предварительно
                            статистику обычных сносок, сохраняем ее в «буферных» переменных и считаем еще
                            раз после конвертирования. Разница даст статистику концевых сносок по
                            отдельности. Сложив статистику сносок и простого текста, получаем статистику
                            документа с учетом сносок так, как ее дает сам Word.
                            Дополнительно…
                            Тут
                            по традиции несколько покритикуем Microsoft. Как оказалось, Word показывает не
                            все, что содержится в документе. Не принимаются в расчет колонтитулы. А ведь в
                            них может содержаться изрядный кусок текста, особенно в справках, бланках и т.п.
                            Оказывается, Word их на самом деле считает, но нам не показывает. Вот и
                            посмотрим, как же его можно заставить это сделать. Колонтитулы в документе тесно связаны с несколько загадочной штукой под
                            названием «разделы» — Sections. Каждый раздел может иметь верхние и нижние
                            колонтитулы. Потому первым делом определяем количество абзацев.

                            isectct:=WordDocument1.DefaultInterface.Sections.Count;
                            

                            Здесь
                            у нас целые переменные isectct, icofct, icohct обозначают
                            соответственно количество разделов как таковых, количество нижних и верхних
                            колонтитулов данного раздела. Переменная isec служит «номером» раздела,
                            переменные icof, icoh «нумеруют» соответственно нижние и верхние
                            колонтитулы в пределах данного раздела. Количество колонтитулов в разделе
                            определяем так:

                            icofct:=WordDocument1.DefaultInterface.Sections.Item(isec).Footers.Count;
                            icohct:=WordDocument1.DefaultInterface.Sections.Item(isec).Headers.Count;
                            

                            Теперь уже можем «достать» текст из колонтитула:

                            CBWCount:=
                            WordDocument1.DefaultInterface.Sections.Item(isec).Footers.Item(icof).Range.ComputeStatistics($00000000);
                            

                            В
                            данном случае мы для примера посчитали число слов, содержащихся в нижнем
                            колонтитуле под номером icof, принадлежащем разделу под номером isec.
                            Теперь можем написать «двойной» цикл для подсчета статистики верхних и нижних
                            колонтитулов.

                            Работа со строками и символами

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

                            Символы

                            Символ — это одна единица текста. Это буква, цифра, какой-либо знак. Кодовая таблица символов состоит из 256 позиций, т.е. каждый символ имеет свой уникальный код от 0 до 255. Символ с некоторым кодом N записывают так: #N. Прямо так символы и указываются в коде программы. Так как код символа представляет собой число не более 255, то очевидно, что в памяти символ занимает 1 байт. Как известно, менее байта размерности нет. Точнее, она есть — это бит, но работать с битами в программе мы не можем: байт — минимальная единица. Просмотреть таблицу символов и их коды можно с помощью стандартной утилиты «Таблица символов», входящей в Windows (ярлык расположен в меню Пуск — Программы — Стандартные — Служебные). Но совсем скоро мы и сами напишем нечто подобное.

                            Строки

                            Строка, она же текст — это набор символов, любая их последовательность. Соответственно, один символ — это тоже строка, тоже текст. Текстовая строка имеет определённую длину. Длина строки — это количество символов, которые она содержит. Если один символ занимает 1 байт, то строка из N символов занимает соответственно N байт.

                            Есть и другие кодовые таблицы, в которых 1 символ представлен не одним байтом, а двумя. Это Юникод (Unicode). В таблице Юникода есть символы всех языков мира. К сожалению, работа с Юникодом довольно затруднена и его поддержка пока что носит лишь локальный характер. Delphi не предоставляет возможностей для работы с Юникодом. Программная часть есть, но вот визуальные элементы — формы, кнопки и т.д. не умеют отображать текст в формате Юникода. Будем надеяться, в ближайшем будущем такая поддержка появится. 2 байта также называют словом (word). Отсюда и название соответствующего числового типа данных — Word (число, занимающее в памяти 2 байта, значения от 0 до 65535). Количество «ячеек» в таблице Юникода составляет 65536 и этого вполне достаточно для хранения всех языков мира. Если вы решили, что «1 байт — 256 значений, значит 2 байта — 2*256 = 512 значений», советую вспомнить двоичную систему и принцип хранения данных в компьютере.

                            Типы данных

                            Перейдём непосредственно к программированию. Для работы с символами и строками существуют соответствующие типы данных:

                            · Char — один символ (т.е. 1 байт);

                            · String — строка символов, текст (N байт).

                            Официально строки вмещают лишь 255 символов, однако в Delphi в строку можно записать гораздо больше. Для хранения больших текстов и текстов со специальными символами существуют специальные типы данных AnsiString и WideString (последний, кстати, двухбайтовый, т.е. для Юникода).

                            Для задания текстовых значений в Pascal используются одинарные кавычки (не двойные!). Т.е. когда вы хотите присвоить строковой переменной какое-либо значение, следует сделать это так:

                            s:=’text’;

                            Символы указываются аналогично, только в кавычках присутствует один-единственный символ.

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

                            В скобках указывается максимальная длина строки.

                            Операции со строками

                            Основной операцией со строками является сложение. Подобно числам, строки можно складывать. И если в числах стулья с апельсинами складывать нельзя, то в строках — можно. Сложение строк — это просто их объединение. Пример:

                            var s: string;
                            ...
                            s:='123'+'456';
                            //s = "123456"

                            Поскольку каждая строка — это последовательность символов, каждый символ имеет свой порядковый номер. В Pascal нумерация символов в строках начинается с 1. Т.е. в строке «ABC» символ «A» — первый, «B» — второй и т.д.

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

                            var s: string; c: char; 
                            ...
                            s:='Hello!';
                            c:=s[2];
                            //c = "e"

                            Чуть позже, когда мы будем изучать массивы, станет понятно, что строка — это массив символов. Отсюда следует и форма обращения к отдельным символам.

                            Обработка строк

                            Перейдём к функциям и процедурам обработки строк.

                            Длину строки можно узнать с помощью функции Length(). Функция принимает единственный параметр — строку, а возвращает её длину. Пример:

                            var Str: String; L: Integer; 
                            { ... } 
                            Str:='Hello!'; 
                            L:=Length(Str);
                            //L = 6

                            Нахождение подстроки в строке

                            Неотъемлемой задачей является нахождение подстроки в строке. Т.е. задача формулируется так: есть строка S1. Определить, начиная с какой позиции в неё входит строка S2. Без выполнения этой операции ни одну обработку представить невозможно.

                            Итак, для такого нахождения существует функция Pos(). Функция принимает два параметра: первый — подстроку, которую нужно найти, второй — строку, в которой нужно выполнить поиск. Поиск осуществляется с учётом регистра символов. Если функция нашла вхождение подстроки в строку, возвращается номер позиции её первого вхождения. Если вхождение не найдено, функция даёт результат 0. Пример:

                            var Str1, Str2: String; P: Integer; 
                            { ... } 
                            Str1:='Hi! How do you do?'; 
                            Str2:='do'; 
                            P:=Pos(Str2, Str1);
                            //P = 9

                            Удаление части строки

                            Удалить часть строки можно процедурой Delete(). Следует обратить внимание, что это именно процедура, а не функция — она производит действия непосредственно над той переменной, которая ей передана. Итак, первый параметр — переменная строкового типа, из которой удаляется фрагмент (именно переменная! конкретное значение не задаётся, т.к. процедура не возвращает результат), второй параметр — номер символа, начиная с которого нужно удалить фрагмент, третий параметр — количество символов для удаления. Пример:

                            var Str1: String; 
                            { ... } 
                            Str1:='Hello, world!'; 
                            Delete(Str1, 6, 7);
                            // Str1 = "Hello!"

                            Следует отметить, что если длина удаляемого фрагмента окажется больше количества символов в строке, начиная с указанной позиции (т.е. «вылезем за край»), функция нормально отработает. Поэтому, если нужно удалить фрагмент из строки с какого-то символа до конца, не нужно вычислять количество этих символов. Лучшим способом будет задать длину самой этой строки.

                            Вот пример. Допустим, требуется найти в строке первую букву «a» и удалить следующую за ней часть строки. Сделаем следующим образом: позицию буквы в строке найдём функцией Pos(), а фрагмент удалим функцией Delete().

                            var Str: String; 
                            { ... } 
                            Str:='This is a test.';
                            Delete(Str,Pos('a',Str),Length(Str));

                            Попробуем подставить значения и посмотреть, что передаётся функции Delete. Первая буква «a» в строке стоит на позиции 9. Длина всей строки — 15 символов. Значит вызов функции происходит такой: Delete(Str,9,15). Видно, что от буквы «a» до конца строки всего 7 символов… Но функция сделает своё дело, не смотря на эту разницу. Результатом, конечно, будет строка «This is «. Данный пример одновременно показал и комбинирование нескольких функций.

                            Копирование (извлечение) части строки

                            Ещё одной важной задачей является копирование части строки. Например, извлечение из текста отдельных слов. Выделить фрагмент строки можно удалением лишних частей, но этот способ неудобен. Функция Copy() позволяет скопировать из строки указанную часть. Функция принимает 3 параметра: текст (строку), откуда копировать, номер символа, начиная с которого скопировать и количество символов для копирования. Результатом работы функции и будет фрагмент строки.

                            Пример: пусть требуется выделить из предложения первое слово (слова разделены пробелом). На форме разместим Edit1 (TEdit), в который будет введено предложение. Операцию будет выполнять по нажатию на кнопку. Имеем:

                            procedure TForm1.Button1Click(Sender: TObject);
                            var s,word: string;
                            begin
                              s:=Edit1.Text;
                              word:=Copy(s,1,Pos(' ',s)-1);
                              ShowMessage('Первое слово: '+word);
                            end;

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

                            Вставка подстроки в строку

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

                            procedure TForm2.Button1Click(Sender: TObject);
                            var S: String;
                            begin
                              S:='1234567890';
                              Insert('000',S,3);
                              ShowMessage(S)
                             
                            end;

                            В данном случае результатом будет строка «1200034567890».

                            Пример «посерьёзнее»

                            Примеры, приведённые выше, лишь демонстрируют принцип работы со строками с помощью функций Length(), Pos(), Delete() и Copy(). Теперь решим задачу посложнее, которая потребует комбинированного применения этих функций.

                            Задача: текст, введённый в поле Memo, разбить на слова и вывести их в ListBox по одному на строке. Слова отделяются друг от друга пробелами, точками, запятыми, восклицательными и вопросительными знаками. Помимо этого вывести общее количество слов в тексте и самое длинное из этих слов.

                            Вот уж да… Задача вовсе не простая. Во-первых, вы сразу должны догадаться, что нужно использовать циклы. Без них никак, ведь мы не знаем, какой текст будет передан программе для обработки. Во-вторых, слова отделяются разными символами — это создаёт дополнительные трудности. Что ж, пойдём по порядку.

                            Интерфейс: Memo1 (TMemo), Button1 (TButton), ListBox1 (TListBox), Label1, Label2 (TLabel).

                            Сначала перенесём введённый текст в переменную. Для того, чтобы разом взять весь текст из Memo, обратимся к свойству Lines.Text:

                            procedure TForm1.Button1Click(Sender: TObject);
                            var Text: string;
                            begin
                              Text:=Memo1.Lines.Text;
                            end;

                            Теперь перейдём к обработке. Первое, что нужно сделать — разобраться с символами-разделителями. Дело в том, что такие символы могут запросто идти подряд, ведь после запятых, точек и других знаков ставится пробел. Обойти эту трудность можно таким простым способом: все разделяющие символы заменим на какой-то один, например на запятую. Для этого пройдём все символы и сделаем необходимые замены. Чтобы определить, является ли символ разделителем, запишем все разделители в отдельную строковую переменную (константу), а затем будем искать в этой строке каждый символ функцией Pos(). Все эти замены будут производиться в переменной, чтобы оригинальный текст в Memo (т.е. на экране) не был затронут. Тем не менее, для проверки промежуточных результатов работы имеет смысл выводить обработанный текст куда-либо. Например, в другое поле Memo. Чтобы пройти все символы, воспользуемся циклом FOR, где переменная пройдёт порядковые номера всех символов, т.е. от 1 до длины строки текста:

                            procedure TForm1.Button1Click(Sender: TObject);
                            const DelSym = ' .,!?';
                            var Text: string; i: integer;
                            begin
                              Text:=Memo1.Lines.Text;
                              for i := 1 to Length(Text) do
                                if Pos(Text[i],DelSym) > 0 then
                                  Text[i]:=',';
                             
                              Memo2.Text:=Text;
                            end;

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

                            if Text[1] = ',' then
                                Delete(Text,1,1);
                            while Pos(',,',Text) > 0 do
                              Delete(Text,Pos(',,',Text),1);
                            if Text[Length(Text)] <> ',' then
                              Text:=Text+',';

                            Здесь замена произведена следующим образом: организован цикл, в котором одна из запятых удаляется, но происходит это до тех пор, пока в тексте есть две идущие подряд запятые.

                            Ну вот, теперь в тексте не осталось ничего лишнего — только слова, разделённые запятыми. Сначала добьёмся того, чтобы программа извлекла из текста первое слово. Для этого найдём первую запятую, скопируем слово от начала текста до этой запятой, после чего удалим это слово из текста вместе с запятой. Удаление делается для того, чтобы далее можно было, проделав ту же самую операцию, вырезать следующее слово.

                            var Word: string;
                            {...}
                            Word:=Copy(Text,1,Pos(',',Text)-1);
                            Delete(Text,1,Length(Word)+1);

                            Теперь в переменной Word у нас слово из текста, а в переменной Text вся остальная часть текста. Вырезанное слово теперь добавляем в ListBox, вызывая ListBox.Items.Add(строка_для_добавления).

                            Теперь нам нужно организовать такой цикл, который позволил бы вырезать из текста все слова, а не только первое. В данном случае подойдёт скорее REPEAT, чем WHILE. В качестве условия следует указать Length(Text) = 0, т.е. завершить цикл тогда, когда текст станет пустым, т.е. когда мы вырежем из него все слова.

                            repeat
                              Word:=Copy(Text,1,Pos(',',Text)-1);
                              Delete(Text,1,Length(Word)+1);
                              ListBox1.Items.Add(Word);
                            until Length(Text) = 0;

                            Итак, на данный момент имеем:

                            procedure TForm1.Button1Click(Sender: TObject);
                            const DelSym = ' .,!?';
                            var Text,Word: string; i: integer;
                            begin
                              Text:=Memo1.Lines.Text;
                              for i := 1 to Length(Text) do
                                if Pos(Text[i],DelSym) > 0 then
                                  Text[i]:=',';
                             
                              if Text[1] = ',' then
                                Delete(Text,1,1);
                              while Pos(',,',Text) > 0 do
                                Delete(Text,Pos(',,',Text),1);
                                    
                              repeat
                                Word:=Copy(Text,1,Pos(',',Text)-1);
                                Delete(Text,1,Length(Word)+1);
                                ListBox1.Items.Add(Word);
                              until Length(Text) = 0;
                            end;

                            Если вы сейчас запустите программу, то увидите, что всё отлично работает. За исключением одного момента — в ListBox в конце появились какие-то пустые строки… Возникает вопрос: откуда же они взялись? Об этом вы узнаете в следующем разделе урока, а пока давайте реализуем требуемое до конца.

                            Количество слов в тексте определить очень просто — не нужно заново ничего писать. Т.к. слова у нас занесены в ListBox, достаточно просто узнать, сколько там строк — ListBox.Items.Count.

                            Label1.Caption:='Количество слов в тексте: '+IntToStr(ListBox1.Items.Count);

                            Теперь нужно найти самое длинное из всех слов. Алгоритм нахождения максимального числа таков: принимаем в качестве максимального первое из чисел. Затем проверяем все остальные числа таким образом: если число больше того, которое сейчас записано как максимальное, делаем максимальным это число. В нашем случае нужно искать максимальную длину слова. Для этого можно добавить код в цикл вырезания слов из текста или произвести поиск после добавления всех слов в ListBox. Сделаем вторым способом: организуем цикл по строкам ListBox. Следует отметить, что строки нумеруются с нуля, а не с единицы! В отдельной переменной будем хранить самое длинное слово. Казалось бы, нужно ведь ещё хранить максимальную длину слова, чтобы было с чем сравнивать… Но не нужно заводить для этого отдельную переменную, ведь мы всегда можем узнать длину слова функцией Length(). Итак, предположим, что первое слово самое длинное…

                            var LongestWord: string;
                            {...}
                              LongestWord:=ListBox1.Items[0];
                              for i := 1 to ListBox1.Items.Count-1 do
                                if Length(ListBox1.Items[i]) > Length(LongestWord) then
                                  LongestWord:=ListBox1.Items[i];
                             
                              Label2.Caption:='Самое длинное слово: '+LongestWord+' ('+IntToStr(Length(LongestWord))+' букв)';

                            Почему цикл до ListBox.Items.Count-1, а не просто до Count, разберитесь самостоятельно :-)

                            Вот теперь всё готово!

                            procedure TForm1.Button1Click(Sender: TObject);
                            const DelSym = ' .,!?';
                            var Text,Word,LongestWord: string; i: integer;
                            begin
                              Text:=Memo1.Lines.Text;
                              for i := 1 to Length(Text) do
                                if Pos(Text[i],DelSym) > 0 then
                                  Text[i]:=',';
                             
                              if Text[1] = ',' then
                                Delete(Text,1,1);
                              while Pos(',,',Text) > 0 do
                                Delete(Text,Pos(',,',Text),1);
                             
                              Text:=AnsiReplaceText(Text,Chr(13),'');
                              Text:=AnsiReplaceText(Text,Chr(10),'');
                             
                              repeat
                                Word:=Copy(Text,1,Pos(',',Text)-1);
                                Delete(Text,1,Length(Word)+1);
                                ListBox1.Items.Add(Word);
                              until Length(Text) = 0;
                             
                              Label1.Caption:='Количество слов в тексте: '+IntToStr(ListBox1.Items.Count);
                             
                              LongestWord:=ListBox1.Items[0];
                              for i := 1 to ListBox1.Items.Count-1 do
                                if Length(ListBox1.Items[i]) > Length(LongestWord) then
                                  LongestWord:=ListBox1.Items[i];
                             
                              Label2.Caption:='Самое длинное слово: '+LongestWord+' ('+IntToStr(Length(LongestWord))+' букв)';
                            end;

                            Работа с символами

                            Собственно, работа с символами сводится к использованию двух основных функций — Ord() и Chr(). С ними мы уже встречались. Функция Ord() возвращает код указанного символа, а функция Chr() — наоборот, возвращает символ с указанным кодом.

                            Помните «Таблицу символов»? Давайте сделаем её сами!

                            Вывод осуществим в TStringGrid. Этот компонент представляет собой таблицу, где в каждой ячейке записано текстовое значение. Компонент расположен на вкладке Additional (по умолчанию следует прямо за Standard). Перво-наперво настроим нашу табличку. Нам нужны всего две колонки: в одной будем отображать код символа, а в другой — сам символ. Количество колонок задаётся в свойстве с логичным названием ColCount. Устанавливаем его равным 2. По умолчанию у StringGrid задан один фиксированный столбец и одна фиксированная строка (они отображаются серым цветом). Столбец нам не нужен, а вот строка очень кстати, поэтому ставим FixedCols = 0, а FixedRows оставляем = 1.

                            Заполнение осуществим прямо при запуске программы, т.е. не будем ставить никаких кнопок. Итак, создаём обработчик события OnCreate() формы.

                            Количество символов в кодовой таблице 256, плюс заголовок — итого 257. Зададим число строк программно (хотя можно задать и в Инспекторе Объекта):

                            procedure TForm1.FormCreate(Sender: TObject);
                            begin
                              StringGrid1.RowCount:=257;
                            end;

                            Вывод делается крайне просто — с помощью цикла. Просто проходим числа от 0 до 255 и выводим соответствующий символ. Также выводим надписи в заголовок. Доступ к ячейкам StringGrid осуществляется с помощью свойства Cells: Cells[номер_столбца,номер_строки]. В квадратных скобках указываются номера столбца и строки (начинаются с нуля). Значения текстовые.

                            procedure TForm1.FormCreate(Sender: TObject);
                            var
                              i: Integer;
                            begin
                              StringGrid1.RowCount:=257;
                              StringGrid1.Cells[0,0]:='Код';
                              StringGrid1.Cells[1,0]:='Символ';
                              for i := 0 to 255 do
                              begin
                                StringGrid1.Cells[0,i+1]:=IntToStr(i);
                                StringGrid1.Cells[1,i+1]:=Chr(i);
                              end;
                            end;

                            Запускаем, смотрим.

                            Специальные символы

                            Если вы внимательно посмотрите на нашу таблицу, то увидите, что многие символы отображаются в виде квадратиков. Нет, это не значки. Так отображаются символы, не имеющие визуального отображения. Т.е. символ, например, с кодом 13 существует, но он невидим. Эти символы используются в дополнительных целях. К примеру, символ #0 (т.е. символ с кодом 0) часто применяется для указания отсутствия символа. Существуют также строки, называемые null-terminated — это строки, заканчивающиеся символом #0. Такие строки используются в языке Си.
                            По кодам можно опознавать нажатия клавиш. К примеру, клавиша Enter имеет код 13, Escape — 27, пробел — 32, Tab — 9 и т.д.

                            Давайте добавим в нашу программу возможность узнать код любой клавиши. Для этого обработаем событие формы OnKeyPress(). Чтобы этот механизм работал, необходимо установить у формы KeyPreview = True.

                            procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
                            begin
                              ShowMessage('Код нажатой клавиши: '+IntToStr(Ord(Key)));
                            end;

                            Здесь мы выводим окошко с текстом. У события есть переменная Key, в которой хранится символ, соответствующий нажатой клавише. С помощью функции Ord() узнаём код этого символа, а затем функцией IntToStr() преобразуем это число в строку.

                            Пример «посерьёзнее» — продолжение

                            Вернёмся к нашему примеру. Пришло время выяснить, откуда в ListBox берутся пустые строки. Дело в том, что они не совсем пустые. Да, визуально они пусты, но на самом деле в каждой из них по 2 специальных символа. Это символы с кодами 13 и 10 (т.е. строка #13#10). В Windows такая последовательность этих двух не визуальных символов означает конец текущей строки и начало новой строки. Т.е. в любом файле и вообще где угодно переносы строк — это два символа. А весь текст, соответственно, остаётся непрерывной последовательностью символов. Эти символы можно (и даже нужно) использовать в случаях, когда требуется вставить перенос строки.

                            Доведём нашу программу по поиску слов до логического конца. Итак, чтобы избавиться от пустых строк, нам нужно удалить из текста символы #13 и #10. Сделать это можно с помощью цикла, по аналогии с тем, как мы делали замену двух запятых на одну:

                            while Pos(Chr(13),Text) > 0 do
                              Delete(Text,Pos(Chr(13),Text),1);
                             
                            while Pos(Chr(10),Text) > 0 do
                              Delete(Text,Pos(Chr(10),Text),1);

                            Ну вот — теперь программа полностью работоспособна!

                            Дополнительные функции для работы со строками — модуль StrUtils

                            Дополнительный модуль StrUtils.pas содержит дополнительные функции для работы со строками. Среди этих функций множество полезных. Вот краткое описание часто используемых функций:

                            PosEx(подстрока, строка, отступ) — функция, аналогичная функции Pos(), но выполняющая поиск с указанной позиции (т.е. с отступом от начала строки). К примеру, если вы хотите найти в строке второй пробел, а не первый, без этой функции вам не обойтись. Чтобы сделать поиск второго пробела вручную, нужно предварительно вырезать часть из исходной строки.

                            AnsiReplaceStr, AnsiReplaceText (строка, текст_1, текст_2) — функции выполняют замену в строке строка строки текст_1 на текст_2. Функции отличаются только тем, что первая ведёт замену с учётом регистра символов, а вторая — без него.

                            В нашей программе можно использовать эти функции для вырезания из строки символов #13 и #10 — для этого в качестве текста для замены следует указать пустую строку. Вот решение в одну строку кода:

                            Text:=AnsiReplaceText(AnsiReplaceText(Text,Chr(13),''),Chr(10),'');

                            DupeString(строка, число_повторений) — формирует строку, состоящую из строки строка путём повторения её заданное количество раз.

                            ReverseString(строка) — инвертирует строку («123» -> «321»).

                            Также следует упомянуть у функциях преобразования регистра.

                            UpperCase(строка) — преобразует строку в верхний регистр; LowerCase(строка) — преобразует строку в нижний регистр.

                            Для преобразования отдельных символов следует использовать эти же функции.

                            Подробную информацию о каждой функции можно получить, введя её название в любом месте редактора кода, установив курсор на это название (или выделив его) и нажав F1.

                            Скриншоты программ, описанных в статье

                            0018_01

                            Программа извлечения слов из текста

                            0018_02

                            Таблица символов

                            Заключение

                            Длинный получился урок. Итак, сегодня мы познакомились со строками и символами и научились с ними работать. Изученные приёмы используются практически повсеместно. Не бойтесь экспериментировать — самостоятельно повышайте свой уровень навыков программирования!

                            Понравилась статья? Поделить с друзьями:
                          • Delphi xml в word
                          • Delphi word что такое
                          • Delphi word удалить строки в таблице word
                          • Delphi word текст в ячейку
                          • Delphi word таблицы работа