Разное

Макрос на выход из тела: Руководство по препроцессору FASM | WASM

Руководство по препроцессору FASM | WASM

1. Об этом документе
Я написал это потому что вижу, как многие задают вопросы на форуме FASM, связанные с непониманием идей или особенностей препроцессора. (Я не отговариваю Вас задавать такие вопросы, непонимание чего-то — это вполне нормально, и если Ваш вопрос не чересчур сложен, кто-нибудь наверняка на него ответит).

Если Вам что-нибудь из туториала покажется непонятным, пожалуйста, напишите на форум FASM, форум WASM, автору или переводчику.

2. Общие понятия

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

Другой пример — Вы хотите имитировать инструкцию, которая на самом деле не существует.

В таком случае препроцессор может заменять её последовательностью инструкций дающих желаемый эффект.

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

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

2.2. Комментарии «;»

Подобно большинству ассемблеров, комментарии в FASM начинаются с точки с запятой «;«. Всё, что следует за этим символом до конца строки игнорируется и удаляется из исходника.

К примеру, исходный текст

Код (ASM):

  1. ; заполним 100h байтов адресуемых EDI нулями

  2. xor eax, eax    ; обнуляем eax

  3. mov ecx, 100h/4

  4. rep stosd

после препроцессора превращается в

Код (ASM):

  1. xor eax,eax

  2. mov ecx,100h/4

  3. rep stosd

ПРИМЕЧАНИЕ: ; можно рассматривать как директиву препроцессора, удаляющую текст начиная с этого символа до конца строки.

ПРИМЕЧАНИЕ: Строка, полностью состоящая из комментария не будет удалена. Она становится пустой строкой (см. пример выше). Это будет важно в дальнейшем.

2.3. Перенос строки (Line Break «\»)

Если строка выглядит слишком длинной, возможно разделить её на несколько, используя символ «\«. При обработке препроцессором следующая строка будет добавлена к текущей.

Например:

Код (ASM):

  1.  

  2. db  1, 2, 3,\

  3.     4, 5, 6,\

  4.     7, 8, 9

будет преобразовано в: Конечно, \ в составе текстовой строки или комментария не вызовет объединения строк. Внутри текстовой строки этот символ воспринимается как обычный ASCII символ (как и всё остальное заключённое между кавычками или «). Комментарии же удаляются без анализа того, что в них написано.

В строке после символа \ могут быть только пробелы или комментарии.

Ранее, я упоминал, что строка, состоящая только из комментария не удаляется, а заменяется на пустую строку. Это значит, что код, подобный этому:

Код (ASM):

  1.  

  2. db  1, 2, 3,\

  3. ;   4,5,6,\   — закомментировано

  4.     7, 8, 9

преобразуется в:

Код (ASM):

  1. db  1, 2, 3

  2.     7, 8, 9

и вызовет ошибку. Выход из положения — помещать символ \ до комментария:

Код (ASM):

  1.  

  2. db  1, 2, 3,\

  3. \;  4,5,6     — правильно закомментировано

  4.     7, 8, 9

в результате будет: как мы и хотели.
2.4. Директива INCLUDE

Синтаксис:

Код (Text):

  1. include <некая строка содержащая имя файла file_name>

Эта директива вставляет содержимое файла file_name в исходный текст. Вставленный текст, естественно, тоже будет обработан препроцессором. Имя файла (и путь к нему, если он есть) должны быть заключены в кавычки « или апострофы.
Например:

Код (ASM):

  1. include ‘file.asm’

  2. include ‘HEADERS\data.inc’

  3. include ‘..\lib\strings.asm’

  4. include ‘C:\config.sys’

Можно также использовать переменные окружения ОС, помещая их имена между символами
%
:

Код (ASM):

  1. include ‘%FASMINC%\win32a.inc’

  2. include ‘%SYSTEMROOT%\somefile.inc’

  3. include ‘%myproject%\headers\something.inc’

  4. include ‘C:\%myprojectdir%\headers\something.inc’

2.5. Strings preprocessing
You may have problem to include ‘ in string declared using ‘s or « in string declared using «s. For this reason you must place the character twice into string, in that case it won’t end string and begin next as you may think, but it will include character into string literaly.

Например:

will generate binary containing string It’s okay
.
It’s same for «.

3. Присваивания (Equates)

3.1. Директива EQU

Простейшая команда препроцессора.

Синтаксис:

Это команда говорит препроцессору, что необходимо заменить все последующие <name1> на <name2>.
Например:

Код (ASM):

  1. count   equ 10  ; это команда препроцессора

  2. mov ecx, count

преобразуется в: Ещё пример:

Код (ASM):

  1. mov eax, count

  2. count   equ 10

  3. mov ecx, count

преобразуется в:

Код (ASM):

  1. mov eax, count

  2. mov ecx,10

потому что препроцессор заменит
count
только после директивы equ.
Даже это работает: после обработки препроцессором, получим: Обратите внимание, name1 может быть любым идентификатором. Идентификатор — это всего лишь набор символов, завершаемый пробелом (space), символом табуляции (tab), концом строки (EOL), комментарием ;, символом переноса строки \ или оператором, включая операторы ассемблера и/или специальные символы вроде , или }.
name2 может быть не только единичным идентификатором, берутся все символы до конца строки. name2 может и отсутствовать, тогда name1 будет заменен на пустое место.
Например:

Код (ASM):

  1. 10  equ 11, 12, 13

  2. db  10

получим: 3.2. Директива RESTORE

Можно заставить препроцессор прекратить заменять идентификаторы, определённые директивой EQU. Это делает директива RESTORE
Синтаксис:

name1 — это идентификатор определённый ранее в директиве EQU. После этой команды name1 больше не будет заменяться на name2.
Например:

Код (ASM):

  1. mov eax, count

  2. count   equ 10

  3. mov eax, count

  4. restore count

  5. mov eax, count

получим:

Код (ASM):

  1. mov eax, count

  2. mov eax, 10

  3. mov eax, count

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

Код (ASM):

  1. mov eax, count

  2. count   equ 1

  3. mov eax, count

  4. count   equ 2

  5. mov eax, count

  6. count   equ 3

  7. mov eax, count

  8. restore count

  9. mov eax, count

  10. restore count

  11. mov eax,count

  12. restore count

  13. mov eax,count

получим:

Код (ASM):

  1. mov eax, count

  2. mov eax, 1

  3. mov eax, 2

  4. mov eax, 3

  5. mov eax, 2

  6. mov eax, 1

  7. mov eax, count

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

Код (ASM):

  1. mov eax, count

  2. restore count

  3. mov eax, count

получим:

Код (ASM):

  1. mov eax, count

  2. mov eax, count

 

Формы циклов

Формы циклов

7.8 Формы циклов

Common Lisp предоставляет некоторые конструкции для циклов. Конструкция loop предоставляет простую функциональность. Она слегка больше, чем progn, и имеет ветку для переноса управления снизу вверх. Конструкции do и do* предоставляют общую функциональность для управления на каждом цикле изменением нескольких переменных. Для специализированных циклов над элементами списка или n последовательных чисел предоставляются формы dolist и dotimes. Конструкция tagbody наиболее общая конструкция, которая внутри себя позволяет использование выражений go. (Традиционная конструкция

prog — это синтез tagbody, block и let.) Большинство конструкций циклов позволяют определённые статически нелокальные выходы (смотрите return-from и return).

7.8.1 Бесконечный цикл

Конструкция loop является наипростейшей функциональностью для итераций. Она не управляет переменными, и просто циклично выполняет своё тело.

Каждая форма form выполняется последовательно слева направо. Когда вычислена последняя форма, тогда вычисляется первая форма и так далее, в безостановочном цикле. Конструкция loop никогда не возвращает значение. Её выполнение может быть остановлено явно, с помощью return или throw, например.

loop, как и многие конструкции циклов, устанавливает неявный блок с именем nil. Таким образом, return с заданным результатом может использоваться для выхода и loop.


7.8.2 Основные формы циклов

В отличие от loop, do и do* предоставляют мощный механизм для повторных вычислений большого количества переменных.

Оператор do представляет общую функциональность цикла, с произвольным количеством «переменных-индексов». Эти переменные связываются при входе в цикл и параллельно наращиваются, как это было задано. Они могут быть использованы, как для генерации необходимых последовательных чисел (как, например, последовательные целые числа), так и для накопления результата. Когда условие окончания цикла успешно выполнилось, тогда цикл завершается с заданным значением.

В общем виде do выглядит так:

(do ((var1 init1 step1)
     (var2 init2 step2)
     …
     (varn initn stepn))
    (end-test . result)
   {declaration}*
  . tagbody)

Цикл do* выглядит также, кроме изменения имени с do на do*.

Первый элемент формы является списком нуля и более спецификаторов переменных-индексов. Каждый спецификатор является списком из имени переменной var, первоначального значения init, и форма приращения step. Если init опущен, используется первоначальное значение nil. Если step опущен, var не изменяется на итерациях цикла (но может изменяться в теле цикла с помощью формы setq).

Спецификатор переменной-индекса может также быть просто именем переменной. В этом случае переменная будет иметь первоначальное значение nil и не будет изменяться при итерациях цикла. В целях стиля, использовать просто имя переменной рекомендуется, только если перед первым использованием для неё устанавливается значение с помощью setq. Если необходимо чтобы первоначальное значение было nil, а не неопределённое, то лучше указывать это явно, если нужна ложь так: (varj nil) или если нужен пустой список так: (varj ’()).

Перед первой итерацией вычисляются все формы init, и каждая переменная var связывается с соответствующим результатом вычислений init. Используется именно связывание, а не присвоение. Когда цикл завершается, старые значения этих переменных восстанавливаются. Для do, все формы init вычисляется перед тем, как будут связаны переменные var. Таким образом все формы могут ссылаться на старые связывания этих переменных (то есть на значения, которые были видимы до начала выполнения конструкции do). Для do* вычисляется первая форма init, затем первая переменная связывается с результатом этих вычислений. Затем вычисляется вторая форма init и вторая переменная var связывается с этим значением, и так далее. В целом, форма initj может ссылаться на новые связывания vark, если k < j, иначе ссылка происходит на старое связывание.

