Reading a File into array by blocks using $/

  • (2 Pages)
  • +
  • 1
  • 2

21 Replies - 3526 Views - Last Post: 11 February 2011 - 02:40 AM

#1 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Reading a File into array by blocks using $/

Posted 28 January 2011 - 04:47 AM

Ok guys quick question just started programming perl today for work (little side job to keep me interested). What i want to do is to read two files into arrays in blocks. So i've done a little reading and have found that i can use this character
$/
. So what i was wondering is how i would link that to the character i want to use as the end of the line ( the actual character i want to link it to is
/>
). If anyone has any suggestions or decent links that could point me in the right direction that would be great.

Just so you guys know what im trying to achieve is that i have two test files with multiple information of tests run inside which are enclosed in brackets ( partically < ..... />) So if i can find a way to move each test into an element of an array i could be making my life alot easier (also i sadly can't just put each line into the array cause some of the test informations rolls onto a second line). So any help would be appreciated

cheers ahead of time

This post has been edited by isuckatC++: 28 January 2011 - 04:53 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Reading a File into array by blocks using $/

#2 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 29 January 2011 - 03:00 AM

View PostisuckatC++, on 28 January 2011 - 12:47 PM, said:

Ok guys quick question just started programming perl today for work (little side job to keep me interested). What i want to do is to read two files into arrays in blocks. So i've done a little reading and have found that i can use this character
$/
. So what i was wondering is how i would link that to the character i want to use as the end of the line ( the actual character i want to link it to is
/>
).


The straightforward way to do this would simply be:
local $/ = '/>';


The "local" is so that $/ will be automatically restored to its original value when the current code block completes. It's not strictly necessary, but it's a good habit to get into for keeping things clean and being friendly to other code.

View PostisuckatC++, on 28 January 2011 - 12:47 PM, said:

Just so you guys know what im trying to achieve is that i have two test files with multiple information of tests run inside which are enclosed in brackets ( partically < ..... />) So if i can find a way to move each test into an element of an array i could be making my life alot easier (also i sadly can't just put each line into the array cause some of the test informations rolls onto a second line). So any help would be appreciated


Be aware that setting $/ to '/>' will not grab chunks of text from within '<'...'/>' pairs, but rather will read the data "line"-by-"line" with '/>' treated as the end of a "line" rather than using the normal CRLF marker to separate lines. Any data outside of a '<'...'/>' pair will be included at the start of the next "line".

Given your delimiters, I suspect you're probably trying to parse some form of XML document, aren't you? If so, you would be wise to take a look at http://search.cpan.org/ for existing XML parsers and see whether you can use one of them instead of rolling your own. Unfortunately, I never deal with XML myself, so I can't offer any recommendations of specific modules to try.
Was This Post Helpful? 0
  • +
  • -

#3 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 29 January 2011 - 03:02 AM

(Double post due to DIC server throwing a 500... Mods may delete.)

This post has been edited by dsherohman: 29 January 2011 - 03:03 AM

Was This Post Helpful? 0
  • +
  • -

#4 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 31 January 2011 - 04:12 AM

lol that easy huh lol :P. So used to C was expecting it to be much harder or something :D. Thanks for the help should make it alot easier to find what i'm looking for. Yea this is being used with XML files so yea having to use /> as the line by line character cause annoyling the block's ( <......./>) aren't just on one line but can be spread over two normally.

Also wondered if this was the correct way to compare arrays (@array1 eq @array2?). Or would u have to loop it or ?
Was This Post Helpful? 0
  • +
  • -

#5 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 01 February 2011 - 02:18 AM

View PostisuckatC++, on 31 January 2011 - 12:12 PM, said:

Also wondered if this was the correct way to compare arrays (@array1 eq @array2?). Or would u have to loop it or ?


I don't think eq will work, but I'm not certain, so let's check and see:
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo bar); print @a1 eq @a2 ? "OK\n" : "No way\n"'
OK
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo baz); print @a1 eq @a2 ? "OK\n" : "No way\n"'
OK
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo bar baz); print @a1 eq @a2 ? "OK\n" : "No way\n"'
No way


Yeah, that's just testing that they each have the same number of elements - eq forces its operands into scalar context and, in scalar context, the value of an array is its size.

If you're using perl 5.10.0 or later, the smart match operator will handle this:
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo bar); print @a1 ~~ @a2 ? "OK\n" : "No way\n"'
OK
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo baz); print @a1 ~~ @a2 ? "OK\n" : "No way\n"'
No way



If you're stuck with an older perl, take a look at Array::Compare. (Link to a StackOverflow post demonstrating A::C because I've never used it myself.)

