Console Mode Pascal/zh CN

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) suomi (fi) magyar (hu) 日本語 (ja) русский (ru) slovenčina (sk) 中文(中国大陆) (zh_CN)

Pascal 控制台模式编程

在[[[Graphical User Interface|图形用户界面]](GUI)和集成开发环境(IDE)尚未流行时,很多人就已经开始编写 Pascal 程序了。还有许多人刚开始学习 Pascal 编程,需要能简单测试一下。另外还有些人则需要编写控制台文本模式的应用程序,以便执行复杂的系统控制任务。

不用 Lazarus 进行控制台模式编程

虽然很多人愿意用 Lazarus IDE 编写控制台程序,但用任一文本编辑器编写代码,再用FPC 编译器进行编译,也是没问题的。假设程序名为 example.pas,则可以如下执行编译:

fpc example.pas

此外还可以用 fp IDE(有点类似以前的Turbo Pascal)。 后续内容主要介绍使用 Lazarus 编程。

用 Lazarus 进行控制台模式编程

Lazarus 为学习 Pascal 语言和开发文本模式程序提供了理想的环境。集成开发环境的所有功能均有提供,包括带语法高亮功能的源代码编辑器、访问已有程序库、复杂搜索和代码补全工具、语法检查。如果用不到带有可视化组件的Form,那 Lazarus 就不是必需的,但 Lazarus 的源代码编辑器依然是程序开发的绝佳环境。这样在开发过程中,不用离开编辑器即可编译和运行程序。

若要创建一个控制台模式的程序,请在主菜单选择“项目”->“新建项目”,然后选择“程序”或“控制台应用程序”。IDE 不会生成完整GUI程序关联的全部文件,也不会打开对象查看器,而会打开源代码编辑器并给出框架代码,等待输入。

Light bulb  Note: Windows GUI 应用程序没有控制台,因此无法使用 writelnreadln。报错信息会是 File not open。冲黄创建控制台应用程序时,请禁用“项目选项/编译器选项/配置和目标/特定目标选项/Win32 图形界面应用程序”选项。
Light bulb  Note: Lazarus 为命令行程序提供了一些函数,例如 copyfile。若要使用这些函数,请为项目添加 LazUtils 需求,这样不会引入整个 LCL 库。然后,在 uses 子句中添加相应单元。

项目类型:控制台应用程序

在 Lazarus 中选择这种项目类型,将会创建一个派生自 TCustomApplication 的新类。TCustomApplication 提供了很多常用功能,编写命令行程序会更为轻松。例如,检查命令行参数、编写帮助信息、检查环境变量、处理异常。所有 LCL 程序都自动拥有这些功能。

项目类型:程序

作为演示,下面给出一个十分简化的 Pascal 程序。在 IDE 中选择“程序”项目类型,IDE 会提供一些辅助功能。比如在添加其他单元时,IDE 会自动将该单元的名称加入uses部分。这是在项目选项中定义的。因此“程序”和“自定义程序”类型之间可以随时切换。

下面是给小白看的一个示例:

program Project1;
{$mode objfpc}{$H+}
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes;
begin
  WriteLn('Hello World!');
  ReadLn;
end.

Pascal 脚本

此外,还可以编写由InstantFPC动态编译的脚本,InstantFPC 是一个跨平台解决方案,能以脚本的形式运行(小型)Pascal 程序。shebang

#!/usr/bin/env instantfpc

即可像运行独立程序一样执行脚本。

Light bulb  Note: 尽管 InstantFPC 可用来运行 Pascal 脚本,但采用标准的 Object Pascal 语言,而不是 PascalScript 脚本语言。因此,“PascalScript”和“Pascal 脚本”不是一个概念。

运行程序

编译

程序(或程序片段)编写完成后,可从主菜单中选择“运行”->“构建”(或“快速编译”),或者点击按钮栏中的绿三角Start.png(运行)Start.png,即可编译并运行。所有编译器信息(警告、进度或错误消息)都会在消息框中显示,幸运的话最终会显示一条消息:

'Project "Project1" successfully built.:)'

可是程序在哪里呢??!!

如果程序尚未保存过,那么 IDE 会将其放入临时目录(例如,在 Linux 下是 /tmp,在 Windows 下是 C:\temp,参见“工具/选项/环境/文件/项目构建测试的目录)。

如果项目已经保存过了,那么程序会在 project1.lpi 同目录下创建。

在控制台中运行

进入控制台(终端)窗口,用cd命令修改当前目录,键入程序名称,即可执行程序。在 Unix/Linux 中,如果程序位于当前目录,必须在程序名前面加上 ./,因为由 PATH 可能会找不到程序。

以下是 Linux/Unix 中的例子,程序存于 /tmp 下:

cd /tmp 
./Project1

不过,在 Lazarus 编辑器和终端窗口之间不停地切换,可能很不方便。幸运的是,有一种机制允许在 Lazarus 中直接打开终端窗口。

在 IDE 中运行

在内部控制台:终端输出窗口中运行。

重定向输出。

在主菜单选择“运行->运行参数”,选中“使用开始(launching)应用程序”。第一次运行会执行编译、运行的步骤,可能会直接收到一条信息:

"xterm: Can't execvp /usr/share/lazarus//tools/runwait.sh: Permission denied".  

这时需修改对应文件的权限(比如用 chmod +x 文件名 命令,或者用 Windows 的修改权限功能);可能需要以管理员身份才能修改权限。之后每次启动程序,都会出现一个控制台窗口,所有的文本输入/输出(如 readln、writeln 等)都会显示在该窗口中。

程序执行完毕后,屏幕上会显示“Press enter”。因此程序产生的输出都会保留在屏幕上,以供阅读;按下“回车”键将会关闭控制台窗口。

在 Linux 系统中,如果勾选“使用开始(launching)应用程序”后没有出现上述“xterm: Can't execvp /usr/share/lazarus//tools/runwait.sh: Permission denied”,请键入/usr/bin/xterm $(TargetCmdLine) 并勾选“使用开始(launching)应用程序”(几乎所有 Linux 系统都安装 xterm 了)。

很遗憾,这种运行模式不支持集成调试器。

在 IDE 中运行并重定向输出

若要看到写入 stdout 的结果且要使用集成调试器,可以将 stdout 重定向至一个文件,代码如下:

uses
  baseunix; 

var
  OutputFile: text;

begin     
  Assign(OutputFile, 'Output.txt');
  if FileExists(Filename) then begin
    Append(OutputFile);
  end
  else begin
    Rewrite(OutputFile);   { 打开文件以供写入,清空原有内容 }
  end;

  ResultCode := fpdup(OutputFile, output);

  if ResultCode < 0 then begin
    raise Exception.CreateFmt('dup failed: %s', [ResultCode]);
  end;
  Close(OutputFile);
end.

Output.txt 可用“tail -f output.txt”命令查看,如果操作系统不提供“tail”程序则可用文本编辑器打开。

在 Lazarus 0.9.31 以上版本(仅限 Linux 系统)中,还可以用“查看”菜单下的“调试窗口/控制台输出”查看 stdout。

Unicode(UTF8)输出

若要控制台程序在 Windows Vista 以上版本(或更低版本)中显示 Unicode(UTF8)输出结果,可用SetConsoleOutputCP命令将控制台设为 UTF8 字符集。

Light bulb  Note: 请确保控制台的字体能够支持输出的字符(如希腊语、西里尔字母、韩语等)。
Light bulb  Note: 需包含 Windows 单元

关于 Lazarus 和 FPC 对 Unicode 的支持, 详见 LCL Unicode 支持

例程如下:

program uniconsole;

{$mode objfpc}{$H+}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
    {$IFDEF UseCThreads}
    cthreads,
    {$ENDIF}
  {Widestring manager needed for widestring support}
  cwstring,
  {$ENDIF}
  {$IFDEF WINDOWS}
  Windows, {for setconsoleoutputcp}
  {$ENDIF}
  Classes
  ;

var
UTF8TestString: string;

begin
{$IFDEF WINDOWS}
SetConsoleOutputCP(CP_UTF8);
{$ENDIF}
UTF8TestString:= 'rosé, водка and ούζο';
writeln ('plain: ' + UTF8TestString);
{显然这里不需要用到 UTF8ToConsole,其实 UTF8ToConsole 也无效。}
end.

在 Windows 控制台输出 CJK 文本

以下示例来自论坛成员Thaddy,演示了如何正确设置 Windows 控制台以支持 CJK 文本。

{$mode delphiunicode}{$H+}
{$codepage utf8}
uses Windows, SysUtils;
var
  s3, s1, s2: string;
  var
  cfi: CONSOLE_FONT_INFOEX;
begin
  cfi.cbSize := SizeOf(cfi);
  cfi.nFont := 0;
  cfi.dwFontSize.X := 0;
  cfi.dwFontSize.Y := 20;
  cfi.FontFamily := FF_DONTCARE;
  cfi.FontWeight := FW_NORMAL;
  // two fonts that support Chinese etc.
  cfi.Facename := 'NSimSun';
  // cfi.FaceName := 'SimSun-extB'; // 这是个光栅字体

  SetTextCodePage(Output, CP_UTF8);
  SetConsoleOutputCP(CP_UTF8);
  SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), False, @cfi);

  s1 := '春夏';
  s2 := '秋冬';
  s3 := s1+s2;
  Writeln('s1=', s1,'  s2=', s2, '  s3=', s3);
  Readln;