Второй элемент конструкции цикла это список из формы предиката-выхода end-test и нуля и более форм результата result. Этот элемент напоминает подвыражение cond. В начале каждой итерации, после обработки всех переменных, вычисляется форма end-test. Если результат nil, выполняется тело формы do (или do*). Если результат не nil, последовательно вычисляются формы result, как неявный progn, и затем do возвращает управление. do возвращает результаты вычисления последней формы result. Если таких форм не быть, значением do становиться nil. Следует отметить, что аналогия с подвыражениями cond не полная, так как cond в этом случае возвращает результат формы условия.

Переменные-индексы изменяются в начале каждой непервой итерации так, как написано далее. Слева направо вычисляются все формы step, и затем результаты присваиваются переменным-индексам. Если такой формы step для переменной указано не было, то переменная и не изменяется. Для do, все формы step вычисляются перед там, как будут изменены переменные. Присваивания переменным осуществляются параллельно, как в psetq. Так как все формы step вычисляются перед тем, как будет изменена хоть одна переменных, форма step при вычислении всегда ссылается на старые значения всех переменных-индексов, даже если другие формы step были выполнены. Для do*, вычисляется первая форма step, затем полученное значение присваивается первой переменной-индексом, затем вычисляется вторая форма step, и полученное значение присваивается второй переменной, и так далее. Присваивание происходит последовательно, как в setq. И для do, и для do* после того как переменные были изменены, вычисляется end-test так, как уже было описано выше. Затем продолжаются итерации.

Если end-test формы do равен nil, тогда предикат всегда ложен. Таким образом получается «бесконечный цикл»: тело body do выполняется циклично, переменные-индексы изменяются как обычно. (Конструкция loop также является «бесконечным циклом», только без переменных-индексов.) Бесконечный цикл может быть остановлен использованием return, return-from, go на более высокий уровень или throw. Например:

(do ((j 0 (+ j 1)))
    (nil)                        ;Выполнять вечно
  (format t «~%Input ~D:» j)
  (let ((item (read)))
    (if (null item) (return)     ;Обрабатывать элементы пока не найден nil
        (format t «~&Output ~D: ~S» j (process item)))))

Оставшаяся часть do оборачивается в неявный tagbody. Теги могут использоваться внутри тела цикла do для того, чтобы затем использовать выражения go. На такие выражения go не могут использоваться в спецификаторах переменных-индексов, в предикате end-test и в формах результата result. Когда управление достигает конца тела цикла do, наступает следующая цикл итерации (начинающийся с вычисления форм step).

Неявный block с именем nil окружает всю форму do. Выражение return может использоваться в любом месте для немедленного выхода из цикла.

Формы declare могут использоваться в начала тела do. Они применяются к коду внутри тела do, для связываний переменных-индексов, для форм init, для форм step, для предиката end-test и для форм результата result.

Вот парочка примеров использования do:

(do ((i 0 (+ i 1))     ;Sets every null element of a-vector to zero
     (n (length a-vector)))
    ((= i n))
  (when (null (aref a-vector i))
    (setf (aref a-vector i) 0)))

Конструкция

(do ((x e (cdr x))
     (oldx x x))
    ((null x))
  body)

использует параллельное присваивание переменным-индексам. На первой итерации значение oldx получает значение x, которое было до входа в цикл. При выходе из цикла oldx будет содержать значение x, которое было на предыдущей итерации.

Очень часто алгоритм цикла может быть по большей части выражен в формах step и тело при этом останется пустым. Например,

(do ((x foo (cdr x))
     (y bar (cdr y))
     (z ’() (cons (f (car x) (car y)) z)))
    ((or (null x) (null y))
     (nreverse z)))