Finally, if you're stuck with an older perl and you're not allowed to use CPAN modules, you can always turn the arrays into strings and compare those:
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo bar); print join("!@#", @a1) eq join("!@#", @a2) ? "OK\n" : "No way\n"'
OK
$ perl -e '@a1 = qw(foo bar); @a2 = qw(foo baz); print join("!@#", @a1) eq join("!@#", @a2) ? "OK\n" : "No way\n"'
No way


...but that will have performance issues with large arrays and you'd probably be better off just writing your own loop to compare them at this point. (Don't forget to bail out with "last" once you find a non-matching element!)
Was This Post Helpful? 0
  • +
  • -

#6 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 01 February 2011 - 05:15 AM

Thanks for the help dsherohman been a real help just one last question is there any way of excluding parts of a string from being compared. As you will notice below this is the standard line that is produced inside the XML files (as said before due to size of the line this can move onto more than one line but u've showed me how to avoid that.

What i was wondering though is there any if statments or commands i can use to remove LastTimeRun and TakeTime from being compared (well if there is an easy way otherwise ill just use substr). But if you have a better idea how i could solve this that would be great. Currently learning about the smart match operator that you gave me which i have found very useful. So any ideas would be nice but greatful for the help you have given so far gonna make my life alot easier at work this week :P (got enough on my plate without doing this first :D but hey always fun to do something different than u usually have to).

<Test Name="Set set" Result="Failed" LastTimeRun="31012011_13h27m45s" Filename="Tests-31012011_13h27m45s.xml" TakenTime_ms="1098" />

Was This Post Helpful? 0
  • +
  • -

#7 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 02 February 2011 - 05:16 AM

Hey Ds thought i would show u how far I am and how's it going.

#!/usr/bin/perl
use Cwd;
use Time::Local qw( timegm );
use Switch;
use Term::ReadLine;
use File::Basename;
$/ = "/>";
$filename ="TestSummary.xml";
open(FILENAME, $filename) || die("Could not open file!"); # open for input
@file=<FILENAME>;
close(FILENAME); 
#print $file[0];
foreach $line (@file)
{
#print "$line";	
$line =~ tr/TestSet//d;
print $line;

}



Just wondering have a slight problem with this line "$line =~ tr/TestSet//d;" what i want it to be is "$line =~ tr/</TestSet>//d;". However just get a compile error after trying this.

$test = \<\/TestSet\>;
$line =~ tr/$test//d;


ha now i know what i was missing :P

$test = "\<\/TestSet\>";
$line =~ s/$test//gi;

This post has been edited by isuckatC++: 02 February 2011 - 05:40 AM

Was This Post Helpful? 0
  • +
  • -

#8 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 02 February 2011 - 06:25 AM

View PostisuckatC++, on 01 February 2011 - 01:15 PM, said:

Thanks for the help dsherohman been a real help just one last question is there any way of excluding parts of a string from being compared.
...
What i was wondering though is there any if statments or commands i can use to remove LastTimeRun and TakeTime from being compared (well if there is an easy way otherwise ill just use substr).

<Test Name="Set set" Result="Failed" LastTimeRun="31012011_13h27m45s" Filename="Tests-31012011_13h27m45s.xml" TakenTime_ms="1098" />

No, there's not a straightforward way (other than substr) to directly compare on only part of the strings. But you can make a temporary copy and chop out the parts you want to ignore:
my $for_compare = $original_string;
$for_compare =~ s/(LastRunTime|TakeTime)="[^"]" //g;
if ($for_compare eq $target) { ... }


Again, though, you'd probably be better off checking search.cpan.org for a good XML parsing module and using that if possible instead of doing this kind of stuff by hand.

View PostisuckatC++, on 02 February 2011 - 01:16 PM, said:

Just wondering have a slight problem with this line "$line =~ tr/TestSet//d;" what i want it to be is "$line =~ tr/</TestSet>//d;". However just get a compile error after trying this.

As I'm sure you've already guessed, the problem is caused by the / in your set of characters to delete.[1] While you could escape it with a backslash, Perl allows you to use alternate delimiters to avoid having to do that. Instead of tr///, you can write tr|||, tr!!!, tr()(), [email protected]@@, or pretty much any other non-alphanumeric delimiter. (Note that pairable delimiters such as (), [], or <> work in pairs, not by repeating the same symbol; this also respects nesting, so tr<</TestSet>><>d will Just Work, although I'd advise against it due to poor human-readability.) This also applies to the m// and s/// operators.

And, yay! I have code to dissect!

