OS: Windows server 2012, so I don't have access to Unix utils
Activestate Perl 5.16. Sorry I cannot upgrade the OS or Perl, I'm stuck with it.
I did a google search and read about 10 pages from that, I find similar problems but not what I'm looking for.
I then did 3 searches here and found similar issues with SQL, R, XSLT, but not what I'm looking for.
I actually am not sure where to start so I don't even have code yet.
I'd like to combine consecutive page numbers into a page range. Input will be a series of numbers in an array.
Input as an array of numbers: my #a=(1,2,5)
Output as a string: 1-2, 5
Input ex: (1,2,3,5,7)
Output ex: 1-3, 5, 7
Input ex: (100,101,102,103,115,120,121)
Output ex: 100-103,115,120-121
Thank you for your help!
This is the only code I have so far.
sub procpages_old
# $aref = array ref to list of page numbers.
# $model = used for debugging.
# $zpos = used for debugging only.
{my($aref,$model,$zpos)=#_;
my $procname=(caller(0))[3];
my #arr=#$aref; # Array of page numbers.
my #newarr=();
my $i=0;
my $np1=0; # Page 1 of possible range.
my $np2=0; # Page 2 of possible range.
my $p1=0; # Page number to test.
my $p2=0;
my $newpos=0;
while ($i<$#arr)
{
$np1=$arr[$i];
$np2=getdata($arr[$i+1],'');
$p1=$np1;
$p2=$np2;
while ($p2==($p1+1)) # Consecutive page numbers?
{
$i++;
$p1=$a[$i];
$p2=getdata($a[$i+1],'');
}
$newarr[$newpos]=$np1.'-'.$p2;
$newpos++;
# End of loop
$i++;
}
my $pages=join(', ',#arr);
return $pages;
}
That's called an intspan. Use Set::IntSpan::Fast::XS.
use Set::IntSpan::Fast::XS qw();
my $s = Set::IntSpan::Fast::XS->new;
$s->add(100,101,102,103,115,120,121);
$s->as_string; # 100-103,115,120-121
This seems to do what you want.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
while (<DATA>) {
chomp;
say rangify(split /,/);
}
sub rangify {
my #nums = #_;
my #range;
for (0 .. $#nums) {
if ($_ == 0 or $nums[$_] != $nums[$_ - 1] + 1) {
push #range, [ $nums[$_] ];
} else {
push #{$range[-1]}, $nums[$_];
}
}
for (#range) {
if (#$_ == 1) {
$_ = $_->[0];
} else {
$_ = "$_->[0]-$_->[-1]";
}
}
return join ',', #range;
}
__DATA__
1,2,5
1,2,3,5,7
100,101,102,103,115,120,121
The rangify() function builds an array of arrays. It traverses your input list and if a number is just one more than the previous number then it adds the new number to the second-level array that's currently at the end of the first-level array. If the new number is not sequential, it adds a new second-level array at the end of the first level array.
Having built this data structure, we walk the first-level array, looking at each of the second-level arrays. If the second level array contains only one element then we know it's not a range, so we overwrite the value with the single number from the array. If it contains more than one element, then it's a range and we overwrite the value with the first and last elements separated with a hyphen.
So I managed to adjust this code to work for me. Pass your array of numbers into procpages() which will then call num2range().
######################################################################
# In:
# Out:
sub num2range
{
local $_ = join ',' => #_;
s/(?<!\d)(\d+)(?:,((??{$++1}))(?!\d))+/$1-$+/g;
tr/-,/, /;
return $_;
}
######################################################################
# Concatenate consecutive page numbers in array.
# In: array like (1,2,5,7,99,100,101)
# Out: string like "1-2, 6, 7, 99-101"
sub procpages
{my($aref,$model,$zpos)=#_;
my $procname=(caller(0))[3];
my #arr=#$aref;
my $pages=num2range(#arr);
$pages=~s/\,/\-/g; # Change comma to dash.
$pages=~s/ /\, /g; # Change space to comma and space.
#$pages=~s/\,/\, /g;
return $pages;
}
You probably have the best solution already with the Set::IntSpan::Fast::XS module, but assuming you want to take the opportunity to learn perl here's another perl-ish way to do it.
use strict;
use warnings;
my #nums = (1,2,5);
my $prev = -999; # assuming you only use positive values, this will work
my #out = ();
for my $num (#nums) {
# if we are continuing a sequence, add a hyphen unless we did last time
if ($num == $prev + 1) {
push (#out, '-') unless (#out and $out[-1] eq '-');
}
else {
# if we are breaking a sequence (#out ends in '-'), add the previous number first
if (#out and $out[-1] eq '-') {
push(#out, $prev);
}
# then add the current number
push (#out, $num);
}
# track the previous number
$prev = $num;
}
# add the final number if necessary to close the sequence
push(#out, $prev) if (#out and $out[-1] eq '-');
# join all values with comma
my $pages = join(',', #out);
# flatten the ',-,' sequence to a single '-'
$pages =~ s/,-,/-/g;
print "$pages\n";
This is not super elegant or short, but is very simple to understand and debug.
I have a wordlist of dictionary words in .txt format. How can I use this with the captcha_helper instead of random characters? I've already extended the captcha_helper file but am having issues integrating my wordlist.txt file for use.
After doing some poking, I found a solution:
// This is the modified version in captcha_helper.php
if($word == ''){
$wordsfile = '../words.php';
$fp = fopen($wordsfile, 'r');
$length = strlen(fgets($fp));
$line = rand(1, (filesize($wordsfile)/$length)-2);
if(fseek($fp, $length*$line) == -1) return FALSE;
$word = trim(fgets($fp));
fclose($fp);
}
But I noticed that sometimes the last letter would get cut off. Is there a way to make sure that the first and last letter never get placed outside of the bounding box?
in this case you can use one function which pass your words randomly to the script to display..
i think this is better option.
instead to work wit
In PHP you need to use preg_quote() to escape all the characters in a string that have a particular meaning in a regular expression, to allow (for example) preg_match() to search for those special characters.
What is the equivalent in Ruby of the following code?
// The content of this variable is obtained from user input, in example.
$search = "$var = 100";
if (preg_match('/' . preg_quote($search, '/') . ";/i")) {
// …
}
You want Regexp.escape.
str = "[...]"
re = /#{Regexp.escape(str)}/
"la[...]la[...]la".gsub(re,"") #=> "lalala"
I'm collecting text through a web form and noticing that when it is collected by my Perl CGI all instances of "+" are transformed into " ". I run the text through a JavaScript escape before submission, but escape seems to leave + unaltered.
There must be something really obvious that I'm missing... how do I send the string "2 + 2 = 4" through and not have it arrive as "2 2 = 4"?
The escape and unescape functions do not work properly for non-ASCII characters and have been deprecated. In JavaScript 1.5 and later, use encodeURI, decodeURI, encodeURIComponent, and decodeURIComponent.
— https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Predefined_Functions/escape_and_unescape_Functions
Replace escape with encodeURIComponent
You '+', in the URL, should be encoded as %2B :
http://www.example.com/myscript.ext?a=2%20%2B%202%20=%204
Will give a = 2 + 2 = 4
In Javascript, this means using the encodeURIComponent function : this portion of code :
encodeURIComponent('2 + 2 = 4')
will give :
"2%20%2B%202%20%3D%204"
Note the + is encoded.
While this one :
escape('2 + 2 = 4')
would only give :
"2%20+%202%20%3D%204"
Note the + is not encoded.
You can encode + as %2B, as seen in: http://www.google.com/search?q=2+%2B+2
I do not know what you mean by using JavaScript escape. Browsers will properly encode form field values and CGI.pm will properly decode them.
For example,
#!/usr/bin/perl
use strict; use warnings;
use CGI;
my $cgi = CGI->new;
if ( $cgi->param ) {
process_form($cgi);
}
else {
show_form($cgi);
}
sub process_form {
my ($cgi) = #_;
print $cgi->header('text/plain'),
$cgi->param('entry'), "\n";
}
sub show_form {
my ($cgi) = #_;
print $cgi->header, <<EO_HTML;
<!DOCTYPE HTML>
<html><head><title>Test +</title></head>
<body><form>
<input name="entry"><input type="submit">
</form></body></html>
EO_HTML
}
The output I get from submitting this form with 2+2=4 in the entry field is:
2+2=4
I'm using Graphviz to draw some graphs. I'm using labels on nodes and I can put in "\n" to force it to split the label across 2 lines. Is there some way to get Graphviz (or dot which I'm using) to automatically see that it should split some nodes labels, and for it itself to make the best choice automagically?
Yes, HTML-like labels (<...>) support tag, using which you can break the lines. E.g.
"A" -> "B"
[label = <1. <br/>
2. <br/>
3. <br/>
4. <br/>
.... <br/>
> color="blue" style="dashed"];
These also work when embedding Graphviz in LaTeX, where \n would not.
I've also searched for this, but I don't think it's possible in the current version. The current "solution" is to write code that automatically adds the "\n" every few characters, based on the minimum distance between nodes (nodesep attribute, if I'm not mistaken).
One person wrote a Perl script to achieve this. I found it in his blog: Text wrapping with dot (graphviz).
⚠ Note
This only works if the labels are in the format node [ label=”node label” ]. If the nodes are declared directly (e.g. ”node label”) then it doesn’t work
Perl script:
#!/usr/bin/perl
use strict;
my $usage = "setdotlabelwidth [char-width] < [dotfile]";
my $width = shift() or die("Usage: $usage $!");
while(<STDIN>)
{
if(m/label="(.*?)"/)
{
my $labeltext = $1;
my #words = split(/ /, $labeltext);
my #newtext = ();
my $newline = "";
foreach my $word(#words)
{
if( length($newline) > 0 and
length($newline) + length($word) > $width )
{
push(#newtext, $newline);
$newline = "";
}
$newline .= " " if( length($newline) > 0 );
$newline .= $word;
}
push(#newtext, $newline) if( length($newline) > 0 );
my $newlabel = join("\\n", #newtext);
s/label=".*?"/label="$newlabel"/;
}
print;
}
Save this program as setdotlabelwidth, then simply pipe the output into GraphViz. If for example you want to set the width to 35 characters, then the command is:
./setdotlabelwidth 35 < tile-error-correction.dot | dot -Tpng -o tile-error-correction.png
Before:
After:
(Not sure how we're supposed to deal with duplicate questions?)
dot2tex (latex + graphviz) handles text wrapping,
along with other workarounds to the typesetting limitations of graphviz.
You'll find a short example at this duplicate question,
with prescribed fixed line width.