katie
katie

Reputation: 37

How to put a file into an array and save it?

I want to put my strings starting from AA to \ in to an array and want to save it. There are about 2000-3000 strings in a text file starting from the same initials, i.e., AA to /. I'm doing it by this way. Please correct me if I'm wrong.

Input File

AA  c0001
BB  afsfjgfjgjgjflffbg
CC  table
DD  hhhfsegsksgk
EB  jksgksjs
\
AA  e0002
BB  rejwkghewhgsejkhrj
CC  chair
DD  egrhjrhojohkhkhrkfs
VB  rkgjehkrkhkh;r
\

Source code

$flag = 0
while ($line = <ifh>)
{

    if ( $line = m//\/g)
    {
        $flag = 1;
    }
    while ( $flag != 0)
    {
        for ($i = 0; $i <= 10000; $i++)
        { # Missing brace added by editor
            $array[$i] = $line;
        } # Missing brace added by editor
    }
}  # Missing close brace added by editor; position guessed!
print $ofh, $line;

close $ofh;

Upvotes: 1

Views: 1511

Answers (4)

Jonathan Leffler
Jonathan Leffler

Reputation: 753675

There are multiple issues with your code. First, please post compilable Perl; I had to add three braces to give it the remotest chance of compiling, and I had to guess where one of them went (and there's a moderate chance it should be on the other side of the print statement from where I put it).

Next, experts have:

use warnings;
use strict;

at the top of their scripts because they know they will miss things if they don't. As a learner, it is crucial for you to do the same; it will prevent you making errors.

With those in place, you have to declare your variables as you use them.

Next, remember to indent your code. Doing so makes it easier to comprehend. Perl can be incomprehensible enough at the best of times; don't make it any harder than it has to be. (You can decide where you like braces - that is open to discussion, though it is simpler to choose a style you like and stick with it, ignoring any discussion because the discussion will probably be fruitless.)

Is the EB vs VB in the data significant? It is hard to guess.

It is also not clear exactly what you are after. It might be that you're after an array of entries, one for each block in the file (where the blocks end at the line containing just a backslash), and where each entry in the array is a hash keyed by the first two letters (or first word) on the line, with the remainder of the line being the value. This is a modestly complex structure, and probably beyond what you're expected to use at this stage in your learning of Perl.

You have the line while ($line = <ifh>). This is not invalid in Perl if you opened the file the old fashioned way, but it is not the way you should be learning. You don't show how the output file handle is opened, but you do use the modern notation when trying to print to it. However, there's a bug there, too:

print $ofh, $line;  # Print two values to standard output
print $ofh  $line;  # Print one value  to $ofh

You need to look hard at your code, and think about the looping logic. I'm sure what you have is not what you need. However, I'm not sure what it is that you do need.

Simpler solution

From the comments:

I want to flag each record starting from AA to \ as record 0 till record n and want to save it in a new file with all the record numbers.

Then you probably just need:

#!/usr/bin/env perl
use strict;
use warnings;
my $recnum = 0;
while (<>)
{
    chomp;
    if (m/^\\$/)
    {
        print "$_\n";
        $recnum++;
    }
    else
    {
        print "$recnum $_\n";
    }
}

This reads from the files specified on the command line (or standard input if there are none), and writes the tagged output to standard output. It prefixes each line except the 'end of record' marker lines with the record number and a space. Choose your output format and file handling to suit your needs. You might argue that the chomp is counter-productive; you can certainly code the program without it.

Overly complex solution

Developed in the absence of clear direction from the questioner.

Here is one possible way to read the data, but it uses moderately advanced Perl (hash references, etc). The Data::Dumper module is also useful for printing out Perl data structures (see: perldoc Data::Dumper).

#!/usr/bin/env perl

use strict;
use warnings;
use Data::Dumper;

my @data;
my $hashref = { };
my $nrecs = 0;

while (<>)
{
    chomp;
    if (m/^\\$/)
    {
        # End of group - save to data array and start new hash
        $data[$nrecs++] = $hashref;
        $hashref = { };
    }
    else
    {
        m/^([A-Z]+)\s+(.*)$/;
        $hashref->{$1} = $2;
    }
}

foreach my $i (0..$nrecs-1)
{
    print "Record $i:\n";
    foreach my $key (sort keys $data[$i])
    {
        print "  $key = $data[$i]->{$key}\n";
    }
}
print Data::Dumper->Dump([ \@data ], [ '@data' ]);

Sample output for example input:

Record 0:
  AA = c0001
  BB = afsfjgfjgjgjflffbg
  CC = table
  DD = hhhfsegsksgk
  EB = jksgksjs
Record 1:
  AA = e0002
  BB = rejwkghewhgsejkhrj
  CC = chair
  DD = egrhjrhojohkhkhrkfs
  VB = rkgjehkrkhkh;r
$@data = [
           {
             'EB' => 'jksgksjs',
             'CC' => 'table',
             'AA' => 'c0001',
             'BB' => 'afsfjgfjgjgjflffbg',
             'DD' => 'hhhfsegsksgk'
           },
           {
             'CC' => 'chair',
             'AA' => 'e0002',
             'VB' => 'rkgjehkrkhkh;r',
             'BB' => 'rejwkghewhgsejkhrj',
             'DD' => 'egrhjrhojohkhkhrkfs'
           }
         ];

Note that this data structure is not optimized for searching except by record number. If you need to search the data in some other way, then you need to organize it differently. (And don't hand this code in as your answer without understanding it all - it is subtle. It also does no error checking; beware faulty data.)

Upvotes: 8

TLP
TLP

Reputation: 67900

Here's a way to read your data into an array. As I said in a comment, "saving" this data to a file is pointless, unless you change it. Because if I were to print the @data array below to a file, it would look exactly like the input file.

So, you need to tell us what it is you want to accomplish before we can give you an answer about how to do it.

This script follows these rules (exactly):

  • Find a line that begins with "AA", and save that into $line
  • Concatenate every new line from the file into $line
  • When you find a line that begins with a backslash \, stop concatenating lines and save $line into @data.
  • Then, find the next line that begins with "AA" and start the loop over.

These matching regexes are pretty loose, as they will match AAARGH and \bonkers as well. If you need them stricter, you can try /^\\$/ and /^AA$/, but then you need to watch out for whitespace at the beginning and end of line. So perhaps /^\s*\\\s*$/ and /^\s*AA\s*$/ instead.

The code:

use warnings;
use strict;

my $line="";
my @data;

while (<DATA>) {
    if (/^AA/) {
        $line = $_;
        while (<DATA>) {
            $line .= $_;
            last if /^\\/;
        }
    }
    push @data, $line;
}

use Data::Dumper;
print Dumper \@data;

__DATA__
AA  c0001
BB  afsfjgfjgjgjflffbg
CC  table
DD  hhhfsegsksgk
EB  jksgksjs
\
AA  e0002
BB  rejwkghewhgsejkhrj
CC  chair
DD  egrhjrhojohkhkhrkfs
VB  rkgjehkrkhkh;r
\

Upvotes: 2

matthias krull
matthias krull

Reputation: 4429

I believe what you want is to split the files content at \ though it's not too clear. To achieve this you can slurp the file into a variable by setting the input record separator, then split the content.

To find out about Perl's special variables related to filehandlers read perlvar

#!perl

use strict;
use warnings;

my $content;

{
    open my $fh, '<', 'test.txt';
    local $/; # slurp mode
    $content = <$fh>;
    close $fh;
}

my @blocks = split /\\/, $content;

Make sure to localize modifications of Perl's special variables to not interfere with different parts of your program.

If you want to keep the separator you could set $/ to \ directly and skip split.

#!perl

use strict;
use warnings;

my @blocks;

{
    open my $fh, '<', 'test.txt';
    local $/ = '\\'; # seperate at \
    @blocks = <$fh>;
    close $fh;
}

Upvotes: 2

Howard
Howard

Reputation: 39197

It can't be right. I can see two main issues with your while-loop.

Once you enter the following loop

while ( $flag != 0)
{
  ...
}

you'll never break out because you do not reset the flag whenever you find an break-line. You'll have to parse you input and exit the loop if necessary.

And second you never read any input within this loop and thus process the same $line over and over again.

You should not put the loop inside your code but instead you can use the following pattern (pseudo-code)

if flag != 0
    append item to array
else
    save array to file
    start with new array
end

Upvotes: 2

Related Questions