#!/usr/bin/env perl
# /usr/bin/env perl instead of /usr/bin/perl will use the first perl it finds
# in $PATH; either way usually works, but this way allows you to use a perl
# other than your OS vendor's system perl, which can be useful if, say, your
# vendor is still running perl 5.8.8 and you want to use features from 5.12
# without taking the risk of hosing the system by upgrading /usr/bin/perl

use strict;
use warnings;
# Unless you know Perl well enough to be able to articulate specific reasons
# for not using strict/warnings in a particular case, you should *always* use
# them.  If you do know Perl well enough to articulate such reasons, you
# should *always* use them (and just turn them off locally where needed).

use Cwd;
use Time::Local qw( timegm );

#use Switch;
# DON'T use Switch.  It causes very-hard-to-debug errors in various corner
# cases and is generally considered deprecated (and dangerous).  Use the new
# given...when syntax (if your perl is 5.10 or newer) or a hash-based dispatch
# table instead.

use Term::ReadLine;
use File::Basename;

local $/ = "/>"; # like I said before, localizing this is a good habit to develop
my $filename ="TestSummary.xml";
# strict requires variables to be declared (usually, though not always, with
# "my") when first mentioned

#open(FILENAME, $filename) || die("Could not open file!"); # open for input
open my $file, '<', $filename or die "Could not open file: $!";
# Four things here, aside from superfluous parentheses:
# 1) It's better to use lexical filehandles ("my $file") than typeglob filehandles
#    ("FILENAME"), as this avoids polluting global namespaces, avoids conflicts
#    with other code using the same global name, and allows perl to automatically
#    close the file when it goes out of scope
# 2) The three-argument form of "open" is safer (in the general case) than the
#    older two-argument form, since it prevents problems if you try to open a
#    file named, say ">foo.txt" - your "open" would overwrite "foo.txt", while
#    mine would, correctly, read from ">foo.txt"
# 3) "or" is preferred over "||" here, both because it's a little more English-
#    like and because it's a very-low-precedence operator, while || is very high
# 4) Don't forget to include $! in your die statement, since that will display the
#    system error which caused the problem

#@file=<FILENAME>;
#close(FILENAME); 
##print $file[0];
#foreach $line (@file)
#{

while ($file)
{
  my $line = $_;
# If you're going to process the file one line at a time, don't slurp the whole
# thing into memory at once without a very good reason for tying up the extra
# memory

#print "$line";	
#$line =~ tr/TestSet//d;
$line =~ tr[</TestSet>][]d;
print $line;

}