делает то же, что и (mapcar #’f foo bar). Следует отметить, что вычисление step для z использует тот факт, что переменные переприсваиваются параллельно. Тело функции пустое. Наконец, использование nreverse в форме возврата результата, переставляет элементы списка для правильного результата. Другой пример:

(defun list-reverse (list)
       (do ((x list (cdr x))
            (y ’() (cons (car x) y)))
           ((endp x) y)))

Нужно заметить, что используется endp вместо null или atom для проверки конца списка. Это даёт более надёжный алгоритм.

В качестве примера вложенных циклов, предположим что env содержит список cons-ячеек. car элемента каждой cons-ячейки является списком символов, и cdr каждой cons-ячейки является списком такой же длины с соответствующими значениями. Такая структура данных похожа на ассоциативный список, но она в отличие разделена на «кадры». Общая структура напоминает грудную клетку. Функция поиска по такой структуре может быть такой:

(defun ribcage-lookup (sym ribcage)
       (do ((r ribcage (cdr r)))
           ((null r) nil)
         (do ((s (caar r) (cdr s))
              (v (cdar r) (cdr v)))
             ((null s))
           (when (eq (car s) sym)
             (return-from ribcage-lookup (car v))))))

(Примечание, использование отступов в примере выше выделяет тела вложенных циклов.)

Цикл do может быть выражен в терминах более примитивных конструкций block, return, let, loop, tagbody и psetq:

(block nil
  (let ((var1 init1)
        (var2 init2)
        …
        (varn initn))
     {declaration}*
    (loop (when end-test (return (progn . result)))
          (tagbody . tagbody)
          (psetq var1 step1
                 var2 step2
                 …
                 varn stepn))))

do* почти то же, что и do за исключением того, что связывание и наращение переменных происходит последовательно, а не параллельно. Таким образом, в вышеприведённой конструкции, let будет заменена на let* и psetq на setq.


7.8.3 Простые формы циклов

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

И dolist и dotimes циклично выполняют тело. На каждой итерации заданная переменная связывается с элементом, которая затем может использоваться в теле. dolist использует элементы списка, и dotimes использует целые числа от 0 по n− 1, при некотором указанном положительном целом n.

Результат двух этих конструкций может быть указан с помощью необязательной формы результата. Если эта форма опущена результат равен nil.

Выражение return может быть использовано для немедленного возврата из форм dolist или dotimes, игнорируя все оставшиеся итерации, которые должны были быть выполнены. block с именем nil окружает конструкцию. Тело цикла неявно обернуто конструкцией tagbody. Таким образом тело может содержать теги и go выражения. Декларации могут быть указаны перед телом цикла.

dolist предоставляет прямой цикл по списку элементов. Сначала dolist вычисляет форму listform, которая должна вернуть список. Затем для каждого элемента вычисляется тело цикла. Данный элемент на каждой итерации связывается с переменной var. Затем вычисляется resultform (одна форма, не неявный progn). (Когда вычисляется resultform, переменная var все ещё связана, и имеет значение nil.) Если resultform опущена, то результат равен nil.

(dolist (x ’(a b c d)) (prin1 x) (princ » «)) ⇒ nil
   вывод «a b c d » (в том числе пробел в конце)

Для завершения цикла и возврата заданного значения может использоваться явное выражение return.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


dotimes предоставляет цикл над последовательностью целых чисел. Выражение (dotimes (var countform resultform) . progbody) вычисляет форму countform, которая должна вернуть целое число. Затем тело цикла выполняется по порядку один раз для каждого число от нуля (включительно) до count (исключая). При этом переменная var связывается с текущим целым числом. Если значение countform отрицательно или равно нулю, тогда progbody не выполняется ни разу. Наконец выполняется resultform (одна форма, не неявный progn), и полученный результат возвращается из формы цикла. (Когда result вычисляется, переменная-индекс var все ещё связана и содержит количество выполненных итераций.) Если resultform опущена, то результат равен nil.

Для завершения цикла и возврата заданного значения может использоваться выражение return.

Пример использования dotimes для обработки строк:

;;; True if the specified subsequence of the string is a
;;; palindrome (reads the same forwards and backwards).

(defun palindromep (string &optional
                           (start 0)
                           (end (length string)))
  (dotimes (k (floor (- end start) 2) t)
    (unless (char-equal (char string (+ start k))
                        (char string (- end k 1)))
      (return nil))))

(palindromep «Able was I ere I saw Elba») ⇒ t

(palindromep «A man, a plan, a canal–Panama!») ⇒ nil

(remove-if-not #’alpha-char-p     ;Удалить знаки препинания
               «A man, a plan, a canal–Panama!»)
    ⇒ «AmanaplanacanalPanama»

(palindromep
 (remove-if-not #’alpha-char-p
                «A man, a plan, a canal–Panama!»)) ⇒ t

(palindromep
 (remove-if-not
   #’alpha-char-p
   «Unremarkable was I ere I saw Elba Kramer, nu?»)) ⇒ t

(palindromep
 (remove-if-not
   #’alpha-char-p
   «A man, a plan, a cat, a ham, a yak,
                   a yam, a hat, a canal–Panama!»)) ⇒ t
(palindromep
 (remove-if-not
   #’alpha-char-p
   «Ja-da, ja-da, ja-da ja-da jing jing jing»)) ⇒ nil

Изменение значения переменной var в теле цикла (с помощью setq например) будет иметь непредсказуемые последствия, возможно зависящие от реализации. Компилятор Common Lisp’а может вывести предупреждение о том, что переменная-индекс используется в setq.


Смотрите также do-symbols, do-external-symbols и do-all-symbols.

7.8.4 Отображение

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

Функция map может быть использована для отображения любого типа последовательности. Следующие же функции оперируют только списками.

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

mapcar последовательно обрабатывает элементы списков. Сначала функция применяется к car элементу каждого списка, затем к cadr элементу, и так далее. (Лучше всего, чтобы все переданные списки имели одинаковую длину. Если это не так, то цикл завершиться, как только закончится самый короткий список, и все оставшиеся элементы в других списках будут проигнорированы.) Значение, возвращаемое mapcar, является списком результатов последовательных вызовов функции из первого параметра. Например:

(mapcar #’abs ’(3 -4 2 -5 -6)) ⇒ (3 4 2 5 6)
(mapcar #’cons ’(a b c) ’(1 2 3)) ⇒ ((a . 1) (b . 2) (c . 3))

maplist похожа на mapcar за исключением того, что функция применяется к спискам и последующим cdr элементам этих списков, а не последовательно к элементам спискам. Например:

(maplist #’(lambda (x) (cons ’foo x))
         ’(a b c d))
    ⇒ ((foo a b c d) (foo b c d) (foo c d) (foo d))
(maplist #’(lambda (x) (if (member (car x) (cdr x)) 0 1)))
         ’(a b a c d b c))
    ⇒ (0 0 1 0 1 1 1)
   ;Возвращается 1, если соответствующий элемент входящего списка
   ;  появлялся последний раз в данном списке.

mapl и mapc похожи на maplist и mapcar, соответственно, за исключением того, что они не накапливают результаты вызова функций.

Эти функции используются, когда функция в первом параметре предназначена для побочных эффектов, а не для возвращаемого значения. Значение возвращаемое mapl или mapc является вторым аргументом, то есть первой последовательностью для отображения.

mapcan и mapcon похожи на mapcar и maplist соответственно, за исключением того, что результат создаётся с помощью функции nconc, а не list. То есть,

(mapcon f x1xn)
    ≡ (apply #’nconc (maplist f x1xn))

Такое же различие между mapcan и mapcar. Концептуально, эти функции позволяют функции отображения возвращать переменное количество элементов. Таким образом длина результата может быть не равна длине входного списка. Это, в частности, полезно для возврата нуля или одного элемента:

(mapcan #’(lambda (x) (and (numberp x) (list x)))
        ’(a 1 b c 3 4 d 5))
    ⇒ (1 3 4 5)

В этом случае функция действует, как фильтр. Это стандартная Lisp’овая идиома использования mapcan. (Однако, в этом контексте функция remove-if-not также может быть полезна.) Помните, что nconc деструктивная операция, следовательно и mapcan и mapcon также деструктивны. Список возвращаемый функцией function изменяется для соединения и возврата результата.

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

Функциональный аргумент функции отображения должен быть подходящим для функции apply. Он не может быть макросом или именем оператора. Кроме того, в качестве функционального аргумента можно использовать функцию, имеющую &optional и &rest параметры.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


7.8.5 Использование «GOTO»

Реализации Lisp’а начиная с Lisp’а 1.5 содержат то, что изначально называлось «the program feature», как будто без этого невозможно писать программы! Конструкция prog позволяет писать в Algol- или Fortran- императивном стиле, используя выражения go, которые могут ссылаться на теги в теле prog. Современный стиль программирования на Lisp’е стремится снизить использование prog. Различные конструкции циклов, как do, имеют тела с характеристиками prog. (Тем не менее, возможность использовать выражения go внутри конструкции цикла очень редко используется на практике.)

prog предоставляет три различные операции: связывание локальный переменных, использование выражения return и использование выражения go. В Common Lisp’е эти три операции были разделены на три конструкции: let, block и tagbody. Эти три конструкции могут использоваться независимо, как строительные кирпичики для других типов конструкций.

Часть tagbody после списка переменные называется телом. Элемент тела может быть символов или целым числом, и называться в этом случае тег. Также элемент тела может быть списком, и называться в этом случае выражением.

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

Если вычисляется форма (go tag), управление перемещается на часть тела, обозначенную тегом.

Область видимости тегов, устанавливаемых в tagbody, является лексической. Продолжительность видимости тегов динамическая. Когда управление вышло из конструкции tagbody, ссылка go на теги в её теле невозможны. Существует возможность для go прыжка в tagbody, которая не находится внутри конструкции tagbody, которая и содержала этот go. То есть возможны прыжки в родительский tagbody. Теги устанавливаемые tagbody будут только скрывать другие одноимённые теги.

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

(tagbody
   (catch ’stuff
      (mapcar #’(lambda (x) (if (numberp x)
                                (hairyfun x)
                                (go lose)))
              items))
   (return)
 lose
   (error «I lost big!»))

В зависимости от ситуации, go в Common Lisp’е не обязательно похож на простую машинную инструкцию «jump». Если необходимо, go может перепрыгивать ловушки исключений. Возможно так, что «замыкание», созданное с помощью function, для лямбда-выражения ссылается на тег (цель go) так долго, сколько лексически доступен данный тег. Смотрите 3 для понимания этого примера.


Конструкция prog является синтезом let, block и tagbody, позволяющая связывать переменные, использовать return и go в одной конструкции. Обычно конструкция prog выглядит так:

(prog (var1 var2 (var3 init3) var4 (var5 init5))
       {declaration}*
      statement1
 tag1
      statement2
      statement3
      statement4
 tag2
      statement5
      …
      )

Список после ключевого символа prog является множеством спецификаторов для связывания переменных var1, var2. Этот список обрабатывается так же, как и в выражении let: сначала слева направо выполняются все формы init (если формы нет, берётся значение nil), и затем переменные параллельно связываются с полученными ранее значениями. Возможно использовать декларации в начале дела prog так же, как и в let.

Тело prog выполняется, как обернутое в tagbody. Таким образом, для перемещения управления к тегу могут использоваться выражения go.

prog неявно устанавливает вокруг тела block с именем nil. Это значит, можно в любое время использовать return для выхода из конструкции prog.

Вот небольшой пример того, что можно сделать с помощью prog:

(defun king-of-confusion (w)
  «Take a cons of two lists and make a list of conses.
   Think of this function as being like a zipper.»
  (prog (x y z)     ;Инициализировать x, y, z в nil
        (setq y (car w) z (cdr w))
   loop
        (cond ((null y) (return x))
              ((null z) (go err)))
   rejoin
        (setq x (cons (cons (car y) (car z)) x))
        (setq y (cdr y) z (cdr z))
        (go loop)
   err
        (cerror «Will self-pair extraneous items»
                «Mismatch — gleep!  S» y)
        (setq z y)
        (go rejoin)))

которые делает то же, что и:

(defun prince-of-clarity (w)
  «Take a cons of two lists and make a list of conses.
   Think of this function as being like a zipper.»
  (do ((y (car w) (cdr y))
       (z (cdr w) (cdr z))
       (x ’() (cons (cons (car y) (car z)) x)))
      ((null y) x)
    (when (null z)
      (cerror «Will self-pair extraneous items»
              «Mismatch — gleep!  S» y)
      (setq z y))))

Конструкция prog может быть выражена в терминах более простых конструкций block, let и tagbody:

(prog variable-list {declaration}* . body)
    ≡ (block nil (let variable-list {declaration}* (tagbody . body)))

Оператор prog* очень похож на prog. Одно отличие в том, что связывание и инициализация переменных осуществляется последовательно, тем самым форма init использовать значения ранее связанных переменных. Таким образом prog* относится к prog, как let* к let. Например,

(prog* ((y z) (x (car y)))
       (return x))

возвращает car элемент значения z.


Оператор (go tag) используется для применения «goto» внутри конструкции tagbody. tag должен быть символов или целым числом. tag не вычисляется. go переносит управление на точку тела, которая была помечена тегом равным eql заданному. Если такого тега в теле нет, поиск осуществляется в лексически доступном теле другой конструкции tagbody. Использоваться go с тегом, которого нет, является ошибкой.

Форма go никогда не возвращает значение.

В целях хорошего стиля, рекомендуется дважды подумать, прежде чем использовать go. Большинство функций go могут быть заменены циклами, вложенными условными формами или return-from. Если использование go неизбежно, рекомендуется управляющую структуру реализованную с помощью go «упаковать» в определении макроса.


GCodeEditor — выход из «прошивочного рабства»

Было ли у вас так, что стоите вы перед принтером и понимаете, что он делает не так, как хотелось бы? После чего начинаются танцы с бубном, пытаясь накамлать решение в интернете.

Затем выясняется, что ваша прошивка может только так и никак иначе. Дальше вы пытаетесь это решить постпроцессингом или другими шаманскими действия.

А ведь «прошивка» — это банальный интерпретатор g-кода, где есть два больших класса: основные и вспомогательные команды. Они нам знакомы по кодам G и М. А почему бы не добавить к ним третий «класс» — макросы?

Это самый доступный для простого пользователя способ программирования в приложениях. Вот для этого и была создана программа GCodeEditor. Как её установить и настроить рассказано в статье Инкапсуляция — мне нравится это слово… Это не конечный продукт, поэтому сильно не пинайте.

Одним из применений программы, был предложен макрос для вставки оператором стороннего «тела» в печатаемое изделие. Конечно, общество сразу предложило использовать команду М600, которая пока реализована только в прошивке Marlin. А если другая прошивка? Да и на сама М600 в чистую не работает, требуется настройка постпроцессинга. А это привязка к определенному слайсеру.

Суть предлагаемого метода — это визуальный поиск слоя и вставка макроса с необходимыми действиями, основанными на минимальной перечне g-кода. Какими действиями? Жизнь, штука непредсказуемая, всяко бывает.

Действия могут быть и достаточно необычными. К примеру, почему бы нам не использовать сопло для перемещения изделия? Вот так:

Процесс достаточно простой. «Приклеиваем» сопло к изделию, выдавливая пластик. После охлаждения сопла, изделие «намертво» прикреплено. Пододвигаем изделие к краю стола. Конечно, оно должно отвалится, но… Поэтому нагреваем сопло и изделие изящно падает в подготовленный ящик.

Режим Снятие

Таким образом, мы можем использовать текущую конфигурацию принтера для «необычных» действий. «Снятие» уже описал, расскажу про «Переставление», вот такое слово придумали ;о).

В статье Инкапсуляция — мне нравится это слово… я рассказывал, как можно вставить различные «тела» в печатаемое изделие вручную. А можно это сделать без участия оператора? Это и делает макрос «Переставление».

Он «приклеивает» инкапсулируемую деталь к соплу и вставляет её в куда надо. Поскольку настройка сего действа достаточно трудоёмкая, то пока не было случая воспользоваться этим макросом — нет такого объемного заказа.

Вот что «делают» те кнопки, которые не описаны в предыдущей статье. ;о)

2.5.2 VBA. Организация циклов | Пакеты прикладных программ

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

VBA поддерживает циклические конструкции двух видов:

  1. Циклы с фиксированным числом повторений (циклы со счетчиком).
  2. Циклы с неопределенными числом повторений (циклы с условием).

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

Фиксированные циклы

VBA предоставляет две управляющие структуры для организации фиксированного цикла: For … Next (цикл со счетчиком) и For Each … Next (цикл с перечислением).

Оператор For … Next это типовой цикл со счетчиком, выполняющий заданное число итераций. Синтаксис оператора For … Next:

For <счетчик> = <начЗначение> То <конЗначение> [Step <приращение>] <блок операторов> Next [<счетчик>] 

Пример использования оператора For … Next.

Листинг 9. Оператор For … Next

' ЗАДАЧА: Составить программу, которая получает два числа от пользователя. ' Складывает все числа в диапазоне, заданном этими двумя числами, а затем ' отображает результирующую сумму. Sub sample7() Dim i As Integer ‘счетчик цикла Dim sStart ‘начальное значение счетчика Dim sEnd ‘конечное значение счетчика Dim sSum As Long ‘результирующая сумма sStart = InputBox("Введите первое число:") sEnd = InputBox("Введите второе число:") sSum = 0 For i = CInt(sStart) To CInt(sEnd) sSum = sSum + i Next i MsgBox "Сумма чисел от " & sStart & " до " & sEnd & " равна: " & sSum End Sub 

