⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch31.htm

📁 《Perl 5 Unreleased》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
file, and the decoding functions are declared in <TT><FONT FACE="Courier">P286dec.c</FONT></TT>.

The acid test really is to see if the code compiles. Try these

commands-you should see no errors:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">gcc -c P286enc.c<BR>

gcc -c P286dec.c</FONT></TT>

</BLOCKQUOTE>

<P>

Alas, we still have to write the code to use these functions.

But that's really beyond the scope of this book. What's important

to see is that in a few hours or so, we have created the boring

part of the application and are now ready to proceed with using

these tools.

<P>

During this discussion I have glazed over the details of how the

header and source files are created in the subroutine calls we

made. Let's take a look at the details of how these functions

work.

<H2><A NAME="WritingthecheaderFiles"><B><FONT SIZE=5 COLOR=#FF0000>Writing

the C Header Files</FONT></B></A></H2>

<P>

The first task for generating the header file is to create the

preamble to the <TT><FONT FACE="Courier">include</FONT></TT> file

being created in the file that is pointed to by the <TT><FONT FACE="Courier">HDRS</FONT></TT>

file handle. The code to do this is as follows:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub startHeaderFile() {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;print ( HDRS &quot;#ifndef P286_HDRS\n&quot;,

<BR>

&nbsp;&quot;#define P286_HDRS 1\n&quot;,<BR>

&nbsp;&quot;#define STRPTR&nbsp;&nbsp; char *\n&quot;);<BR>

&nbsp;&nbsp;&nbsp;&nbsp;}</FONT></TT>

</BLOCKQUOTE>

<BLOCKQUOTE>

When the file is closed, you'll want to put in an #endif statement

to allow multiple inclusions of the header file. This is done

with a call to the subroutine sHeaderFile():

</BLOCKQUOTE>

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub closeHeaderFile() {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n#endif\n&quot;;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;}</FONT></TT>

</BLOCKQUOTE>

<P>

The empty pair of parentheses in the function declaration is used

in Perl 5.002 or later to define a function prototype that allows

for no input parameters. When a <TT><FONT FACE="Courier">RECORD</FONT></TT>

header is received, it generates two items: a <TT><FONT FACE="Courier">#define</FONT></TT>

token for a header number and the preamble for the structure to

use. The token is helpful if you want to create a parser that

does a <TT><FONT FACE="Courier">switch()</FONT></TT> statement

on a type of structure. To make sure that each token has a unique

value, a counter is kept in <TT><FONT FACE="Courier">$recordCounter</FONT></TT>

for use in assigning a record type a unique value. The code to

do this is shown here:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub startHeaderRecord {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my ($name) = @_;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n#define P286_%s_RECTYPE

%d &quot;, $name, $recordCounter++;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n\ntypedef struct P286_%s_type

{&quot;, $name;<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

By assigning the <TT><FONT FACE="Courier">@_</FONT></TT> array

to <TT><FONT FACE="Courier">my($name)</FONT></TT> we are actually

permitting more than one argument into the function even though

only the first argument is used. The contents of the <TT><FONT FACE="Courier">@_</FONT></TT>

array are not altered in this case. Using a command like my <TT><FONT FACE="Courier">$name

= shift @_</FONT></TT> would achieve the same purpose but would

also alter the contents of the <TT><FONT FACE="Courier">@_</FONT></TT>

array.

<P>

Each structure definition being created has to be stopped. Two

variable types are constructed: <TT><FONT FACE="Courier">P286_HEADERNAME_TYPE</FONT></TT>

and a pointer type to the structure <TT><FONT FACE="Courier">*P286_HEADERNAME_PTR</FONT></TT>.

The code to do this is shown here:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub stopHeaderRecord($name) {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my $name = shift;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my $ntype = &quot;P286_&quot; . $name

. &quot;_TYPE&quot;;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my $nptr&nbsp;&nbsp;= &quot;*P286_&quot;

. $name . &quot;_PTR;&quot;;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;print HDRS &quot;\n}&quot; .&nbsp;&nbsp;uc($ntype)

. &quot;,&quot; . uc($nptr) .&quot; \n&quot;;<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

Note the syntax used for the function prototype shown previously.

The <TT><FONT FACE="Courier">$name</FONT></TT> variable declaration

in the argument list is only valid in Perl 5.002 or later. Formal

parameter lists to subroutines in Perl are not completely supported

in Perl as we go to print. It does not hurt to be prepared for

the future by including formal parameter lists if they do not

affect the underlying code in the subroutine itself. If you want

to force Perl to take only one parameter into this subroutine,

you can also declare this as

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub stopHeaderRecord($) { &#133; }</FONT></TT>

</BLOCKQUOTE>

<P>

The dollar sign in the parentheses will be used by the Perl interpreter

as an indicator that only one parameter is allowed into the subroutine.

<P>

For a non-arrayed item, three variable types are created: a <TT><FONT FACE="Courier">char</FONT></TT>

string of fixed length, an <TT><FONT FACE="Courier">int</FONT></TT>,

and a <TT><FONT FACE="Courier">double</FONT></TT>. The type of

variable to generate a declaration for is passed in as a parameter

to the <TT><FONT FACE="Courier">makeHeaderItem</FONT></TT> function.

Comments are also generated in the header file pointed to by the

<TT><FONT FACE="Courier">HDRS</FONT></TT> file handle to indicate

what columns the data points to. These comments serve as a cross-

reference for when you are debugging the generated code:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub makeHeaderItem {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my($vname,$vtype,$from,$to) = @_;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my $len;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;if ($vtype eq 'char') {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;$len = $to - $from + 2 ;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n %s %s[%d]\; \/* %d

%d *\/ &quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$vtype,

$vname, $len, $from, $to;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;}<BR>

&nbsp;&nbsp;&nbsp;&nbsp;else<BR>

&nbsp;&nbsp;&nbsp;&nbsp;{<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n %s %s\; \/* %d %d

*\/&quot;, $vtype, $vname, $from,&nbsp;&nbsp;$to;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;}<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

If the variable being generated is in a <TT><FONT FACE="Courier">REPEAT</FONT></TT>

block, it's declared as an array with a call to the <TT><FONT FACE="Courier">MakeArrayedItem()</FONT></TT>

function. The call to generate this arrayed item is

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub makeArrayedItem {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my ($vname,$vtype,$count,$from,$to) =

@_;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf HDRS &quot;\n&nbsp;&nbsp;&nbsp;&nbsp;%s

%s[%d]; \/* from %d %d *\/ &quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$vtype,

$vname, $count, $from,&nbsp;&nbsp;$to;<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

This code generates the <TT><FONT FACE="Courier">P286.h</FONT></TT>

headers file. Now let's look at the files that will contain the

encode and decode functions.

<H2><A NAME="WritingtheEncoderSourceFile"><B><FONT SIZE=5 COLOR=#FF0000>Writing

the Encoder Source File</FONT></B></A></H2>

<P>

To create the C source file to create encoder for the data file,

use the <TT><FONT FACE="Courier">EncD</FONT></TT> file handle.

The subroutine <TT><FONT FACE="Courier">startEncoderFile()</FONT></TT>

starts the preamble for the file, which includes two items. The

first is the call to include the header file, which is also being

generated. The second is to write out the code to a function that

pads spaces to the right of an incoming string to make the length

equal to 80 characters plus a null character. The code to perform

this preamble is shown here:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub startEncoderFile {<BR>

print (EncD &quot;\/*** C source file to encode records.&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\nDon't edit this file\n &quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;*/\n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;'#include &quot;p286.h&quot; ',<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n/* The incoming buffer must be

81 chars! */&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\nvoid padTo80(STRPTR buffer)\n{\n&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;int i,ln;\n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;ln = strlen(buffer);\n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;for(i=ln;i&lt;80;i++) buffer[i]

= ' '; \n &quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;buffer[81] = 0; /* NULL terminate

the string*/&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n} /* end of padding function */\n&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n&quot;);<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

As each new <TT><FONT FACE="Courier">RECORD</FONT></TT> type is

encountered in the input file, its corresponding encoding function

header is created in the output file. A function header is created

that takes a string to put an encoded record in and a pointer

to a structure to unpack. Because the name of the record being

parsed is passed into the code generation function, it's easy

to derive the pointer name for it: <TT><FONT FACE="Courier">P286_HEADERNAME_PTR</FONT></TT>.

The code to accomplish this is shown here:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub startEncoderFunction {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my($vname) = @_;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

# Pick up name of record.<BR>

&nbsp;&nbsp;&nbsp;&nbsp;print (EncD &quot;\n/*: &quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n** Generated

by Perl script -- Avoid editing\n*/\n&quot;);<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;void encode_%s_type(STRPTR

buffer,P286_%s_PTR sp)\n{&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$vname, $vname;

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;print (EncD &quot;\nSTRPTR ncp; \n&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;\nSTRPTR

cp; \n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;register

int i; \n&quot;,<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;char tempbuffer[80];\n&quot;);

<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

When an <TT><FONT FACE="Courier">ENDREC</FONT></TT> line is read,

the function has to be closed. Unmatched <TT><FONT FACE="Courier">RECORD</FONT></TT>

and <TT><FONT FACE="Courier">ENDREC</FONT></TT> lines in the input

file will cause bad, uncompilable code to be generated. The cleanup

at the end of the encode function is done by adding a call the

padding function, <TT><FONT FACE="Courier">padTo80</FONT></TT>,

and printing out the ending curly brace:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub closeEncoderFunction {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;print (EncD &quot;\npadTo80(buffer);&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;\n}

/* End of encoding function */ \n&quot;);<BR>

}</FONT></TT>

</BLOCKQUOTE>

<P>

As with structure declarations, two types of variables have to

be parsed. One is a non-arrayed element and the other is an arrayed

element. However, within the original loop the <TT><FONT FACE="Courier">$vname</FONT></TT>

being passed into this function is already set up as a variable

or a member of an array so that no further processing is necessary.

<P>

The three types of variables used by the parsing encoder are used

to generate the code. If the variable is a string, it's simply

cut and pasted into its columns in the outgoing buffer. If it's

an integer, the value of the integer is printed in the columns

in the output buffer for the integer. For a <TT><FONT FACE="Courier">double</FONT></TT>,

the <TT><FONT FACE="Courier">$fmt</FONT></TT> string contains

the format string to explicitly place the decimal point at the

right location in the columns for the output buffer. The code

to perform this parsing is shown here:

<BLOCKQUOTE>

<TT><FONT FACE="Courier">sub encodeVariable {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my($vname,$vtype,$from,$to,$fmt) = @_;

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;my $len = $to - $from + 1;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\n\n&quot;;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;/* Encode:$vtype,$vname,$from,$to,$len,$fmt

*/&quot;;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;if ($vtype =~ /char/) {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\ncp

= (STRPTR )&amp;(buffer[%d]); &quot;,$from-1;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\nfor

(i=0; i&lt; %d;i++)&quot;,$len;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer[%d

+ i] = sp-&gt;%s[i]; &quot;,$from-1,$vname;<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>

&nbsp;&nbsp;&nbsp;&nbsp;if ($vtype =~ /double/) {<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (length($fmt)

&gt; 0)<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\nsprintf(tempbuffer,\&quot;%%%sf\&quot;,(sp-&gt;%s));&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$fmt,$vname;

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\nsprintf(tempbuffer,\&quot;%%%sf\&quot;,(sp-&gt;%s));&quot;,

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$len,$vname;

<BR>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>

&nbsp;&nbsp;&nbsp;&nbsp;printf EncD &quot;\ntempbuffer[%d]= 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -