BASIC FAQ Hаиболее часто задаваемые вопросы в конференции RU.DOS.BASIC (часть 1) ============================================================= (C) Gregory Zeldner 1999 ~~~~~~~~~~~~~~~~~~~~~~~~ Все права на данный документ принадлежат автору. Приветству- ется некоммерческое использование и распространение в сети Fidonet. Полный или частичный форвард в другие сети катего- рически запрещается. ============================================================= Q: Чем отличаются между собой QuickBASIC, QBASIC и Microsoft BASIC Professional Development System (PDS)? Q: Почему обсуждение Visial BASIC for DOS является оффтопиком? Q: Существуют ли реализации языка BASIC других фирм? Q: Какие существуют конференции со сходной тематикой? Q: Где можно найти QBASIC, QuickBASIC и PDS? Q: Почему никогда не следует использовать оператор GOTO? Q: Что такое модульное программирование? Q: Как ввести малую русскую букву "р" в среде QuickBASIC? Q: Как подключить мышь? Q: Как прочитать содержимое текущей директории? Q: Как вернуть ERRORLEVEL - код завершения программы? Q: Как передать управление большой внешней программе и вернуться обратно? > Q: Чем отличаются между собой QuickBASIC, QBASIC и Microsoft > BASIC Professional Development System (PDS)? QuickBASIC. Создание Microsoft QuickBASIC (сокращенное обозначение Ћ QB) в середине 80-х годов произвело настоящую революцию в мире BASIC, результатом которой было то, что впервые этот язык занял доста- точно прочные позиции среди средств разработки серьезных приклад- ных систем.В QuickBASIC в достаточно полной мере реализованы идеи структурного и модульного программирования, возможности использо- вания процедур и функций. Специфика технологии программирования в среде QB определяет- ся наличием в ней двух трансляторов Ћ интерпретатора и компилято- ра. Основу интегрированной среды, в которой выполняется основной объем разработки и отладки программы, составляет Интеллектуальный редактор и интерпретатор компилирующего типа (ИКТ). ИКТ Ћ это но- вый тип интерпретатора, который производит предварительные "ком- пиляцию и компоновку" программы в специальный псевдокод, а затем уже ее выполнение. При завершении отладки программы пользователь может создать исполняемый EXE-модуль с помощью настоящего ком- пилятора и компоновщика программ. QBASIC. Hачиная с версии MS-DOS 5.0, вместо устаревшего GW-BASIC фирма Microsoft стала поставлять систему QBASIC, которая предс- тавляет собой усеченный вариант QuickBASIC без компилятора и не- которых возможностей модульного программирования. QBASIC, конеч- но, может вполне использоваться для обучения и написания неболь- ших программок, но не для сколь-нибудь серьезной работы. И дело здесь не только в невозможности создавать исполняемые модули. В версии QBASIC программа может состоять только из одного модуля (отсутствует операция LOAD) и следовательно нельзя загружать и использовать ранее созданные модули. А на разработке по принципу "напиши по новой всю программу от начала до конца" далеко, конеч- но, не уедешь. MICROSOFT BASIC PROFESSIONAL DEVELOPMENT SYSTEM (PDS) В 1989 году появилась Microsoft Basic Professional Develoр- ment System (система для профессиональной разработки) версии 7.0, а на следующий год ее сменила версия 7.1. Сегодня ее называют Microsoft BASIC или просто PDS. Это дальнейшее логическое разви- тие QB 4.5 и в этом плане название QuickBASIC Extended (расширен- ный) вполне оправдано. Краткая характеристика Basic PDS, по сравнению с QB 4.5, заключается в следующем: он позволяет создавать более мощные программные комплексы и расширить круг решаемых задач за счет ис- пользования дополнительных возможностей процессора и оперативной памяти, новых средств разработки программ, встроенной системы уп- равления большими базами данных, а также повышения эффективности программного кода (объем памяти, быстродействие). Кроме новых возможностей, в PDS исправлены ряд ошибок QB, в частности, нет проблем с вводом прописной русской буквы "р". Большинство приме- ров программ в данном FAQ ориентированы в первую очередь на ис- пользование PDS. > Q: Почему обсуждение Visial BASIC for DOS является оффтопиком? A: VBDOS является не более чем утяжеленной версией PDS. Абсолютно все возможности VBDOS (за исключением средств визуального прог- раммирования) имеются и в PDS. А для программиста, имеющего навык работы с предыдущими версиями BASIC фирмы Microsoft средства ви- зуального программирования явяются совершенно чужеродными, так как фактически требуют изучить новый язык, не нужный при програм- мировании в текстовом режиме DOS. Именно новый, поскольку объект- ные конструкции Visual интерфейса мало похожи на "человеческий язык", столь милый сердцу приверженцев BASIC, а более напоминают "инопланетные" конструкции C или Pascal. Так что любителям визу- ального программирования в этой эхе делать нечего - их ждут в RU.VISIAL.BASIC. > Q: Существуют ли реализации языка BASIC других фирм? A: Разумеется, фирма Microsoft не является единственным разработ- чиком систем BASIC. Существуют также версии GFA, True, Power, Z и некоторые другие, но они не получили столь широкое распростране- ние Ћ отношение объема продаж языков BASIC фирмы Microsoft к реа- лизациям BASIC остальных фирм составляет 12:1. > Q: Какие существуют конференции со сходной тематикой? A: Таких конференций, насколько мне известно, две. Это междуна- родные конференции BASIC7 (посвященная обсуждению PDS) и QUIK_BAS (QuickBASIC). >Q: Где можно найти QBASIC, QuickBASIC и PDS? Internet: FTP Directory: ftp://195.96.66.201/pub/pds/ PDS71-1.ZIP. . . . . . . . . . . [Nov 17 21:13] 979k PDS71-2.ZIP. . . . . . . . . . . [Nov 17 21:23] 1064k PDS71-3.ZIP. . . . . . . . . . . [Nov 20 09:20] 1010k PDS71-4.ZIP. . . . . . . . . . . [Nov 20 09:33] 1070k PDS71-5.ZIP. . . . . . . . . . . [Nov 20 09:39] 1046k QB45_1.ZIP. . . . . . . . . . . . [Nov 20 09:40] 162k QB45_2.ZIP. . . . . . . . . . . . [Nov 20 09:42] 242k QB45_3.ZIP. . . . . . . . . . . . [Nov 20 09:43] 212k QB45_4.ZIP. . . . . . . . . . . . [Nov 20 09:44] 188k QB45_5.ZIP. . . . . . . . . . . . [Nov 20 09:45] 277k QBASIC.ZIP . . . . . . . . . . . [Nov 17 21:07] 294k FIDO: - Freq from 2:5020/1967 (00:00-05:30 MSK) PDS: BC7_*.ZIP QuickBASIC: QB45_*.ZIP QBASIC: QBASIC@.ZIP а также дpугие полезные пpогpаммы: PCX Programmer ToolKit 3.51: PCX*.ZIP [Если кто-нибудь хочет выкладывать эти программы для свободного доступа - напишите об этом мне, я пополню список :) ] >Q: Почему никогда не следует использовать оператор GOTO? A: Оператор GOTO Ћ старейший и неструктурированный оператор языка BASIC. Программу с обилием GOTO трудно читать, отлаживать, мо- дифицировать, так как она не имеет четкой структуры. Использование в программе оператора GOTO свидетельствует о том, что программист не полностью овладел всем богатством управ- ляющих структур языка QuickBASIC, или не знает их возможностей, а кроме того, не существует программных конструкций, где действи- тельно было бы необходимо применение оператора GOTO. Приведу примеры того, как можно избежать применения GOTO и других устаревших конструкций, которые достались QuickBASIC "по наследству" от GWBASIC. Как получить код нажатой клавиши: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ m1: A$ = INKEY$: IF A$ = "" GOTO m1 Эту конструкцию можно переписать, используя цикл DO...LOOP WHILE: DO: A$= INKEY$: LOOP WHILE A$ = "" Как выйти из цикла по условию: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FOR i = 1 TO 1000 ... IF Flag% = TRUE THEN GOTO m999 ... NEXT i . . . m999: В этом случае можно воспользоваться альтернативным выходом из цикла FOR...NEXT Ћ EXIT FOR: FOR i = 1 TO 1000 ... IF Flag% = TRUE THEN EXIT FOR ... NEXT i . . . Точно также можно выйти и из цикла DO...LOOP: DO ... IF Flag% = TRUE THEN EXIT DO ... LOOP . . . Предвижу вопрос: а если нужно выйти из середины конструкции WHILE...WEND Ћ ведь оператор EXIT WHILE не предусмотрен (также как и EXIT SELECT)? Казалось, бы Ћ ведь в таких случаях можно просто написать: WHILE A$ <> "Конец" ... IF Flag = FALSE THEN GOTO m998 ... WEND . . . m998: Я считаю, что и в этом случае применение оператора GOTO из- лишним. Конечно, чтобы обойти это ограничение приходится идти на хитрость Ћ обрамлять конструкцию бесконечным циклом DO...LOOP и выходить с помощью EXIT DO Ћ даже в этом случае, это гораздо удобнее, нагляднее, и я бы сказал красивее: DO WHILE A$ <> "Конец" ... IF Flag = FALSE THEN EXIT DO ... WEND LOOP . . . Также можно организовать и "EXIT SELECT": DO INPUT Kod SELECT CASE KOD CASE IS = ... ... CASE IS = 0: EXIT DO ... CASE ELSE ... ... END SELECT LOOP Как избежать синдрома "±жика в тумане" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ При организации ветвления бесконечные операторы GOTO мешают понять логику программы: 30 INPUT A IF A > 100 THEN PRINT "Это много": GOTO 30 IF A = 100 THEN GOTO 300 PRINT A/100: GOTO 30 ... 300 ... Вместо этого можно использовать или блочную форму оператора IF...THEN...ELSE в окружении цикла DO...LOOP WHILE: DO INPUT A IF A < 100 THEN PRINT A / 100 ELSEIF A > 100 THEN PRINT "слишком много" END IF LOOP WHILE A <> 100 . . . ...или конструкцию SELECT...CASE в окружении цикла DO ... LOOP DO INPUT A SELECT CASE A CASE IS < 100: PRINT A / 100 CASE IS > 100: PRINT "слишком много" CASE IS = 100: EXIT DO END SELECT LOOP . . . Как правильно "разветвиться" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ При организации ветвления вместо устаревшей управляющей конструкции ON...GOSUB используйте более современную кон-струкцию SELECT...END SELECT: m0: INPUT A ON A GOSUB m1, m2, m3, m3, m4, m4, m4 GOTO m5 m1: ... X = y * z ... RETURN m2: ... RETURN m3: ... RETURN m4: ... RETURN m5: ... Hе правда ли, с первого раза трудно понять, что это фрагмент программы делает. Приходится проговаривать про себя: "Если значе- ние A равно единице, то ... перейти на метку... метку m1, вычис- лить значение X ... вернуться на следующий оператор пос-ле ON...GOSUB ... это переход на метку m5 ... метка m5 Ћ это продол- жение выполнения программы..." Посмотрите, насколько удобнее использование конструкции SE- LECT...END SELECT: INPUT A SELECT CASE A CASE IS = 1: ... X = y * z ... CASE IS = 2 ... CASE IS = 3, 4 ... CASE 5 TO 7 ... END SELECT "Если значение A равно единице, вычислить значение X и по- кинуть SELECT...END SELECT". >Q: Что такое модульное программирование? При разработке собственной программы у каждого программиста через некоторое время появляется большой набор собственных заго- товок, неординарных решений и т. д., которые он хотел бы исполь- зовать во всех своих творениях. QuickBASIC предоставляет такую возможность, позволяя разра- батывать программы по модульному принципу. Модуль Ћ это отдель- ная, полностью независимая от других, часть Вашей программы. Каж- дая программа на QuickBASIC может состоять из одного или несколь- ких модулей. Каждый модуль имеет главную часть. В главной части модуля описываются процедуры и функции модуля (операторами DECLARE), функции DEF FN, константы (оператор CONST). В сpеде для создания пpоцедуp и функций испольуется команда "Edit - New Sub" и "Edit - New Function". Пpосмотp текущих пpоце- дуp и функций вызывается по клавише F2. Один из модулей называется главным. Он содержит так называе- мую точку входа, с которой начинается выполнение программы. В каждой программе может быть только один главный модуль. MAIN.BAS ЌЋ SUB One ЉЋ FUNCTION Two К главному модулю можно подключить один или несколько вспо- могательных (дополнительных) модулей. В чем же преимущество мо- дульного программирования? MAIN.BAS ЌЋ SUB one ЉЋ FUNCTION two ADD-ON.BAS ЌЋ SUB three ЌЋ SUB forth ЉЋ FINCTION six Так как вспомогательные модули Ћ это отдельные файлы прог- рамм, в них обычно выносятся процедуры и функции, которые исполь- зуются одновременно в нескольких программах. Таким образом, вы как бы собираете программу из готовых блоков (модулей); OTHER.BAS ЌЋ SUB one ЉЋ FUNCTION two ADD-ON.BAS ЌЋ SUB three ЌЋ SUB forth ЉЋ FINCTION six Если вы дополняете или исправляете какую-либо процедуру или функцию из вспомогательного модуля, то все исправления автомати- чески становятся доступны всем Вашим программы, которые эти моду- ли используют; Сосредоточение вспомогательных модулей в одном месте облег- чает поиск нужного фрагмента исходного текста, и позволяет избе- жать ситуации, когда одна и та же процедура существует в несколь- ких вариантах в каждой программе. >Q: Как ввести малую русскую букву "р" в среде QuickBASIC? A: К сожалению, стандартными средствами (INPUT, INPUT$, LINE IN- PUT, INKEY$) невозможно ввести символ с кодом 224 Ћ это малая русская буква "р" в альтернативной кодировке. Это связано с не- корректным опросом клавиатуры, который выполняет BASIC. Есть нес- колько вариантов решения. 1. Использовать драйвер русских букв с изменяемой раскладкой клавиатуры, который бы подставлял английскую букву "p" вместо русской "р", например KEYRUS. Однако, если вы вводите таким спо- собом, скажем фамилии, то их невозможно будет отсортировать по алфавиту; 2. Другим вариантом является использование "примочек" к фи- дошному редактору сообщений GoldED, например GEDSTART.COM. Эта программа подменяет при вводе текста не только "H", но и "р". Hе- достатки такого подхода те же, что и описанные выше. 3. Отказаться от стандартных операторов BASIC и опрашивать клавиатуру самостоятельно, используя прерывание BIOS 16h, функция 0. Для использования прерываний необходимо загрузить среду с па- раметром "/L". === cut CHAR.BAS === '$INCLUDE: 'interupt.bi' DECLARE SUB Char (c$) ' Define the type needed for Interrupt TYPE RegType ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER si AS INTEGER di AS INTEGER flags AS INTEGER END TYPE DIM SHARED Inreg AS RegType DIM SHARED Outreg AS RegType ' Вызов процедуры CHAR, печать всех вводимых символов ' ESC - конец работы программы DO CALL Char(a$): PRINT a$; IF a$ = CHR$(27) THEN EXIT DO LOOP END SUB Char (c$) ' ***************************************************** ' Данная процедура корректно обрабатывает ввод всех ' символов ASCII, включая малую русскую букву "р" ' используя прерывание BIOS 16h, функция 0. ' Вызов: CALL Char (variable$) ' Аналог: ' DO: variable$ = INKEY$: LOOP WHILE variable$ = "" ' ***************************************************** n = &H16: ' прерывание 16h inreg.ax = 0: ' функция 0 CALL Interrupt(n, inreg, outreg) nah = outreg.ax \ 256: nal = outreg.ax MOD 256 c$ = CHR$(nal): IF nal = 0 THEN c$ = c$ + CHR$(nah) END SUB === end CHAR.BAS === 4. Пропатчить саму среду QuickBASIC и все необходимые для компиляции библиотеки: === cut QB45.CRK === Russian "p" for QuickBasic 4.5 (c) Igor Knizhny (2:5020/1343.20) File QB.EXE qb.exe 00024A75: 3C 0A 00024A76: E0 E4 00024A78: 14 04 00024A7A: F0 E0 File BRUN45.EXE brun45.exe 00007C97: 3C 0A 00007C98: E0 E4 00007C9A: 14 04 00007C9C: F0 E0 File BCOM45.LIB bcom45.lib 0001C83C: 3C 0A 0001C83D: E0 E4 0001C83F: 14 04 0001C841: F0 E0 === end QB45.CRK === 5. Перейти на следующую версию языка Ћ Microsoft BASIC Pro- fessional Development System, где эта ошибка, наконец, исправле- на. >Q: Как подключить мышь? A: Процедура MOUSE, использующая прерывание INT 33H позволяет уп- равлять мышью из программы на языке BASIC. Для использования пре- рываний необходимо загрузить среду с параметром "/L". === cut MOUSE.BAS === '***************************************** ' 'MOUSETST.BAS - интерфейс с драйвером мыши ' '***************************************** DEFINT A-Z DECLARE SUB MOUSE (m1%, m2%, m3%, m4%) ' Define the type needed for Interrupt TYPE RegType ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER si AS INTEGER di AS INTEGER flags AS INTEGER END TYPE DIM SHARED Inreg AS RegType DIM SHARED Outreg AS RegType SCREEN 12 'включить курсор мыши G1% = 1: CALL MOUSE(G1%, G2%, G3%, G4%) ' Пpочитать кооpдинаты кypсоpа и статyс кнопок DO G1% = 3: CALL MOUSE(G1%, G2%, G3%, G4%) LOCATE 1, 1: PRINT "Кооpдинаты мыши : X ="; G3%; " Y ="; G4%; " " LOCATE 3, 1 SELECT CASE G2% CASE IS = 1: PRINT "Hажата левая кнопка " CASE IS = 2: PRINT "Hажата пpавая кнопка " CASE IS = 4: PRINT "Hажата сpедняя кнопка" CASE ELSE: PRINT "Кнопки не нажаты " END SELECT IF INKEY$ <> "" THEN EXIT DO LOOP 'Погасить курсор мыши G1% = 2: CALL MOUSE(G1%, G2%, G3%, G4%) SCREEN 0 SUB MOUSE (m1, m2, m3, m4) ' ***************************************************** ' Эта пpоцедypа обеспечивает интеpфейс с дpайвеpом мыши ' m1, m2, m3, m4 - паpаметpы, пеpедаваемые в дpайвеp мыши ' и возвpащаемые оттyда. Они соответствyют pегистpам ' пpоцессоpа AX, BX, CX, DX ' ***************************************************** n = &H33: ' пpеpывание 33h Inreg.ax = m1 ' входные pегистpы Inreg.bx = m2 Inreg.cx = m3 Inreg.dx = m4 CALL Interrupt(n, Inreg, Outreg) m1 = Outreg.ax ' выходные pегистpы m2 = Outreg.bx m3 = Outreg.cx m4 = Outreg.dx END SUB === end MOUSE.BAS === >Q: Как прочитать содержимое текущей директории? В PDS используется функция DIR$. При первом запуске функции передается маска для поиска файлов и возвращается первое имя фай- ла по заданной маске. При следующих запусках возвращаются имена остальных файлов: === cut LIST.BAS === '*********************************************************** ' 'LIST.BAS - программа чтения содержимого текущей директории ' '*********************************************************** DEFINT A-Z REDIM FileName$(1) 'определяем маску для поиска файлов maska$ = "*.*" count = 1: CLS 'первый файл FileName$(1) = DIR$(maska$) IF FileName$(1) <> "" THEN 'cледующий файл DO FindNext$ = DIR$ IF FindNext$ <> "" THEN count = count + 1 REDIM PRESERVE FileName$(count) FileName$(count) = FindNext$ ELSE EXIT DO END IF LOOP ELSE PRINT "В текущей директории нет файлов": END END IF 'вывод результатов PRINT "Всего найдено файлов:"; count PRINT FOR i = 1 TO count PRINT FileName$(i) NEXT i 'ждем нажатия любой клавиши DO: LOOP WHILE INKEY$ = "" END === end LIST.BAS === Часто при программировании возникает задача проверки существо- вания того или иного файла. Для этого также можно использовать функцию DIR$: IF DIR$(filename$)<> "" THEN PRINT "Файл существует" ELSE PRINT "Файл не существует" END IF Q: Как вернуть ERRORLEVEL - код завершения программы? В PDS операторы окончания программы (END, SYSTEM, STOP) имеют необязательный параметр - код завершения. Это должно быть целое число в интервале от 0 до 255. ERRORLEVEL используется в команд- ных файлах DOS для организации ветвления. === cut ERROR.BAS === IF OK THEN END 0 ELSE END 64 END IF === end ERROR.BAS === >Q: Как передать управление большой внешней программе и вернуться > обратно? Оператор SHELL позволяет передать управления внешней прог- рамме, а после ее завершения вернуться в основную программу. Од- нако при этом память, занимаемая основной программой не освобож- дается и может возникнуть ситуация когда ее станет недостаточно для внешней программы. Оператор CHAIN освобождает память, занимаемую основной прог- раммой и запускает внешнюю, но при этом возникает вопрос - а как же вернуться в основную программу? Конечно, можно решить эту проблему, запуская командный файл и организуя ветвление по ERROR- LEVEL. Hо есть способ лучше - полная передача управления програм- ме-спутнику, которая запускает внешнюю программу через SHELL, а затем передает управление основной программе: MAIN.BAS Ћ основная программа BIG.EXE Ћ внешняя программа QBSWAP.EXE Ћ решение проблемы :) === cut MAIN.BAS === . . . CHAIN "QBSWAP" . . . === end MAIN.BAS === === cut QBSWAP.BAS === SHELL "BIG" CHAIN "MAIN" === end QBSWAP.BAS ===