Оператор цикла For Each … Next относится к категории операторов объектного типа, т.е. применяется в первую очередь к коллекциям объектов, а также к массивам. Тело цикла выполняется фиксированное число раз, соответствующее числу элементов массива или коллекции. Формат оператора For Each … Next:

For Each <элемент> In <группа> <блок операторов> Next [<элемент>] 

Циклы с условием (неопределенные циклы)

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

  • Четыре вида циклов Do..Loop, которые различаются типом проверяемого условия и временем выполнения этой проверки.
  • Непрерываемый цикл While … Wend.

Цикл Do While … Loop — типичный цикл с предусловием. Условие проверяется до того, как выполняется тело цикла. Цикл продолжает свою работу, пока это <условие> выполняется (т.е. имеет значение True). Так как проверка выполняется в начале, то тело цикла может ни разу не выполниться. Формат цикла Do While … Loop:

Do While <условие> <блок операторов> Loop 

Листинг 10. Цикл Do While … Loop

' ЗАДАЧА: Составить программу, которая предусматривает ввод пользователем ' произвольной последовательности чисел. Ввод должен быть прекращен ' только после того, как сумма введенных нечетных чисел превысит 100. Sub sample8() Dim OddSum As Integer ‘сумма нечетных чисел Dim OddStr As String ‘строка с нечетными числами Dim Num ‘для приема вводимых чисел OddStr = "" ‘инициализация выходной строки OddSum = 0 ‘инициализация суммы OddSum Do While OddSum < 100 ‘начало цикла Num = InputBox("Введите число: ") If (Num Mod 2) <> 0 Then ‘проверка на четность OddSum = OddSum + Num ‘накопление суммы нечетных чисел OddStr = OddStr & Num & " " End If Loop 'вывод строки с нечетными числами MsgBox prompt:="Нечетные числа: " & OddStr End Sub 

Оператор Do … Loop While предназначен для организации цикла с постусловием. Условие проверяется после того, как тело цикла, будет выполнено хотя бы один раз. Цикл продолжает свою работу, пока <условие> остается истинным. Формат цикла Do … Loop While:

Do <блок операторов> Loop While<условие> 

Листинг 11. Цикл с постусловием

' ЗАДАЧА: Составить программу игры "Угадай число". Программа должна случайным ' образом генерировать число в диапазоне от 1 до 1000, пользователь должен ' угадать это число. Программа на каждое вводимое число выводит подсказку ' "больше" или "меньше". Sub sample8() Randomize Timer ' инициализация генератора случайных чисел Dim msg As String ' строка сообщения Dim SecretNumber As Long, UserNumber As Variant Begin:	SecretNumber = Round(Rnd * 1000) ' число, сгенерированное компьютером UserNumber = Empty ' число, вводимое пользователем Do ' игровой процесс Select Case True Case IsEmpty(UserNumber): msg = "Введите число" Case UserNumber > SecretNumber: msg = "Слишком много!" Case UserNumber < SecretNumber: msg = "Слишком мало!" End Select UserNumber = InputBox(prompt:=msg, Title:="Угадай число") Loop While UserNumber <> SecretNumber ' проверка If MsgBox("Играть еще? ", vbYesNo + vbQuestion, "Вы угадали!") = vbYes Then GoTo Begin End If End Sub 

Циклы Do Until … Loop и Do … Loop Until являются инверсиями ранее рассмотренных циклов с условием. В общем случае они работают аналогично, за исключением того, что тело цикла выполняется при ложном условии (т.е. <условие>=False). Формат цикла Do Until … Loop:

Do Until <условие> <блок операторов> Loop 

Формат цикла Do … Loop Until:

Do <блок операторов> Loop Until<условие> 

Практическое задание: Перепишите программы из листингов 10 и 11 с использованием инвертированных операторов цикла.

Цикл While … Wend также относится к циклам с условием. Данный оператор полностью соответствует структуре Do While … Loop. Формат цикла While … Wend:

While <условие> <блок операторов> Wend 

Отличительной особенностью этого оператора является невозможность принудительного завершения (прерывания) тела цикла (оператор Exit Do не работает в цикле While … Wend).

Прерывание цикла

Для досрочного завершения итерации и выхода из цикла применяется оператор Exit. Этот оператор применим в любой циклической структуре, кроме While … Wend. Общий синтаксис использования Exit для прерывания цикла таков:

<начало_цикла> [<блок операторов1>] Exit (For | Do) [<блок операторов2>] [Exit (For | Do)] ... <конец_цикла> 

При выполнении оператора Exit цикл прерывается, и управление передается оператору, следующему за оператором <конец_цикла>. В теле цикла может присутствовать несколько операторов Exit.

Листинг 12. Принудительный выход из цикла

Sub sample9() For i = 1 To 10000000 If i = 10 Then Exit For ' выход из цикла, когда счетчик достигнет 10 Next End Sub 

CC-BY-CA Анатольев А.Г., 31.01.2012

Как импортировать электронные письма Outlook в Excel с помощью VBA

Если вы получаете много электронных писем и вам необходимо проанализировать содержащиеся в них данные, то импорт ваших электронных писем из Outlook в Excel с помощью VBA сэкономит вам много времени. В этом посте мы собираемся изучить способ импорта всех электронных писем в заданной папке, которые были получены после определенной даты. Очевидно, что для этого на вашем компьютере должен быть установлен Microsoft Outlook.

Если вы не знакомы с VBA, вероятно, было бы неплохо прочитать этот пост о том, как использовать код VBA, который вы найдете в Интернете, прежде чем продолжить чтение и работу с заполненной рабочей тетрадью.

Для этого кода вам понадобится Microsoft Outlook 16.0 Object Library . В визуальном базовом редакторе перейдите к Инструменты , затем Ссылки , установите флажок рядом с ним и нажмите кнопку OK , чтобы включить его.

В этом примере я буду импортировать из папки Outlook под названием Входящие/Отчет о чистых продажах/Продажи . Если вы хотите импортировать из подпапки Sales , вам нужно будет добавить еще .Folders(«Subfolder Name») в строку кода Set Folder = .

Я добавил именованные диапазоны в книгу, ссылаясь на ячейку с именованным диапазоном в VBA, а не на общий адрес ячейки, такой как Range («A1»), что означает, что вы можете перемещать элементы в своей книге, не беспокоясь о нарушении кода. . Это именованные диапазоны, которые будет использовать код.

  • From_date — эта ячейка позволит пользователю ввести a From Date , чтобы возвращались только электронные письма, полученные (и находящиеся в нашей папке «Продажи») после этой даты.
  • eMail_subject — эта ячейка содержит заголовок столбца Subject . Темы из электронных писем будут импортированы чуть ниже этой ячейки.
  • eMail_date — эта ячейка содержит заголовок столбца Date . Дата получения из электронных писем будет импортирована чуть ниже этой ячейки.
  • eMail_sender — эта ячейка содержит заголовок столбца Sender . Информация об отправителе из электронных писем будет импортирована чуть ниже этой ячейки.
  • eMail_text — эта ячейка содержит заголовок столбца Email Text . Основной текст  из электронных писем будет импортирован сразу под этой ячейкой.

Вот код.

  Подпрограмма GetFromOutlook()

Dim OutlookApp как Outlook.Application
Dim OutlookNamespace как пространство имен
Затемнить папку как MAPIFolder
Dim OutlookMail как вариант
Dim i как целое число

Установите OutlookApp = Новый Outlook.Application
Установите OutlookNamespace = OutlookApp.GetNamespace("MAPI")
Установите папку = OutlookNamespace.GetDefaultFolder(olFolderInbox).Folders("Отчет о чистых продажах").Folders("Продажи")

я = 1

Для каждого OutlookMail в Folder.Items
    Если OutlookMail.ReceivedTime >= Range("From_date").Значение Тогда
        Диапазон ("eMail_subject"). Смещение (i, 0). Значение = OutlookMail.Subject
        Диапазон ("eMail_date"). Смещение (i, 0). Значение = OutlookMail.ReceivedTime
        Диапазон ("eMail_sender"). Смещение (i, 0). Значение = OutlookMail.SenderName
        Диапазон ("eMail_text"). Смещение (i, 0). Значение = OutlookMail.Body
        
        я = я + 1
    Конец, если
Следующий OutlookMail

Установить папку = ничего
Установите OutlookNamespace = Ничего
Установите OutlookApp = Ничего

End Sub  

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

Использование макроса для замены текста везде, где он появляется в документе

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ/УСЛОВИЯ ИСПОЛЬЗОВАНИЯ

Информация, иллюстрации и код, содержащиеся в моих «Советах по Microsoft Word», предоставляются бесплатно и без риска или обязательств.

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

Если вы хотите сделать пожертвование, используйте соответствующую кнопку пожертвования для доступа к PayPal. Благодарю вас!


Целью этой страницы советов и справки Microsoft Word является обсуждение и предоставление решения VBA для поиска и замены текста в любом месте документа. Этот контент представляет собой измененную версию моей статьи на ту же тему, ранее опубликованную на веб-сайте часто задаваемых вопросов по Word MVP. Благодарим Дуга Роббинса, Питера Хьюетта и Джонатана Уэста за их вклад в эту статью.

Фон

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

В Word 2007 и более ранних версиях даже встроенная утилита «Найти и заменить» имеет недостаток. Например, текст в текстовом поле, расположенном в верхнем или нижнем колонтитуле, выходит за рамки диапазона поиска утилиты «Найти и заменить».

Существует от одиннадцати до семнадцати констант wdStoryType, которые могут формировать StoryRanges (или части) документа. Не все сюжетные диапазоны используются в каждом документе. Семнадцать возможных типов материалов в документе Word 2016 показаны ниже.

Базовый код

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

Сценарий VBA:

 Sub FindAndReplaceFirstStoryOfEachType()
Dim rngStory As Range
  Для каждой rngStory в ActiveDocument.StoryRanges
    С помощью rngStory.Find
      .Text = "найти текст"
      .Replacement.Text = "Меня нашли"
      .Wrap = wdFindContinue
      .Выполнить замену:=wdReplaceAll
    Конец с
  Следующая история ринга
лбл_Выход:
  Выйти из подпрограммы