(Revisions are untested and may not compile, but I'm pretty sure I spelled them all correctly.)


[1] If you want to remove the string "</TestSet>", you need to use s///, not tr/// - tr|</TestSet>||d will delete all occurrences of the characters <, /, T, e, s, t, S, or >, even when not appearing together.
Was This Post Helpful? 0
  • +
  • -

#9 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 02 February 2011 - 09:34 AM

Yea sadly did a big look for what i was looking for however they all seem to not cover what i'm trying to do. But then u have to remember that these XML files are custom made so aren't ur standard file output. Ha as you saww got it in the end decided to keep with the slashes (just cause it's better if i learn to use it that way cause otherwise ill end up doing it those other ways in C too :D).

When it comes to the delimiter sadly can't just set it to local as it's used a hell of alot of times so thought it would be better to set it once at the top and just undef it once i have finished it. Altogether i'm gonna have to use it about 10 times or maybe more depending on what kinda outputs i need. Mind u suppose i can just inclose all my code in a while 1 loop and then just break out when user select "no".

Ah keep forgetting to use "my" at the beginning c habit i suppose find replace ftw :D.



Dont suppose you know of a good link to anywhere which just has the newer functions available that have superceeded others ? As it seems even perldoc seems to be out of date "http://www.modernperlbooks.com/mt/2010/02/a-decade-of-lexical-filehandles.html" (thought u'd find that amusing :P).

Btw what is this saying
="[^"]"
?

Ha btw ha one hell of a day so managed to actually produce quite alot.

#!/usr/bin/perl
use Cwd;
use Time::Local qw( timegm );
use Term::ReadLine;
use File::Basename;
$/ = ">";
my $filename ="TestSummary.xml";
open(my $file,'<', $filename) || die("Could not open $filename: $!"); # open for input
@file=<$file>;
close($file); 

foreach $line (@file)
{
	
$test = "\<\/TestSet\>";
$line =~ s/$test//gi;

$test = "\<\/TestSummary\>";
$line =~ s/$test//gi;

$line  =~ s/^\s+//;
$line  =~ s/\s+$//;
	
$test = ">";
$line =~ s/$test/>\n/gi;

}

@greptests = grep(/Passed/, @file);
#@greptestsfilename = grep(/Filename*/,@greptests);
print @greptests;


Also think i do actually have to take in the whole array cause were not just comparing line by line by also will be file by file as need to do a comparison over both files more than once. But then i suppose i could just compare each element of each file against the other file at creation time. btw thanks for the help again have turn this job from a pain to a little bit of fun :P.


Also bit confused as to how this

while ($file)
	{
	  my $line = $_;

is the same as

foreach $line (@file)
{


is it because $line is the whole string and $_ is one line of the string ?

Ok last thing :D just wondering is there any way to use awk with perl as i would like to use this command

awk '{ print $2}';


lol wonders of working at a semiconductor company theres always someone who know what they are doing. However if you could explain this line u be helping me cause the guy i got the code off doesn't really speak english :D.
$line =~ m,.*Filename=\"([^\"]+)\".*,;
print $1 . "\n";


This post has been edited by isuckatC++: 02 February 2011 - 11:27 AM

Was This Post Helpful? 0
  • +
  • -

#10 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 03 February 2011 - 02:49 AM

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

Yea sadly did a big look for what i was looking for however they all seem to not cover what i'm trying to do. But then u have to remember that these XML files are custom made so aren't ur standard file output.


Ah, in that case... yeah. Like I said before, I don't tend to muck about with XML myself, but one of the most common complaints I hear about XML modules in general (for any language, not just Perl) is "the module requires well-formed XML, which my data isn't".

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

When it comes to the delimiter sadly can't just set it to local as it's used a hell of alot of times so thought it would be better to set it once at the top and just undef it once i have finished it.


If that's what you need to do, then store the original value and restore it when you're finished instead of undeffing it - the normal value is not undef.

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

Ah keep forgetting to use "my" at the beginning c habit i suppose find replace ftw :D.


"use strict". It won't let you forget. :D

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

Dont suppose you know of a good link to anywhere which just has the newer functions available that have superceeded others ?


Not offhand, no. All the big stuff came in with 5.10, though, so googling "perl 5.10 new functions" or "perl 5.10 new operators" should cover it pretty well. The ones that people tend to talk about (and use) are given...when, ~~ (smart match operator), // (defined-or operator), and say.

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

As it seems even perldoc seems to be out of date


That's just your perldoc, I think... perldoc.org is nice and current. http://perldoc.perl....tions/open.html presents three-arg open and lexical filehandles as the norm, getting about 1/3 of the way through before using a typeglob handle (other than STD[IN|OUT|ERR]) and nearly to the end before introducing two-arg open.

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

Btw what is this saying
="[^"]"
?


It was part of a regex, in which context [square brackets] define a character class (a set of characters to match) and a character class starting with ^ contains all characters except those listed (excluding the leading ^).

It was also wrong. It should have been
s/(LastRunTime|TakeTime)="[^"]*" //g;[/quote]

The fragment /="[^"]*"/ will match a literal =", followed by zero or more non-" characters, followed by another ".  The use of [^"]* instead of .* will force it to stop at the first " instead of continuing on to the last " in the string (because regexes try to find the longest possible match).

As I originally wrote it, without the *, it would only match if there was exactly one non-" character within the quotes.

[quote name='isuckatC++' date='02 February 2011 - 05:34 PM' timestamp='1296664461' post='1238509']
btw thanks for the help again have turn this job from a pain to a little bit of fun :P/>.
[/quote]

That's why I love Perl...  It's the most fun language I've ever worked with, aside from the one Scheme course I took in college, but I never learned enough Scheme/Lisp to do anything useful with it, so I've basically forgotten it, but am still using Perl *mumble* years later.

[quote name='isuckatC++' date='02 February 2011 - 05:34 PM' timestamp='1296664461' post='1238509']
Also bit confused as to how this

[code]while ($file)
	{
	  my $line = $_;

is the same as

foreach $line (@file)
{


is it because $line is the whole string and $_ is one line of the string ?[/code]

Nope, it's not the same; I messed that one up, too. (That'll teach me to post untested code...)

It should have been "while (<$file>)", which will read one line at a time from $file (as opposed to slurping the whole thing into memory at once with "@file = <$file>"), putting the line into $_.

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

Ok last thing :D just wondering is there any way to use awk with perl as i would like to use this command

awk '{ print $2}';


You can do shell escapes in Perl with backticks:
my $awk_result = `awk '{ print $2}'`;
but you could probably do the same thing more efficiently with a regex within the perl code instead of calling out to awk.

View PostisuckatC++, on 02 February 2011 - 05:34 PM, said:

However if you could explain this line u be helping me cause the guy i got the code off doesn't really speak english :D.
$line =~ m,.*Filename=\"([^\"]+)\".*,;
print $1 . "\n";



First off, remember what I said yesterday about alternate regex delimiters... This guy is using , instead of /, although I don't really have any idea why. Changing those back, we get
$line =~ m/.*Filename=\"([^\"]+)\".*/;


The leading and trailing .* are superfluous (and may even hurt performance on some regex implementations, but Perl's is smart enough to just ignore them) because a regex can match anywhere in the string by default. So get rid of them. The "m" is also unnecessary if you're using / as your delimiter and double quotes have no special meaning in a regex, so they don't need to be escaped.
$line =~ /Filename="([^"]+)"/;


I imagine this looks a little familiar now... This regex will match the literal text Filename=", followed by one or more non-" characters, followed by another ". The parentheses around ([^"]+) will capture that portion of the matched text and store it in the variable $1 (because it's the first set of parens; the second set would go into $2, etc.) and the following line prints out that value.

So, given the sample line of XML in your Feb. 1 post, these two lines would print the output
Tests-31012011_13h27m45s.xml

Was This Post Helpful? 0
  • +
  • -

#11 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 03 February 2011 - 04:48 AM

Ha yea really do wish it was standard file format otherwise i would have been laughing :D. Yea lol the idea of well formed XML and this test summary file don't really come in the same sentence.

Yea will do ha just added strict was like 15 my's missing :D.

Ha lol yea btw kinda annoying is that they still have the old doc pages so most peoples links still point to that page instead (ha someone really needs to tidy perl up a bit maybe that could be in 6 :P).

Thanks man for the info never like not knowing all of my code. Ha but man regexp are hard to get ur head around mind u suppose when u do it becomes useful in loads of areas :P. So with those backticks btw does that mean u can use any unix command aslong as u use backticks ?

Ha yea u could say that lol gone from making the line unreadable to actually understanding what it is doing :D. Yea getting a filename output which is great know all i have to do is do it for another test summary compare them for failed passed tests. Then need to add a section to repeat the process on 12 other directories :P so not much more to go :D. But thanks again been a real help especially considering how little i knew about perl at the beginning of this week (have to admit know 12 other languages did help :D).
Was This Post Helpful? 0
  • +
  • -

#12 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 04 February 2011 - 02:58 AM

View PostisuckatC++, on 03 February 2011 - 12:48 PM, said:

So with those backticks btw does that mean u can use any unix command aslong as u use backticks ?


Yep! Just like in bash, anything you put in backticks gets passed to a shell and the output is returned.

View PostisuckatC++, on 03 February 2011 - 12:48 PM, said:

(have to admit know 12 other languages did help :D).


I'm sure, especially since Perl has probably "borrowed" features and/or syntax from most (if not all) of those other languages. :D
Was This Post Helpful? 0
  • +
  • -

#13 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 04 February 2011 - 05:25 AM

Ok ds have a little problem atm with smart match operator tried out ur code for a test and couldn't get it to work. Also i've tried using grep to give me the 3rd array. Don't suppose you have any idea what i've done wrong.

These are my two goes.

%passedtestsfilename = map {$_, 1} @passedtestsfilename;
@difference = grep {!$passedtestsfilename {$_}} @failedtestsfilename1;

print "@difference\n";



#say "[@passedtestsfilename] and [@failedtestsfilename1] match" if @passedtestsfilename ~~ @failedtestsfilename1;
#{
#print "passed";	
#}


So if u have any idea whats going wrong please let me know. Oh and heres the whole code. Btw tested my compare code in a seperte file and it worked fine moved it into my main code and getting a spam of errors's

Use of uninitialized value in substitution (s///) at Regression1.pl line 46.
Use of uninitialized value in substitution (s///) at Regression1.pl line 48.
Use of uninitialized value in substitution (s///) at Regression1.pl line 49.
Use of uninitialized value in substitution (s///) at Regression1.pl line 50.
Use of uninitialized value in substitution (s///) at Regression1.pl line 52.

#!/usr/bin/perl
use Cwd;
use Time::Local qw( timegm );
use Term::ReadLine;
use File::Basename;
#use strict;
use warnings;
use CGI qw/:standard/;

$/ = ">";
#####################################################################################
# 			Grab the Test Summary files and place them into an array				#
#####################################################################################
my $filename ="TestSummary.xml";
my $filename1 ="TestSummary1.xml";
open(my $file,'<', $filename) || die("Could not open $filename: $!"); # open for input
open(my $file1, '<', $filename1) || die("Could not open $filename: $!"); # open for input
my @file=<$file>;	#Push file data onto an array
my @file1=<$file1>; #Push file data onto an array
close($file); #Close File
close($file1);#Close File

#####################################################################################
# 				Move through array and remove any unwanted strings 					#
#####################################################################################

############					Test Summary 1					 		 ############
foreach my $line (@file)
{
	
	my $test = "\<\/TestSet\>"; # Remove TestSet line from Test Summary 
	my $line =~ s/$test//gi;
	$test = "\<\/TestSummary\>"; # Remove Test Summary from file
	$line =~ s/$test//gi;
	$line  =~ s/^\s+//; #Remove spaces from beginning
	$line  =~ s/\s+$//; #Remove spaces from end of line
	$test = ">";		
	$line =~ s/$test/>\n/gi; #Place newline after >

}
############					Test Summary 2					 		 ############
foreach my $line1 (@file1)
{
	
	my $test = "\<\/TestSet\>"; # Remove TestSet line from Test Summary 
	my $line =~ s/$test//gi;
	$test = "\<\/TestSummary\>"; # Remove Test Summary from file
	$line =~ s/$test//gi;
	$line  =~ s/^\s+//; #Remove spaces from beginning
	$line  =~ s/\s+$//; #Remove spaces from end of line
	$test = ">";		
	$line =~ s/$test/>\n/gi; #Place newline after >

}
#####################################################################################
# 		Check Test Summary to see which tests have failed and which have passed		#
#####################################################################################

############					Test Summary 1					 		 ############
my @passedtests = grep(/Passed/, @file); #Check for all Passed tests inside Test Summary
my @failedtests = grep(/Failed/, @file); #Check for all Failed tests inside Test Summary

############					Test Summary 2					 		 ############
my @passedtests1 = grep(/Passed/, @file1); #Check for all Passed tests inside Test Summary
my @failedtests1 = grep(/Failed/, @file1); #Check for all Failed tests inside Test Summary


#####################################################################################
# Move through loop of passed tests and remove all informations apart from filename	#
#####################################################################################

#####################################################################################
############					Test Summary 1					 		 ############
############					Passed Tests							 ############
#####################################################################################
print "\n			TEST SUMMARY 1";
print "\n			PASSED TESTS:\n";

foreach my $line (@passedtests) #Loop through passed tests and display filename for each passed test
{
	$line =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(my @passedtestsfilename, $1); #Push passed filenames onto an array
}
#####################################################################################
############					Failed Tests							 ############
#####################################################################################
print "\n			FAILED TESTS:\n";

foreach my $line (@failedtests) #Loop through failed tests and display filename for each failed test
{
	$line =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(my @failedtestsfilename, $1); #Push failed filenames onto an array
}
#####################################################################################
############					Test Summary 2					 		 ############
############					Passed Tests							 ############
#####################################################################################
print "\n			TEST SUMMARY 2";
print "\n			PASSED TESTS:\n";

foreach my $line1 (@passedtests1) #Loop through passed tests and display filename for each passed test
{
	$line1 =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(my @passedtestsfilename1, $1); #Push passed filenames onto an array
}
#####################################################################################
############					Failed Tests							 ############
#####################################################################################
print "\n			FAILED TESTS:\n";

foreach my $line1 (@failedtests1) #Loop through failed tests and display filename for each failed test
{
	$line1 =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(my @failedtestsfilename1, $1); #Push failed filenames onto an array
}
#####################################################################################
#									Reset Delimiter									#
#####################################################################################

$/ = "\n"; # Reset delimiter back to newline

%passedtestsfilename = map {$_, 1} @passedtestsfilename;
@difference = grep {!$passedtestsfilename {$_}} @failedtestsfilename1;

print "@difference\n";



#say "[@passedtestsfilename] and [@failedtestsfilename1] match" if @passedtestsfilename ~~ @failedtestsfilename1;
#{
#print "passed";	
#}

#foreach my $item (@failedtestsfilename1)
#		{
#		push(my @array3, $item) unless grep (/$item/, @passedtestsfilename);
#		}
#		
#		print @array3;
#foreach my $item (@array3)
#		{
#		print "Regresssions";
#		print "$item";	
#		}
#		my $item;
#				print "Regresssions\n";
#		print "$item";	


See what im doing wrong by chance ?

And heres a working test version (that for some reason wont work when i move it to my script

#!/usr/bin/perl
use Cwd;
use Time::Local qw( timegm );
use Term::ReadLine;
use File::Basename;
#use strict;
use warnings;
use CGI qw/:standard/;

@array_one = qw(b c d e);
@array_two = qw(a b c d e f);

%array_one = map {$_, 1} @array_one;
@difference = grep {!$array_one {$_}} @array_two;

print "@difference\n";


Got it working now btw.

This post has been edited by isuckatC++: 04 February 2011 - 10:26 AM

Was This Post Helpful? 0
  • +
  • -

#14 isuckatC++   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 78
  • Joined: 04-May 09

Re: Reading a File into array by blocks using $/

Posted 04 February 2011 - 08:09 AM

Hey DS just to let u know think i have solved it however would you mind looking over the code to see if it's alright ?

#!/usr/bin/perl
use Cwd;
use Time::Local qw( timegm );
use Term::ReadLine;
use File::Basename;
use strict;
use warnings;
use CGI qw/:standard/;
use Data::Compare;


$/ = ">";
#####################################################################################
# 			Grab the Test Summary files and place them into an array				#
#####################################################################################
my $filename ="TestSummary.xml";
my $filename1 ="TestSummary1.xml";
open(my $file,'<', $filename) || die("Could not open $filename: $!"); # open for input
open(my $file1, '<', $filename1) || die("Could not open $filename: $!"); # open for input
my @file=<$file>;	#Push file data onto an array
my @file1=<$file1>; #Push file data onto an array
close($file); #Close File
close($file1);#Close File

#####################################################################################
# 				Move through array and remove any unwanted strings 					#
#####################################################################################

############					Test Summary 1					 		 ############
#foreach my $line (@file)
#{
	
	#my $test = "\<\/TestSet\>"; # Remove TestSet line from Test Summary 
	#my $line =~ s/$test//gi;
	#$test = "\<\/TestSummary\>"; # Remove Test Summary from file
	#$line =~ s/$test//gi;
	#$line  =~ s/^\s+//; #Remove spaces from beginning
	#$line  =~ s/\s+$//; #Remove spaces from end of line
	#$test = ">";		
	#$line =~ s/$test/>\n/gi; #Place newline after >

#}
############					Test Summary 2					 		 ############
#foreach my $line1 (@file1)
#{
	
	#my $test = "\<\/TestSet\>"; # Remove TestSet line from Test Summary 
	#my $line =~ s/$test//gi;
	#$test = "\<\/TestSummary\>"; # Remove Test Summary from file
	#$line =~ s/$test//gi;
	#$line  =~ s/^\s+//; #Remove spaces from beginning
	#$line  =~ s/\s+$//; #Remove spaces from end of line
	#$test = ">";		
	#$line =~ s/$test/>\n/gi; #Place newline after >

#}
#####################################################################################
# 		Check Test Summary to see which tests have failed and which have passed		#
#####################################################################################

############					Test Summary 1					 		 ############
my @passedtests = grep(/Passed/, @file); #Check for all Passed tests inside Test Summary
my @failedtests = grep(/Failed/, @file); #Check for all Failed tests inside Test Summary

############					Test Summary 2					 		 ############
my @passedtests1 = grep(/Passed/, @file1); #Check for all Passed tests inside Test Summary
my @failedtests1 = grep(/Failed/, @file1); #Check for all Failed tests inside Test Summary


#####################################################################################
# Move through loop of passed tests and remove all informations apart from filename	#
#####################################################################################

#####################################################################################
############					Test Summary 1					 		 ############
############					Passed Tests							 ############
#####################################################################################
print "\n			TEST SUMMARY 1";
print "\n			PASSED TESTS:\n";

foreach my $line (@passedtests) #Loop through passed tests and display filename for each passed test
{
	$line =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(our @passedtestsfilename, $1); #Push passed filenames onto an array
}
#####################################################################################
############					Failed Tests							 ############
#####################################################################################
print "\n			FAILED TESTS:\n";

foreach my $line (@failedtests) #Loop through failed tests and display filename for each failed test
{
	$line =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	#push(@failedtestsfilename, $1); #Push failed filenames onto an array
}
#####################################################################################
############					Test Summary 2					 		 ############
############					Passed Tests							 ############
#####################################################################################
print "\n			TEST SUMMARY 2";
print "\n			PASSED TESTS:\n";

foreach my $line1 (@passedtests1) #Loop through passed tests and display filename for each passed test
{
	$line1 =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	#push(@passedtestsfilename1, $1); #Push passed filenames onto an array
}
#####################################################################################
############					Failed Tests							 ############
#####################################################################################
print "\n			FAILED TESTS:\n";

foreach my $line1 (@failedtests1) #Loop through failed tests and display filename for each failed test
{
	$line1 =~ /Filename="([^"]+)"/;
	print $1 . "\n";
	push(our @failedtestsfilename1, $1); #Push failed filenames onto an array
}
#####################################################################################
#									Reset Delimiter									#
#####################################################################################

$/ = " "; # Reset delimiter back to newline
print "\n\n			Differences:\n\n";
my %passedtestsfilename = map {$_, $_} our @passedtestsfilename;
my @difference = grep {$passedtestsfilename {$_}} our @failedtestsfilename1;
foreach my $line (@difference)
{
print "$line\n\n";	
}


lol was surprised when i found that i didn't need to do that formatting to @file and @file1 but then i suppose grep ftw. Didn't realise how powerful it could be :D.

This post has been edited by isuckatC++: 04 February 2011 - 08:10 AM

Was This Post Helpful? 0
  • +
  • -

#15 dsherohman   User is offline

  • Perl Parson
  • member icon

Reputation: 227
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Reading a File into array by blocks using $/

Posted 07 February 2011 - 05:13 AM

View PostisuckatC++, on 04 February 2011 - 04:09 PM, said:

Hey DS just to let u know think i have solved it however would you mind looking over the code to see if it's alright ?


The most important thing, of course, is that is works right, so good that you've achieved that! There are a couple things that look a bit fishy to me, though, so here are some suggestions to clean it up:

1)
$/ = ">";
# Read in files
# Process stuff
$/ = " "; # Reset delimiter back to newline



I'm still skeptical of your claim that local isn't suitable for what you're doing. In your latest posted code, you can do this with local like so:
{
  local $/ = ">";
  # Read in files
}
# Process stuff


This way, there's no need to restore the original value of $/ because it happens automatically when execution exits the anonymous block I added (the { ... } ) around the file reading.

If you really can't use local, it would still be better not to hardcode the "restore" value:
my $orig_delimiter = $/;
$/ = ">";
# ...
$/ = $orig_delimiter; # Reset delimiter back to newline




2)
my %passedtestsfilename = map {$_, $_} our @passedtestsfilename;
my @difference = grep {$passedtestsfilename {$_}} our @failedtestsfilename1;



Those "our" specifiers shouldn't be there - you should never have to declare the same variable (with my/our) more than once in the same package. To avoid having to do that, add the line
my (@passedtestsfilename, @failedtestsfilename1);

before the "Test Summary 1" loop.


My new version of the whole thing, as you've posted it so far, showing a few more tricks, some more-idiomatic Perl, and removing repeated code:
#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;

my @first = read_file('TestSummary.xml');
my @second = read_file('TestSummary1.xml');

# $first_passed, etc. instead of @first_passed because they're references to
# arrays (kind of like pointers in C, except you can't do pointer arithmetic
# on them); see comments in get_test_results for why they have to be refs

my ($first_passed_files, $first_failed_files) = get_test_results(@first);
my ($second_passed_files, $second_failed_files) = get_test_results(@second);

print "\n                       TEST SUMMARY 1\n";
show_test_results($first_passed_files, $first_failed_files);
print "\n                       TEST SUMMARY 2\n";
show_test_results($second_passed_files, $second_failed_files);

print "\n\n                     Regressions:\n\n";
my %first_passed_lookup = map { $_ => 1 } @$first_passed_files;
my @regression = grep { exists $first_passed_lookup{$_} } @$second_failed_files;
say join "\n\n", @regression;

exit;

sub read_file {
    my $filename = shift;

    open my $fh, '<', $filename or die "Could not open $filename: $!";
    local $/ = '>';
    my @lines = <$fh>;
    return \@lines;

    # $fh is closed and $/ is restored automatically when the sub exits, so
    # no need to deal with those manually
}

sub get_test_results {
    my @lines = shift;

    # map aliases $_ to each element of the list, so modifying $_ will also
    # modify the original list

    my @passed = grep /Passed/, @lines;
    map { /Filename="([^"]+)"/; $_ = $1; } @passed;
    my @failed = grep /Failed/, @lines;
    map { /Filename="([^"]+)"/; $_ = $1; } @failed;

    # We have to return references to the arrays here because
    # return(@passed, @failed) would flatten them into a single list with no
    # way to tell when @passed ended and @failed began

    return (\@passed, \@failed);
}

sub show_test_results {
    my ($passed, $failed) = @_;

    # Use @$passed to defererence $passed into an array

    print   "                       PASSED TESTS:\n";
    say join "\n", @$passed;
    print "\n                       FAILED TESTS:\n";
    say join "\n", @$failed;
}


It's syntactically valid, but not tested beyond that. Since I don't know what else you'll need to do in your final program, parts may not apply for you, but it should at least do the same thing as the code you posted. (I actually debated combining read_file and test_results into a single sub that takes a filename and returns passed/failed lists, but you said earlier that you need to access the input file contents more than once, so I figured it would be good to hold on to that data in @first and @second instead of only the _passed_files and _failed_files lists.)
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2