end.

示例

只要是标准 Pascal 教材中的示例,均可用 Lazarus 编辑器进行试验,也可以编写自己的程序。最有用的是那些执行系统命令或运行其他程序的过程(这些外部程序可以是用 Pascal、C、Perl 编写的,也可以是 shell 或批处理脚本)。

执行 shell 命令

以下是执行自定义程序系统自带程序的示例,不过是 Linux/Unix/macOS 系统特有的:

program TryShell;
uses 
  Classes, Unix;
var 
  S: LongInt;
begin
  S := fpsystem('/bin/ls -la *.p*'); //lists .pp, .pas, .php, .png etc in current directory
  WriteLn('Program exited with status : ', S)
end.

更新 FPC 和 Lazarus 的示例

还可以执行一些更复杂的命令。比如在签出 FPC 和 Lazarus 的 SVN 库(参见buildfaq)后,可通过以下调用从 SVN 存储库中获取 FPC 和 Lazarus 的最新源代码:

program LazUpdate;
uses
  Classes, Unix;
var 
  S: LongInt;
begin 
  S := fpsystem('cd /usr/local/src/fpc/devel/fpc        ; make clean');
  S := fpsystem('cd /usr/local/src/fpc/devel/lazarus  ; make clean');
  S := fpsystem('cd /usr/local/src/fpc/devel             ; svn update fpc       >& ~/cvscheckout.log');
  S := fpsystem('cd /usr/local/src/fpc/devel             ; svn update lazarus >& ~/cvslaz.log'        );
end.

请注意,以下命令:

fpsystem('cd /somedirectory');

紧跟着以下命令

fpsystem ('do something in that subdirectory');

无法生效,因为每次调用fpsystem()函数后,程序都会返回初始目录再执行;因此,通过fpsystem()调用 shell 命令时,需在一行中放入多条命令。(更准确的说法是:fpsystem 总是从当前目录开始执行)

不一定要把每条命令都写成一条 Pascal 语句;可以如下创建bash脚本文件(来自buildfaq):

#!/bin/sh
cd /usr/local/src/fpc/devel
cd fpc
make clean
cd ..
cd lazarus
make clean
cd ..
svn up fpc >& ~/cvscheckout.log
svn up lazarus >& ~/cvslaz.log

将其命名为 updatelaz.sh,然后不是直接用bash去调用,而是用 Pascal 程序去调用:

(* This can be compiled or used as a Pascal script *)
program LazUpdate1;
uses 
  Classes, Unix;
var 
  S: LongInt; 
begin
  S := fpsystem('updatelaz.sh')
end.

参阅