Конец сабвуфера
 

Дополнительный совет: если вы не знаете, при использовании метода Selection.Find необходимо указать все параметры поиска и замены, например .Forward = True, поскольку в противном случае значения берутся из текущих настроек диалогового окна «Найти и заменить». , которые являются «липкими. В этом нет необходимости при использовании Range.Find, потому что параметры являются их значениями по умолчанию, если вы не укажете значение в своем коде.

Приведенный выше базовый макрос имеет недостаток. Он действует только на «первый» StoryRange каждого из (одиннадцати-семнадцати) StoryType.

Хотя документ имеет только один объект StoryRange wdMainTextStory, он может иметь один или несколько объектов StoryRange в нескольких других типах StoryType. Например, документ может содержать несколько текстовых полей или несколько разделов с несвязанными верхними или нижними колонтитулами. Каждый из этих нескольких элементов является StoryRange в StoryType. Базовый код обрабатывает только первый StoryRange в этих типах StoryType.

Промежуточный код

Чтобы убедиться, что код работает с каждым StoryRange в каждом StoryType, вам необходимо:

  • Используйте метод NextStoryRange
  • Примените небольшой «обман» VBA (предложенный Питером Хьюеттом для соединения) с любыми пустыми несвязанными верхними и нижними колонтитулами.

Сценарий VBA:

 Общедоступная подпрограмма FindReplaceAlmostAnywhere()
Dim rngStory As Word.Range
Dim lngValidate As Long
  «Исправьте проблему с пропущенным пустым верхним/нижним колонтитулом, предложенную Питером Хьюеттом.lngValidate = ActiveDocument.Sections(1).Headers(1).Range.StoryType
  'Перебрать все типы материалов в текущем документе.
  Для каждой rngStory в ActiveDocument.StoryRanges
    'Перебрать все связанные истории.
    Делать
      С помощью rngStory.Find
        .Текст = "Тест"
        .Replacement.Text = "Меня нашли"
        .Wrap = wdFindContinue
        .Выполнить замену:=wdReplaceAll
      Конец с
      'Получить следующую связанную историю (если есть).
      Установите rngStory = rngStory.NextStoryRange
    Цикл до тех пор, пока rngStory не станет ничем
  Следующий
лбл_Выход:
  Выйти из подпрограммы
Конец суб 

Упомянутый выше VBA «Trickery» просто возвращает значение StoryType основного заголовка раздела One. Доказано, что это устраняет проблему VBA, связанную с «перескакиванием» пустых несвязанных верхних и нижних колонтитулов и продолжением обработки последовательных верхних и нижних колонтитулов.


В указанном выше промежуточном коде остается один недостаток. Как и утилита меню «Найти и заменить» в более ранних версиях Word, этот код пропустит и не сможет обработать текстовые поля и фигуры, привязанные к заголовку/нижнему колонтитулу StoryRange.

Комплексный код

Решение проблемы с текстовым полем и формой верхнего/нижнего колонтитула заключается в том, что текстовые поля и фигуры содержатся в коллекции ShapeRange документа. Сначала мы проверяем ShapeRange каждого из шести StoryRanges верхнего и нижнего колонтитула в каждом разделе документа на наличие формы. Если фигура найдена, мы затем проверяем .TextFrame.TextRange формы для параметра .Find.Text.

Этот последний макрос содержит весь код для поиска и замены текста «где угодно» в документе.Было добавлено несколько улучшений, упрощающих применение желаемого поиска и замены текстовых строк:

Сценарий VBA:

 Общедоступная подпрограмма FindReplaceAnywhere()
Dim rngStory As Word.Range
Dim strFind As String, strRplc As String
Dim lngValidate As Long
Дим оШп Ас Шейп

  strFind = InputBox("Введите текст, который вы хотите найти.", "НАЙТИ")
  Если strFind = "" Тогда
    MsgBox "Отменено пользователем"
    Выйти из подпрограммы
  Конец, если
Попробуйте еще раз:
  strRplc = InputBox("Введите замену.", "ЗАМЕНЯТЬ")
  Если strRplc = "" Тогда
    Если MsgBox("Вы хотите просто удалить найденный текст?", _
      vbYesNoCancel) = vbNo Тогда
      Перейти к попытке еще раз
    ИначеЕсли vbОтменить Тогда
      MsgBox «Отменено пользователем».
      Выйти из подпрограммы
    Конец, если
  Конец, если
  'Исправить проблему с пропущенным пустым верхним/нижним колонтитулом.
  lngValidate = ActiveDocument.Sections(1).Headers(1).Range.StoryType
  'Перебрать все типы материалов в текущем документе.
  Для каждой rngStory в ActiveDocument.StoryRanges
    'Перебрать все связанные истории.
    Делать
      SearchAndReplaceInStory rngStory, strFind, strRplc
      При ошибке Возобновить Далее
      Выберите Case rngStory.StoryType
        Случай 6, 7, 8, 9, 10, 11
          Если rngStory.ShapeRange.Count > 0 Тогда
            Для каждого оШп в rngStory.ShapeRange
              Если oShp.TextFrame.HasText Тогда
                SearchAndReplaceInStory oShp.TextFrame.TextRange, strFind, strRplc
              Конец, если
            Следующий
          Конец, если
        Дело еще
          'Ничего не делать
      Конец выбора
      При ошибке Перейти к 0
      'Получить следующую связанную историю (если есть)
      Установите rngStory = rngStory.NextStoryRange
    Цикл до тех пор, пока rngStory не станет ничем
  Следующий
лбл_Выход:
  Выйти из подпрограммы
Конец сабвуфера

Public Sub SearchAndReplaceInStory (ByVal rngStory As Word.Range, _
                                   ByVal strSearch As String, _
                                   ByVal strReplace As String)
    С помощью rngStory.Find
      .ClearFormatting
      .Replacement.ClearFormatting
      .Текст = стрПоиск
      .Replacement.Text = strReplace
      .Обернуть = wdFindContinue
      .Выполнить замену:=wdReplaceAll
    Конец с
лбл_Выход:
  Выйти из подпрограммы
Конец суб 

Примечание. Эта страница с советами, иллюстрации и примеры были разработаны с использованием Word 2016. Она полностью функциональна с Word 2003–2016.

Вот оно! Я надеюсь, что вы нашли эту страницу советов полезной и информативной.

Переход к началу или концу реального документа (Microsoft Word)

Обратите внимание: Эта статья написана для пользователей следующих версий Microsoft Word: 97, 2000, 2002 и 2003.Если вы используете более позднюю версию (Word 2007 или более позднюю), этот совет может вам не подойти . Чтобы просмотреть версию этого совета, написанную специально для более поздних версий Word, щелкните здесь: Переход к началу или концу реального документа.

В других выпусках WordTips вы узнаете, как использовать методы HomeKey и EndKey для перехода к началу или концу документа в макросе.Они отлично работают, если точка вставки находится в основной части документа при выполнении макроса. Однако это не всегда работает должным образом, если точка вставки находится в другом месте.

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

.
Selection.GoTo Что:=wdGoToSection, Который:=wdGoToFirst
 

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

ActiveDocument.Characters.Last.Select
Selection.Collapse
 

Отсутствие элегантности и симметрии между двумя командами вызывает сожаление, но, не зная, где находится точка вставки, эти команды безопаснее, чем использование только HomeKey и EndKey.

WordTips — ваш источник недорогого обучения работе с Microsoft Word. (Microsoft Word — самая популярная в мире программа для обработки текстов.) Этот совет (826) относится к Microsoft Word 97, 2000, 2002 и 2003. Вы можете найти версию этого совета для ленточного интерфейса Word (Word 2007 и более поздних версий) здесь: Переход к началу или концу реального Документ .

Биография автора

Аллен Вятт

На его счету более 50 научно-популярных книг и множество журнальных статей. Аллен Уайатт является всемирно признанным автором.Он является президентом Sharon Parq Associates, компании, предоставляющей компьютерные и издательские услуги. Узнать больше об Аллене…

Поиск ведущих апострофов

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

Открой для себя больше

Отображение количества нулей в строке состояния

Excel позволяет отображать результаты нескольких распространенных функций рабочего листа в строке состояния.Доступные функции …

Открой для себя больше

Создание уникальных последовательных имен

Вам нужно создать несколько слов или фраз, в каждой из которых вы меняете всего несколько букв? Если изменения …

Открой для себя больше

Определение наличия выделенного текста

Макросы часто предназначены для запуска только в выбранной части документа.Рекомендуется убедиться, что …

Открой для себя больше

Подсчет символов в выделении с помощью VBA

Нужно выяснить количество символов в диапазоне выделенного текста? Вот как это сделать в VBA.

Открой для себя больше

Игнорирование умных кавычек при сравнении текста

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

Открой для себя больше

Страница, которую вы искали, не найдена

Предложения:

  • Нажмите в поле поиска в заголовке этой страницы, введите один или несколько ключевые слова, которые вы хотите найти, и нажмите кнопку «Поиск».Целиком Будет выполнен поиск на сайте Word MVP.
  • Проверьте ссылки слева.
  • Посмотрите меню в шапке.
  • Если вы перешли на эту страницу, перейдя по ссылке, которую вы нашли в другом месте или через набрав URL-адрес в адресной строке браузера, продолжите чтение ниже.

 

URL-адреса на этом сайте чувствительны к регистру

Возможно, вы попали сюда, перейдя по ссылке, которую вы нашли в другом месте, или набрав URL-адрес в адресной строке вашего браузера.

Как поясняется на главной странице Word, все содержимое этого сайта было перемещено с word.mvps.org с по wordmvp.com . Все старые ссылки, указывающие на word.mvps.org автоматически перенаправляется с на wordmvp.com . Если возможно, редирект автоматически покажет страницу со статьей старой ссылка указывала. Если эта страница не может быть найдена, вместо нее отображается эта страница.

Возможно, статья, которую вы ищете, уже существует.Проблема может заключаться в неаккуратном использовании прописных и строчных букв в URL.

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

  • Этот URL будет работать:   https://wordmvp.com/ Часто задаваемые вопросы s/ G общий/Shortcuts.htm
  • Этот URL-адрес не будет работать:      https://wordmvp.com/ faq s/ g общие/ярлыки.хтм

 

Доменное имя URL-адреса никогда не зависит от регистра. Например, не важно набираете ли вы W or MVP .com или w ord mvp .com.

 

Список URL-адресов с правильным использованием прописных и строчных букв

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

