Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

CUFP: Random tag insertion

by William G. Davis (Friar)
on Nov 20, 2003 at 22:55 UTC ( #308740=CUFP: print w/replies, xml ) Need Help??

(Updated, again, 11/26/03—Thanks Roy and Coruscate.)

A while ago on /., while viewing at -1, I witnessed one, um, "condemned" post that utilized a rather bizarre style of markup; one where bold tags were randomly inserted around letters, creating an eye-catching/unique/annoying effect. I've tried to recreate that using Perl, and this script is the result.

Run the script with -h for usage:

#!/usr/bin/perl -w use 5.004; use strict; use Getopt::Std; srand($$ ^ time); # get the options: my %opts; usage() unless (getopts('t:i:o:h', \%opts)); usage() if ($opts{'h'} || !$opts{'t'}); # the opening and closing tags we'll insert into the text: my $opening_tag = "<$opts{'t'}>"; my $closing_tag = "</$opts{'t'}>"; # open the input file for reading, or, if no file was specified, get s +ome text # from the console: my $text_from_console; if ($opts{'i'}) { open(INFILE, "< $opts{'i'}") || die "Couldn't open file ($opts{'i'}) to read text: $!"; } else { print "Enter some text: "; $text_from_console = <STDIN>; } # now randomly insert the specified HTML tag into the text: my $tagged_text = ''; my $unclosed_tag = 0; while (my $line = $opts{'i'} ? <INFILE> : $text_from_console) { my $char; while (length($char = substr($line, 0, 1, ''))) { if ($char !~ /\s/ # skip spaces. and int rand 2) # 0 or 1. { # add the opening tag unless the previous character is # within a tag that has yet to be closed: $tagged_text .= $opening_tag unless ($unclosed_tag); $tagged_text .= $char; $unclosed_tag = 1; } else { # Either this is a space or a non-space character that # was rejected by rand(). Regardless, close the last tag # if it's still open: if ($unclosed_tag) { $tagged_text .= $closing_tag; $unclosed_tag = 0; } $tagged_text .= $char; } } # break if we got text from the console instead of from a file, si +nce # there's nothing left: last unless ($opts{'i'}); } close INFILE if ($opts{'i'}); if (my $output_file = $opts{'o'} || $opts{'i'}) { # print the text with tags inserted into it to a file: open(OUTFILE, "> $output_file") || die "Couldn't file ($output_file) to save text: $!"; print OUTFILE $tagged_text; close OUTFILE; print "Saved text to $output_file.\n"; } else { print "\n$tagged_text"; } sub usage { print <<'END_OF_USAGE'; This script randomly inserts a specified HTML tag into some text. Usage: $ taginserter [FLAGS | OPTIONS...] Options: -t The name of the tag you want inserted (e.g., "b" for <b> (bol +d), "i" for <i> (italic), etc.). -i (Optional.) The file which taginserter will read the text you + want tags inserted into from. If this isn't supplied, then you'll +be prompted to enter some text. -o (Optional.) The file where you want the text outputted to aft +er the tags have been inserted. If you don't supply this, then t +he filename you supplied to the -i option will be used instead. +If you didn't supply a filename to -i, then the text will be pri +nted out to the console. Flags: -h Displays this message. END_OF_USAGE exit; }

For example, running the PM copyright notice through the script with -t set to "b" converts this:

This page brought to you by the kind folks at The Everything Development Company and maintained by Tim Vroom. Perl Monks somehow became entangled with Yet Another Society. Beefy Boxes and Bandwidth Generously Provided by pair Networks

to this:

This page brought to you by the kind folks at The Everything Development Company and maintained by Tim Vroom. Perl Monks somehow became entangled with Yet Another Society. Beefy Boxes and Bandwidth Generously Provided by pair Networks

Replies are listed 'Best First'.
Re: CUFP: Random tag insertion
by Coruscate (Sexton) on Nov 21, 2003 at 01:03 UTC

    Interesting (as long as you're not planning on using this for your next website template). I enjoy rewriting other people's code, it passes some time and I usually end up learning something new. So here's my version (not that you'd want to use this on extremely large files. I imagine this would be very inefficient as I handle the whole thing in memory):

    #!c:/perl/bin/perl -w $|++; use strict; use Getopt::Std; sub show_usage; my $text; show_usage unless getopts('t:i:o:h', \my %opts); show_usage if $opts{'h'} or not $opts{'t'}; # read input file if ($opts{'i'}) { open my $fh, '<', $opts{'i'} or die "Couldn't open input file for reading: $!\n"; $text = do { local $/; <$fh> }; close $fh or die "Coudn't close input file: $!\n"; } # get text from console else { print "Ready for Input:\n\n"; $text = <STDIN>; } my $t_open = '<' . $opts{'t'} . '>'; my $t_close = '</' . $opts{'t'} . '>'; # insert random tags $text = join '', map { /\s/ || int rand 2 ? $_ : $t_open . $_ . $t_close } split //, $text; $text =~ s/\Q$t_close$t_open\E//g; # output to outfile if (defined $opts{'o'}) { open my $fh, '>', $opts{'o'} or die "Couldn't open output file for writing: $!\n"; print $fh $text; close $fh or die "Couldn't close output file: $!\n"; print "Text saved to output file $opts{'o'}.\n"; } # make backup/save to infile elsif (defined $opts{'i'}) { rename $opts{'i'}, $opts{'i'} . '.bak'; open my $fh, '>', $opts{'i'} or die "Couldn't open output file for writing: $!\n"; print $fh $text; close $fh or die "Couldn't close output file: $!\n"; print "Input file $opts{'i'} backed up to $opts{'i'}.bak.\n", "Text saved back to output file $opts{'i'}.\n"; } # print to console else { print $text, "\n"; } # display usage information sub show_usage { print <<"END_OF_USAGE"; This script randomly inserts a specified HTML tag into some text. Usage: \$ $0 -t htmltag [-i] [-o] [-h] Options: -t The html tag you wish to insert (ie: "em", "strong"). -i (Optional) The input file from which to read the text into which the random html tags will be inserted. -o (Optional) The file to which the generated output will be pri +nted. If you do not supply this option, the input file will be used + for output. A backup of the original input file will first be mad +e. If an input file was not used, output will be printed to the +console. Flags: -h Displays this help message. END_OF_USAGE exit; }

Re: CUFP: Random tag insertion
by Roy Johnson (Monsignor) on Nov 25, 2003 at 18:39 UTC
    Your main loop had a couple of features that struck me as odd: the anchoring of the pattern, and the destruction of one string to build another. The if structure also had redundant branching (you're either going to tag or you're not, so you only need one if).

    In pondering how I'd do it, I discovered why you destroy the string and create a new one: walking through the string and changing it becomes complicated. You end up getting lost. (The anchor and the if-structure points are still valid.)

    By working from the end of the string, and indexing from the front (or vice-versa), you can insert into the string and not lose your place. A c-style for loop is handy for this:

    for (my $pos = length($text); $pos ; --$pos) { my $char = substr($text, $pos-1, 1); if ($char =~ /\S/ and (int rand 2)) # 0 or 1 { substr($text, $pos-1, 1) = "$opening_tag$char$closing_tag"; } }
    Another technique that came to mind is the one-line main loop:
    $text =~ s/(\S)/(int rand 2) ? "$opening_tag$1$closing_tag" : $1/ge;
    Trying to use a while(/\S/g) gets very messy. I was not able to work it out.

    Then I started thinking about randomly opening and closing the tag, so you don't end up having to clean up closes followed immediately by opens.

    With a toggle, you're always going to get about 50% of the characters tagged; the random number just determines how often you go from tagged to untagged. We need to walk forward through the string, so we'll index from the end:

    my $tag_is_open = 0; for (my $pos = length($text); $pos > 0; ) { my $char = substr($text, -$pos, 1); if ($char =~ /\S/ and (int rand 2)) # 0 or 1 { if ($tag_is_open) { substr($text, -$pos, 1) .= $closing_tag; $pos-=2; # After closing a tag, skip at least one char } else { substr($text, -$pos, 0) = $opening_tag; } $tag_is_open ^= 1; # Toggle } else { --$pos } } $text .= $closing_tag if $tag_is_open;
    Have you ever appended to substr()? I haven't, before, but it works.


    The PerlMonk tr/// Advocate

      Yeah... The anchoring was needless and the block structure was indeed redundant. I liked your open/closed tag toggling example and added it to the script, and I also removed the input file slurping that Coruscate alluded to. No, I never assigned to substr() before either.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://308740]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2021-09-18 11:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?