- New Features
- Reading External Files with Perl
- Reading an External File through a Browser
- Project I: Automating Articles
- Project II: Writing to External Files
- Recap
- Advanced Projects
Project II: Writing to External Files
Files Used |
Permissions |
write.cgi |
755 |
write.html |
744 |
numbers (you'll need to create this directory) |
755 |
In the past few scripts you learned how to open up external files and display the output to the browser. With the write.cgi script and write.html file, you will see how to use an HTML interface to enter information into three fields, then have the script write that to an individual HTML file to a specified directory. In this case, you will use a directory named numbers.
Once this is done, the script will then list all of the files it has written to the numbers directory with a hot-link to them so you may view them.
NEW FEATURES
Writing to a file
open(FILEHANDLE, ">file.html");
> will write to a file. If copy already exists in this file it will be overwritten.
open(FILEHANDLE, ">>file.html");
>> will append to the file. If content exists, the new content will be added to the end of the file, thus preserving the existing content.
opendir
opendir(FILEHANDLE, "directory");
Similar to the open function where you specify a FILEHANDLE for a folder instead of a file to be opened.
closedir
closedir(FILEHANDLE);
Just like the open function had to be closed, now the opendir function must be closed.
readdir
The readdir function gives us a list of all the files and directories contained in the FILEHANDLE specified. When used in a scalar context, readdir returns the next filename. When using readdir in an array context, all remaining files and directories are placed into an array as a list with one name per element. Also, the order in which the files and directories are given corresponds exactly to the order in which the filesystem stores the files and directories. Here are some examples:
opendir(DIR, "."); foreach $name (readdir(DIR)) { print "$name\n"; } closedir(DIR);
This prints out every file and directory residing in the current directory (.). We can also use the sort command to display the names sorted alphabetically; simply replace the second line with:
foreach $name (sort readdir(DIR))
Also, we can put the contents of a directory into an array like this:
@files = sort readdir(DIR);
push
The push command simply adds a scalar value to the end of an array.
push(@thisarray, $somevalue);
So, if the elements of @thisarray were (1, 2, 3, 4), then after executing this line @thisarray would contain (1, 2, 3, 4, $somevalue).
pop
Although not used in this script, the pop command does the exact opposite of push: It removes the last element of an array and returns it.
$lastelement = pop(@thisarray);
subroutines
Subroutines are a series of functions that perform a particular task or set of tasks. This is a good way to set up a subroutine that you can call from within the script every time you need it rather than writing it out every time you use it.
Syntax
sub some_name { statements; }
Use &subroutine_name; to call the subroutine.
Now you're ready to write your first interface to enter content and have it sent to the script.
FIGURE 11 Interface to the write.cgi script. The contents of these fields will be parsed to write.cgi and then displayed in a list.
HTML for 15write.html
<HTML> <HEAD> <TITLE>Write</TITLE> </HEAD> <BODY> <FORM METHOD="POST" ACTION="write.cgi"> <P>Title:<BR> <INPUT TYPE="text" NAME="title" SIZE"20"></P> <P>Heading:<BR> <INPUT TYPE="text" NAME="heading" SIZE="20"></P> <P>Body:<BR> <TEXTAREA ROWS="4" NAME="body" COLS="20" WRAP="virtual"></TEXTAREA></P> <P><INPUT TYPE="submit" VALUE="Submit" NAME="submit"> <INPUT TYPE="reset" VALUE="Reset" NAME="reset"></P> </FORM> </BODY> </HTML>
Script 15write.cgi
#!/usr/bin/perl 1. &get_form_data; print "Content-type: text/html\n\n"; 2. opendir(DIR, "./numbers"); 3. while($name = readdir(DIR)) { 4. next if $name !~ /^\d*.html/; 5. push(@files, $name); } 6. close(DIR); 7. if($#files == 0) { 8. $nextfile = "1.html"; } 9. else { 10. $lastfile = $files[$#files]; 11. $lastfile =~ s/.html//g; 12. $nextfile = $lastfile + 1; 13. $nextfile .= ".html"; } 14. open(OUT, ">numbers/$nextfile"); 15. print OUT "<HTML>\n<HEAD>\n "; 16. print OUT "<TITLE>\n"; 17. print OUT "$FORM{'title'}\n"; 18. print OUT "</TITLE>\n"; 19. print OUT "</HEAD>\n"; 20. print OUT "<BODY BGCOLOR=\"#FFFFFF\">\n"; 21. print OUT "<H1>\n"; 22. print OUT "$FORM{'heading'}\n"; 23. print OUT "</H1>\n"; 24. print OUT "<P>\n"; 25. print OUT "$FORM{'body'}\n"; 26. close(OUT); 27. push(@files, $nextfile); 28. print "<HTML>\n<BODY>\n"; 29. foreach $file (@files) { 30. print "<A HREF=\"numbers/$file\">$file</A>\n"; print "<BR>\n"; } print "</BODY>\n</HTML>\n"; 31. exit; 32. sub get_form_data { # Get the input read(STDIN, $buffer, $ENV{ 'CONTENT_LENGTH' } ); # Split the name-value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); # Un-Webify plus signs and %-encoding $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s/<!--(.|\n)*-->//g; $FORM{ $name } = $value; } }
HOW THE SCRIPT WORKS
1. &get_form_data; calls the subroutine get_form_data, which is located at the end of the script, and which parses the user output.
2. The opendir command gets a listing of all the files in the directory specified. The ./ command tells the script to look in the local directory in the numbers subdirectory and to assign DIR as the FILEHANDLE.
3. This loops through each file and directory in the ./numbers directory, with $name containing a single file/directory for that iteration.
4. The next command uses regular expressions to skip this iteration of the current loop structure (the while) only if the file does not match (!~) the following:
The beginning of the file starts with a digit (^ means beginning of the file, \d means any digit from 09), followed by any number of digits. (The * matches any character. Since it follows the \d, it only matches more digits, instead of any character). Then, it matches .html. So, any file such as 5.html, 1231.html, 1.html, and so forth, will be matched.
5. push adds the filename to the @files array. Remember that this is only done if the regular expression in line 4 fails (that is, we don't skip through this iteration of the loop).
6. This line checks to see if the @files array is empty ($#arrayname is the length of arrayname).
7. If the array is empty, then this is our first run through. We do not have any files such as 1.html or 5.html.
8. If line 7 were true then we start with 1.html, by assigning it to $nextfile.
9. $lastfile is assigned the last element in the @files array (from line 6, $#arrayname is the length of the array, which is also the number of elements in the array).
10. Using regular expressions, remove .html from the filename.
11. Add 1 to the value of $nextfile (since we removed the .html from the filename, all we have left is a number).
12. The .= character is used to append .html to $nextfile.
13. Create a file with the name of the value of $nextfile in the /numbers directory.
1425. Print out HTML content to the OUT FILEHANDLE, which appends the content to the end of the file.
26. Close the OUT FILEHANDLE.
27. Add the value of $nextfile to the @files array.
28. Print the opening HTML tags that will be shown to the browser.
29. Loop through each element in the @files array.
30. Print out a link to each file along with the filename itself, separated by a line break.
31. Quit executing the script.
32. The get_form_data function reads in the form submission content and places each value into the %FORM associative array.
Once you have write.cgi and write.html saved, you will use write.html to fill in the three fields (title, heading, and body) with content. Then write.cgi will be accessed and write a file using the content that was just entered (see Figure 12).
FIGURE 12 Results of the write.cgi script