wordmvp.ком/

wordmvp.com/FAQs/

wordmvp.com/FAQs/AppErrors/

wordmvp.com/Часто задаваемые вопросы/Настройка/

wordmvp.com/FAQs/DrwGrphcs/

wordmvp.com/Часто задаваемые вопросы/Форматирование/

wordmvp.com/Часто задаваемые вопросы/Общие/

wordmvp.com/FAQs/InterDev/

wordmvp.com/FAQs/MacrosVBA/

wordmvp.com/FAQs/MailMerge/

wordmvp.com/FAQ/Нумерация/

wordmvp.com/FAQs/TblsFldsFms/

wordmvp.com/Часто задаваемые вопросы/Пользовательские формы/

wordmvp.com/FindHelp/

wordmvp.com/Mac/

wordmvp.com/Шаблоны/

wordmvp.com/Учебники/

Таблицы VBA и ListObjects — Excel Off The Grid

Таблицы — одна из самых мощных функций Excel. Управление ими с помощью VBA позволяет автоматизировать эту мощь, что дает двойную выгоду 🙂

Excel любит хранить данные в таблицах. Основные структурные правила, такие как (а) заголовки должны быть уникальными (б) разрешена только одна строка заголовка, делают таблицы совместимыми с более сложными инструментами.Например, Power Query, Power Pivot и SharePoint перечисляют все таблицы использования в качестве источника или вывода. Таким образом, Microsoft явно намерена использовать таблицы.

Однако самое большое преимущество для повседневного пользователя Excel заключается в гораздо большей простоте; если мы добавим новые данные в конец таблицы, любые формулы, ссылающиеся на таблицу, будут автоматически расширены, чтобы включить новые данные.

Любите ли вы таблицы так же сильно, как я, или нет, этот пост поможет вам автоматизировать их с помощью VBA.

Таблицы в том виде, в каком мы их знаем сегодня, впервые появились в Excel 2007.  Это была замена функции списков, представленной в Excel 2003.  С точки зрения VBA объектная модель документа (DOM) не изменилась с обновленной функциональностью. Итак, хотя мы используем термин «таблицы» в Excel, в VBA они по-прежнему называются ListObjects.

Скачать файл примера

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


Скачать файл: 0009 Таблицы VBA и ListObjects.zip

Структура таблицы

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

Диапазон и диапазон тела данных

Диапазон — это вся область таблицы.

Диапазон тела данных включает только строки данных, исключая заголовок и итоги.

Заголовок и итоговые строки

Диапазон строк заголовка — это верхняя строка таблицы, содержащая заголовки столбцов.

Диапазон строк итогов , если он отображается, включает вычисления в нижней части таблицы.

Столбцы списка и строки списка

Отдельные столбцы называются столбцами списка .

Каждая строка называется строкой списка .

Код VBA в этом посте подробно описывает, как управлять всеми этими объектами таблицы.

Ссылки на части таблицы

Хотя у вас может возникнуть соблазн пропустить этот раздел, я рекомендую прочитать его полностью и изучить примеры.Понимание объектной модели документа Excel является ключом к чтению и написанию кода VBA. Освойте это, и ваша способность писать собственный код VBA будет намного выше.

Многие примеры в этом первом разделе используют метод select, чтобы проиллюстрировать, как ссылаться на части таблицы. На самом деле вы редко будете использовать метод select.

Выбрать всю таблицу

Следующий макрос выберет всю таблицу, включая итоги и строки заголовков.

 Sub SelectTable()

Активный лист.ListObjects("myTable").Range.Select

End Sub 

Выбор данных в таблице

DataBodyRange исключает разделы заголовка и итогов таблицы.

 Sub SelectTableData()

ActiveSheet.ListObjects("myTable").DataBodyRange.Select

End Sub 

Получить значение из отдельной ячейки в таблице

Следующий макрос извлекает значение таблицы из строки 2, столбца 4 и отображает его в окне сообщения.

 Sub GetValueFromTable()

MsgBox ActiveSheet.ListObjects("myTable").DataBodyRange(2, 4).value

End Sub 

Выделить весь столбец

В приведенном ниже макросе показано, как выбрать столбец по его положению или по имени.

 Sub SelectAnEntireColumn()

'Выбрать столбец по позиции
ActiveSheet.ListObjects("myTable").ListColumns(2).Range.Select

'Выбрать столбец по названию
ActiveSheet.ListObjects("myTable").ListColumns("Категория").Range.Select

End Sub 

Выберите столбец (только данные)

Это похоже на макрос выше, но использует DataBodyRange только для выбора данных; он исключает заголовки и итоги.

 Sub SelectColumnData()

'Выбрать данные столбца на основе позиции
ActiveSheet.ListObjects("myTable").ListColumns(4).DataBodyRange.Select

'Выбрать данные столбца по имени
ActiveSheet.ListObjects("myTable").ListColumns("Категория").DataBodyRange.Select

End Sub 

Выберите определенный заголовок столбца

Этот макрос показывает, как выбрать ячейку заголовка столбца 5-го столбца.

 Sub SelectCellInHeader()

ActiveSheet.ListObjects("myTable").HeaderRowRange(5).Select

End Sub 

Выбор определенного столбца в разделе итогов

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

 Sub SelectCellInTotal()

ActiveSheet.ListObjects("myTable").TotalsRowRange(3).Select

End Sub 

сообщить об этом объявленииВыбрать всю строку данных

Приведенный ниже макрос выбирает 3-ю строку данных из таблицы.

ПРИМЕЧАНИЕ. – Строка заголовка не включается в список ListRow. Таким образом, ListRows(3) является третьей строкой в ​​DataBodyRange, а не третьей строкой сверху таблицы.

 Sub SelectRowOfData()

ActiveSheet.ListObjects("myTable").ListRows(3).Диапазон.Выбрать

End Sub 

Выберите строку заголовка

Следующий макрос выбирает раздел заголовка таблицы.

 Подраздел SelectHeaderSection()

ActiveSheet.ListObjects("myTable").HeaderRowRange.Select

End Sub 

Выберите строку итогов

Чтобы выбрать строку итогов таблицы, используйте следующий код.

 Sub SelectTotalsSection()

ActiveSheet.ListObjects("myTable").TotalsRowRange.Select

End Sub 

Итак, теперь мы знаем, как ссылаться на части таблицы, пора перейти к более интересным примерам.



Создание и преобразование таблиц

Этот раздел макросов посвящен созданию и изменению размеров таблиц.

Преобразование выделения в таблицу

Приведенный ниже макрос создает таблицу на основе текущей выбранной области и называет ее myTable . Диапазон упоминается как Selection.CurrentRegion, но его можно заменить любым объектом диапазона.

Если вы работаете с файлом примера, этот макрос вызовет ошибку, так как таблица с именем myTable уже существует в книге.Новая таблица по-прежнему будет создана с именем по умолчанию, но код VBA выдаст ошибку на этапе переименования.

 Sub ConvertRangeToTable()

имя_таблицы как строка
Dim tableRange As Range

Установите имя_таблицы = "myTable"
Установите tableRange = Selection.CurrentRegion
ActiveSheet.ListObjects.Add(SourceType:=xlSrcRange, _
    Источник: = tableRange, _
    xlListObjectHasHeaders:=xlYes _
    ).Имя = имя_таблицы

End Sub 

Преобразование таблицы обратно в диапазон

Этот макрос преобразует таблицу обратно в стандартный диапазон.

 Sub ConvertTableToRange()

ActiveSheet.ListObjects("myTable").Unlist

End Sub 

ПРИМЕЧАНИЕ. – К сожалению, при преобразовании таблицы в стандартный диапазон форматирование таблицы не удаляется. Поэтому ячейки могут все еще выглядеть как таблица, даже если это не так — это расстраивает!!!

Изменение размера диапазона таблицы

Следующий макрос изменяет размер таблицы до ячейки A1 – J100.

 Sub ResizeTableRange()

ActiveSheet.ListObjects("мояТаблица").Диапазон изменения ("$A$1:$J$100")

End Sub 

Стили таблиц

Существует множество параметров форматирования таблиц, наиболее распространенные из которых показаны ниже.

Изменение стиля таблицы

Изменение стиля таблицы на существующий предопределенный стиль.

 Sub ChangeTableStyle()

ActiveSheet.ListObjects("myTable").TableStyle = "TableStyleLight15"

End Sub 

Самый простой способ применить различные стили таблиц — использовать средство записи макросов. Записанный код VBA будет включать названия всех выбранных вами стилей.

Получить имя стиля таблицы

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

 Sub GetTableStyleName()

MsgBox ActiveSheet.ListObjects("myTable").TableStyle

End Sub 

Применение стиля к первому или последнему столбцу

Первый и последний столбцы таблицы можно форматировать по-разному с помощью следующих макросов.

 Стили подколонок()

'Применить особый стиль к первому столбцу
ActiveSheet.ListObjects("мояТаблица").Шовтаблестилефирстколумн = Истина

'Применить особый стиль к последнему столбцу
ActiveSheet.ListObjects("myTable").ShowTableStyleLastColumn = True

End Sub 

Добавление или удаление полос

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

 Sub ChangeStripes()

'Применить полосы столбцов
ActiveSheet.ListObjects("myTable").ShowTableStyleColumnStripes = True

'Удалить полосы строк
ActiveSheet.ListObjects("мояТаблица").Шовтаблстилеровстрипес = Ложь

End Sub 

Установить стиль таблицы по умолчанию

Следующий макрос устанавливает стиль таблицы по умолчанию.

 Sub SetDefaultTableStyle()

'Установить стиль таблицы по умолчанию
ActiveWorkbook.DefaultTableStyle = "TableStyleMedium2"

End Sub 

Перебор таблиц

Макросы в этом разделе перебирают все таблицы на листе или книге.

Цикл по всем таблицам рабочего листа

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

 Sub LoopThroughAllTablesWorksheet()

'Создайте переменные для хранения рабочего листа и таблицы
Dim ws As рабочий лист
Dim таблица как ListObject

Установите ws = ActiveSheet
'Перебрать каждую таблицу на листе
Для каждой таблицы в ws.ListObjects

    «Сделай что-нибудь со столом...

Следующая таблица

End Sub 

В приведенном выше коде мы установили таблицу в переменную, поэтому мы должны правильно обращаться к таблице. В разделе « Сделайте что-нибудь с таблицей… » введите действие, которое необходимо выполнить для каждой таблицы, используя tbl для ссылки на таблицу.

Например, следующее изменит стиль каждой таблицы.

 tbl.TableStyle = "TableStyleLight15" 

Цикл по всем таблицам в рабочей книге

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

 Sub LoopThroughAllTablesWorkbook()

'Создайте переменные для хранения рабочего листа и таблицы
Dim ws As рабочий лист
Dim таблица как ListObject

'Пройтись по каждому рабочему листу
Для каждого ws в ActiveWorkbook.Рабочие листы

    'Перебрать каждую таблицу на листе
    Для каждой таблицы в ws.ListObjects

        «Сделай что-нибудь со столом...

    Следующая таблица

Следующий мс

End Sub 

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

 tbl.ShowTotals = True 

Добавление и удаление строк и столбцов

Следующие макросы добавляют и удаляют строки, заголовки и итоги из таблицы.

Добавить столбцы в таблицу

Следующий макрос добавляет столбец в таблицу.

 Подпрограмма AddColumnToTable()

'Добавить столбец в конце
ActiveSheet.ListObjects("myTable").ListColumns.Добавить

'Добавить столбец в позицию 2
ActiveSheet.ListObjects("myTable").ListColumns.Добавить позицию:=2
End Sub 

Добавить строки в конец таблицы

Следующий макрос добавит строку в конец таблицы

 Sub AddRowsToTable()

'Добавить строку внизу
ActiveSheet.ListObjects("myTable").ListRows.Добавить

'Добавить строку в первую строку
ActiveSheet.ListObjects("myTable").ListRows.Добавить позицию:=1
End Sub 

Удалить столбцы из таблицы

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

 Sub DeleteColumnsFromTable()

'Удалить столбец 2
ActiveSheet.ListObjects("myTable").ListColumns(2).Удалить

'Удалить столбец по имени
ActiveSheet.ListObjects("myTable").ListColumns("Фев").Удалить
End Sub 

Удаление строк из таблицы

В структуре таблицы строки не имеют имен, поэтому их можно удалить, только обратившись к номеру строки.

 Sub DeleteRowsFromTable()

'Удалить строку 2
ActiveSheet.ListObjects("myTable").ListRows(2).Удалить

'Удалить несколько строк
Активный лист.ListObjects("myTable").Range.Rows("4:6").Delete
End Sub 

Добавить итоговую строку в таблицу

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

 Подпрограмма AddTotalRowToTable()

'Показать общую строку со значением в последнем столбце
ActiveSheet.ListObjects("myTable").ShowTotals = True

'Изменить итоговое значение столбца "Итого" на среднее
ActiveSheet.ListObjects("myTable").ListColumns("TotalColumn").TotalsCalculation = _
    xlTotalsCalculationAverage

'Итоги можно добавлять по позиции, а не по имени
Активный лист.ListObjects("myTable").ListColumns(2).TotalsCalculation = _
    xlTotalsCalculationAverage
End Sub 

Типы расчета итогов

 xlTotalsCalculationNone
xlTotalsCalculationAverage
кслтоталскалкулатионкаунт
кслтоталскалкулатионкаунтнумс
кслтоталскалкулатионмакс
кслтоталскалкулатионмин
кслтоталскалкулатионсум
кслтоталскалкулатионстддев
xlTotalsCalculationVar 

Видимость заголовка таблицы

Заголовки таблицы можно включить или отключить. Следующее скроет заголовки.

 Sub ChangeTableHeader ()

ActiveSheet.ListObjects("myTable").ShowHeaders = False

End Sub 

Удалить автоматический фильтр

Автоматический фильтр можно скрыть. Обратите внимание, что для работы этого кода должен быть виден заголовок таблицы.

 Sub RemoveAutoFilter()

ActiveSheet.ListObjects("myTable").ShowAutoFilterDropDown = False

End Sub 

У меня есть отдельный пост об управлении настройками автофильтра — посмотрите его здесь. Большая часть этого поста относится и к таблицам.

Другие методы управления диапазонами

Другие существующие методы VBA для управления диапазонами также могут применяться к таблицам.

Использование оператора объединения

Чтобы выбрать несколько диапазонов, мы можем использовать оператор объединения VBA. Вот пример, он выберет строки 4, 1 и 3.

 Sub SelectMultipleRangesUnionOperator()

Union(ActiveSheet.ListObjects("myTable").ListRows(4).Range, _
    ActiveSheet.ListObjects("myTable").ListRows(1).Range, _
    ActiveSheet.ListObjects("myTable").ListRows(3).Range).Select

End Sub 

Присвоить значения из массива вариантов строке таблицы

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

 Sub AssignValueToTableFromArray()

'Присвоение значений массиву (для иллюстрации)
Dim myArray как вариант
мойМассив = Диапазон("A2:D2")

'Присвоить значения в массиве таблице
Активный лист.ListObjects("myTable").ListRows(2).Range.Value = myArray

End Sub 

Ссылка на части таблицы с использованием объекта диапазона

В VBA на таблицу можно ссылаться, как если бы она была стандартным объектом диапазона.

 Sub SelectTablePartsAsRange()

ActiveSheet.Range("myTable[Категория]").Выбрать

End Sub 

Подсчет строк и столбцов

Часто бывает полезно подсчитать количество строк или столбцов. Это хороший способ сослаться на добавленные строки или столбцы.

Подсчет строк

Чтобы подсчитать количество строк в таблице, используйте следующий макрос.

 Sub CountNumberOfRows()

Msgbox ActiveSheet.ListObjects("myTable").ListRows.Count

End Sub 

Подсчет столбцов

Следующий макрос будет подсчитывать количество столбцов в таблице.

 Sub CountNumberOfColumns()

Msgbox ActiveSheet.ListObjects("myTable").ListColumns.Count

End Sub 

Полезные методы работы с таблицами

Ниже приведены некоторые другие полезные коды VBA для управления таблицами.

Показать форму ввода данных таблицы

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

 Подпрограмма ShowDataEntryForm()

'Работает, только если таблица начинается с ячейки A1
ActiveSheet.ShowDataForm

End Sub 

На следующем снимке экрана показана форма данных для примера таблицы.

Проверить, существует ли таблица

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

 Подпрограмма CheckIfTableExists()

'Создайте переменные для хранения рабочего листа и таблицы
Dim ws As рабочий лист
Dim таблица как ListObject
Dim tblName как строка
Dim tblExists As Boolean

имя_таблицы = "мояТаблица"

'Пройтись по каждому рабочему листу
Для каждого ws в ActiveWorkbook.Worksheets

    'Перебрать каждую таблицу на листе
    Для каждой таблицы в ws.ListObjects

        Если имя_таблицы = Имя_таблицы Тогда

            tblExists = Истина

        Конец, если

    Следующая таблица

Следующий мс

Если tblExists = Истина Тогда

    MsgBox "Таблица " & tblName & " существует."

Еще

    MsgBox "Таблица " & tblName & " не существует."

Конец, если

End Sub 

Узнать, была ли выбрана таблица, и если да, то какая

Следующие макросы находят имя выбранной таблицы.

Метод 1

Как вы увидите в комментариях, у Джона Пелтиера был простой подход к этому, который теперь стал моим предпочтительным подходом.

 Sub SimulateActiveTable()

Dim ActiveTable как ListObject

При ошибке Возобновить Далее
Установить ActiveTable = ActiveCell.ListObject
При ошибке Перейти к 0

'Подтвердить, находится ли ячейка в таблице
Если ActiveTable ничто, то
    MsgBox "Выберите таблицу и повторите попытку"
Еще
    MsgBox "Активная ячейка находится в таблице с именем: " & ActiveTable.Имя
Конец, если

End Sub 
Метод 2

Этот вариант, который был моим исходным методом, перебирает каждую таблицу на листе и проверяет, пересекаются ли они с активной ячейкой.

 Sub SimulateActiveTable_Method2()

Dim ActiveTable как ListObject
Dim таблица как ListObject

'Проходим по каждой таблице, проверяем, не пересекается ли таблица с активной ячейкой
Для каждой таблицы в ActiveSheet.ListObjects

    Если Not Intersect(ActiveCell, tbl.Range) ничего не значит, тогда

        Установить ActiveTable = таблица
        MsgBox "Активная ячейка находится в таблице с именем: " & ActiveTable.Имя
    
    Конец, если

Следующая таблица

'Если нет пересечения, значит не выбрана таблица
Если ActiveTable ничто, то

    MsgBox "Выберите таблицу Excel и повторите попытку"

Конец, если

End Sub 

Заключение

Вау! Это было много примеров кода.

Выше приведено более 30 макросов VBA, и даже это не все, но, надеюсь, покроет 99% ваших требований. Для выполнения остальных требований вы можете попробовать справочную библиотеку объектов VBA от Microsoft (https://docs.microsoft.com/en-us/office/vba/api/Excel.ListObject)



Получите нашу БЕСПЛАТНУЮ электронную книгу VBA с 30 наиболее полезными макросами Excel VBA.

Автоматизируйте Excel, чтобы сэкономить время и перестать выполнять работу, которую могла бы выполнять обученная обезьяна.

Получите бесплатную электронную книгу


Не забудьте:

Если вы нашли этот пост полезным или у вас есть лучший подход, оставьте комментарий ниже.

Вам нужна помощь в адаптации этого к вашим потребностям?

Я предполагаю, что примеры в этом посте не совсем соответствуют вашей ситуации.Мы все используем Excel по-разному, поэтому невозможно написать пост, который удовлетворит потребности всех. Потратив время на то, чтобы понять методы и принципы, изложенные в этом посте (и в других местах на этом сайте), вы сможете адаптировать его к своим потребностям.

Но если вы все еще испытываете трудности, вам следует:

  1. Почитайте другие блоги или посмотрите видео на YouTube по той же теме. Вы получите гораздо больше пользы, открыв для себя собственные решения.
  2. Спросите «Excel Ninja» в вашем офисе.Удивительно, какие вещи знают другие люди.
  3. Задайте вопрос на форуме, таком как Mr Excel, или в сообществе Microsoft Answers. Помните, что люди на этих форумах обычно отдают свое время бесплатно. Поэтому постарайтесь сформулировать свой вопрос, убедитесь, что он четкий и лаконичный. Перечислите все, что вы пробовали, и предоставьте скриншоты, сегменты кода и примеры книг.
  4. Используйте Excel Rescue, моего партнера-консультанта. Они помогают, предоставляя решения небольших проблем Excel.]

    Там вы найдете способ добавить книгу в качестве вложения (пример MS):

    Установите myOlApp = CreateObject("Outlook.Application")
    Установите myItem = myOlApp.CreateItem(olMailItem)
    Установите myAttachments = myItem.Attachments
    myAttachments.Add "C:\My Documents\Q496.xls", olByValue, 1, "Таблица результатов за 4 квартал 1996 года" 

    Я предлагаю вам сделать код более профессиональным:
    1) Контекст кода (!),
    2) Использовать Object переменные и в конце концов — очистить используемую ими память,
    3) Используйте обработчики ошибок.
    Например:

    Опция явная
    
    Sub CreateMails ()
    Dim wshInput как рабочий лист, wshData как рабочий лист, wshCalc как рабочий лист, rng как диапазон
    Dim i как целое число, numRows как целое число
    Dim sTo как строка, sSubject как строка, sHTMLBody как строка
        
    При ошибке Перейти к Err_CreateMails
    
    
    Application.ScreenUpdating = Ложь
    
    
    Установите wshInput = ThisWorkbook.Worksheets("Ввод")
    Установить wshData = ThisWorkbook.Worksheets("Диаграмма данных")
    Установите wshCalc = ThisWorkbook.Рабочие листы («вычислить»)
    
    
    numRows = wshData.Range("D5", wshData.Range("D5").End(xlDown)).Rows.Count
    Для i = 0 до numRows
        Если Не ЯвляетсяПустым(wshData.Range("D5")) Тогда
            wshInput.Range("J2").Value = wshCalc.Range("P2").Offset(i, 0).Value
            wshInput.Range("K2").Value = wshCalc.Range("Q2").Offset(i, 0).Value
            wshInput.Range("B7").Value = wshCalc.Range("R2").Offset(i, 0).Value
            wshInput.Range("J3").Value = wshCalc.Range("O2").Offset(i, 0).Ценность
            wshInput.Range("B10:B21").Копировать
            wshCalc.Range("O2").Offset(i, 4).PasteSpecial Paste:=xlPasteValues, Transpose:=True
            sTo = wshInput.Range("N3")
            sSubject = "Это строка темы"
            Установите rng = ThisWorkbook.Worksheets("График").UsedRange
            sHTMLBody = RangetoHTML(rng)
            Если не CreateMailItem(sTo, sSubject, sHTMLBody) — ничто, то
                
            Еще
                
            Конец, если
        Конец, если
    Следующий
    
    Exit_CreateMails:
        При ошибке Возобновить Далее
        Заявление.Обновление экрана = Истина
        Установить wshInput = Ничего
        Установить wshData = Ничего
        Установить wshCalc = Ничего
        Выйти из подпрограммы
    
    Err_CreateMails:
        MsgBox Err.Description, vbExclamation, Err.Number
        Возобновить Exit_CreateMails
    Конец суб 
    Функция CreateMailItem(sTo As String, sSubject As String, sHTMLBody As String) As Object
    Dim OutApp как объект, OutMail как объект
    
    При ошибке Перейти к Err_CreateMailItem
    
    Установите OutApp = CreateObject("Outlook.Заявление")
    Установить OutMail = OutApp.CreateItem(0)
    
    С исходящей почтой
      .To = sTo
      '.CC = ""
      '.BCC = ""
      .Тема = Тема
      .HTMLBody = sHTMLBody
      .Отображать
    Конец с
    
    Exit_CreateMailItem:
        При ошибке Возобновить Далее
        CreateMailItem = Исходящая Почта
        Установите OutMail = Ничего
        Установить OutApp = Ничего
        Выход из функции
    
    Err_CreateMailItem:
        MsgBox Err.Description, vbExclamation, Err.Number
        Возобновить Exit_CreateMailItem
    
    Завершить функцию 
    Изображение подписи

    отображается как красный крест в электронной почте, отправленной Excel VBA

    Дорогие друзья,

    Многие из вас постоянно задают мне этот вопрос о том, что всякий раз, когда ваша подпись Outlook имеет изображение, остальные тексты отображаются правильно, но изображение никогда не было отображается.Он всегда появлялся со знаком красного креста (отсутствующее изображение). Чтобы избавиться от этой проблемы, есть два решения.

    Outlook Подпись с изображением

    Метод 1: трюк при отображении подписи HTML (применим для любой подписи)

    Прежде чем мы перейдем к решению, давайте посмотрим, что пойдет не так, когда мы попытаемся отобразить подпись с изображением через VBA код. Как я объяснил в своем предыдущем посте, Как добавить подписи из Outlook перед отправкой электронной почты через Excel VBA?

    Outlook хранит все подписи в папке AppData в окнах в другом формате, таком как текст, HTML и т. д.Поэтому, чтобы отобразить подпись HTML, сначала мы находим файл HTML, читаем весь файл HTML и добавляем его в конец тела письма. Именно в этом и заключалась проблема, когда в HTML-подписи было изображение.
    Все тексты являются частью HTML, но, как вы знаете, для изображения в HTML есть тег, в котором вы указываете путь, по которому находится изображение. Все изображения хранятся отдельно в папке Signature, как вы можете видеть на рисунке ниже.

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

    Теперь вы знаете проблему, тогда решение простое:

    Прежде чем использовать HTML-код для подписи в конце тела сообщения, нам нужно заменить все пути (неполные URL-адреса) полными URL-адресами.

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

     
    Подпрограмма With_HTML_Signature_With_Image()
    
        'Не забудьте изменить адрес электронной почты
        'перед запуском этого кода
    
        Dim OlApp как объект
        Dim NewMail как объект
        Dim EmailBody как строка
        Dim StrSignature как строка
        Dim sPath как строка
        Dim signImageFolderName As String
        Dim CompleteFolderPath As String
    
        Установите OlApp = CreateObject("Outlook.Application")
        Установить NewMail = OlApp.CreateItem(0)
        
        ' Здесь Поскольку речь идет о
        ' электронное письмо в формате HTML, тогда нам нужно
        ' напишите тело письма в
        ' HTML.EmailBody = "Привет, друзья!!" & "
    
    Добро пожаловать в LearnExcelMacro.com" & vbNewLine & _
        "Здесь я сделаю тебя крутым в макросе Excel.
    
    Вы можете написать мне по адресу [email protected]"
    
        '****************************************************** ****
        '                    Важный
        '****************************************************** ****
        ' перейти к пути к данным приложения, как указано в
        ' выше важный момент. Проверьте имя
        ' файл подписи. Например: здесь, в моей системе
        ' имя файла подписи - vish.txt, виш.htm
        ' Поэтому перед запуском этого кода проверьте
        ' имя файла подписи в вашей системе и
        ' замените vish.txt на ваше имя файла.
        '****************************************************** ***
    
        sPath = Environ("appdata") & "\Microsoft\Signatures\Default.htm"
        'Имя папки с файлами всегда совпадает с именем файла html, к которому добавляется
        ' "_файлы"
        signImageFolderName = "Файлы_по умолчанию"
        ' в Outlook HTML косая черта используется для
        ' путь к файлу
        CompleteFolderPath = Environ("appdata") & "\Microsoft\Signatures\" & signImageFolderName
    
        ' Если указанный вами путь и имя файла не
        ' правильный код вы вставляете пустую подпись
        
        Если Dir(sPath) <> "" Затем
            StrSignature = GetSignature(sPath)
            ' Теперь замените этот неполный путь к файлу
            ' с полным путем везде, где он используется
            СтрПодпись = VBA.Заменить (StrSignature, signImageFolderName, CompleteFolderPath)
        Еще
            СтрПодпись = ""
        Конец, если
    
        При ошибке Возобновить Далее
        С Новой Почтой
            .To = "[email protected]"
            .CC = "[email protected]learnexcelmacro.com"
            .BCC = "[email protected]"
            .Subject = "Введите здесь тему"
            ' Здесь, в конце тела письма
            ' HTML-подпись вставлена.
            .htmlBody = EmailBody и StrSignature
            .отображать
            .Отправить
        Конец с
        При ошибке Перейти к 0
        Установить NeMail = Ничего
        Установить OlApp = Ничего
    Конец сабвуфера
    
      

    Не забудьте скопировать и вставить приведенную ниже функцию в свой модуль
    Функция для чтения файла подписи и возврата текста подписи

     
    Функция GetSignature(fPath как строка) как строка
        Dim fso как объект
        Dim TSet As Object
        Установите fso = CreateObject("Scripting.ФайловаяСистемаОбъект")
        Установите TSet = fso.GetFile(fPath).OpenAsTextStream(1, -2)
        GetSignature=TSet.readall
        TSet.Close
    Конечная функция
    
      

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

    Каждый раз перед отправкой электронной почты, если вы отображаете свою почту, вы можете видеть, что этот метод будет медленным.Поэтому это не подходит для отправки массовых писем в цикле. Кстати, этот метод можно использовать для всех типов подписей (текст, HTML, изображение и т. д.). Но важно отметить, что это применимо только для подписи по умолчанию в Outlook

    Важно . Оператор Display следует использовать где угодно, но перед оператором электронной почты, как описано выше. Если вы отобразите свою электронную почту после размещения тела электронной почты, Outlook не добавит подпись по умолчанию в конце вашего тела электронной почты.

     
    Подпрограмма Outlook_Default_Signature_With_Image()
    
        'Не забудьте изменить адрес электронной почты
        'перед запуском этого кода
    
        Dim OlApp как объект
        Dim NewMail как объект
        Dim EmailBody как строка
        Dim StrSignature как строка
        Dim sPath как строка
    
        Установите OlApp = CreateObject("Outlook.Application")
        Установить NewMail = OlApp.CreateItem(0)
        
        ' Здесь Поскольку речь идет о
        ' электронное письмо в формате HTML, тогда нам нужно
        ' напишите тело письма в
        ' HTML.
    
        EmailBody = "Привет, друзья!!" & "
    
    Добро пожаловать в LearnExcelMacro.com" & vbNewLine & _
        "Здесь я сделаю тебя крутым в макросе Excel.
    
    Вы можете написать мне по адресу [email protected]"
    
        При ошибке Возобновить Далее
        С Новой Почтой
            .To = "[email protected]"
            .CC = "[email protected]"
            .BCC = "[email protected]"
            .Subject = "Введите здесь тему"
            ' Важный! Прежде чем писать тело письма
            ' вы должны отобразить почту
            .        
        

Добавить комментарий

Ваш адрес email не будет опубликован.