I am trying to understand the context (array/list/scalar) in Perl.
I tried the following:
#array = qw (john bill george);
print #array;
print "\n";
#sorted = sort (array);
print #sorted;
Why does the print #array concats the quoted words? I need to print
"#array"; to print the list? I mean # signifies an array right?
So why are the quoted needed for print?
Why does the print #sorted; prints array? If it is treated as a
scalar shouldn't it print 3 which is the size of the array?
print #sorted prints "array" because you forgot the # in the previous line :P
Change sort(array) to sort(#array) and it will print "billgeorgejohn".
As for why does print #array concatenate the quoted words, first let's make sure we're on the same page regarding qw:
#array = qw(john bill george);
is equivalent to
#array = ("john", "bill", "george");
so you're getting an array of three elements. Next, see the documentation for print. Passing a list of stuff to print will print them all, in order, separated by whatever value $, (the output field separator) has at the time. The default is empty string.
So you could do:
$, = " ";
print #array;
to get "john bill george".
The function print takes a list of arguments and prints those arguments.
If you explicitly pass a list to print then I hope you're not surprised when it prints out the elements of that list without spaces between them.
print 'one', 'two', 'three'; # prints "onetwothree
Passing an array to print is exactly the same. The contents of the array are converted to a list and then passed to print;
my #array = qw(one two three);
print #array; # prints "onetwothree"
In both of those cases, print receives three arguments and prints those arguments with nothing separating them.
Actually, Perl uses the special variable $, to control what print outputs between its arguments. By default that's an empty string, but you can change it.
Now let's consider your other case.
my #array = qw(one two three);
print "#array"; # prints "one two three"
How many arguments does print get in this example? Well, it's just one, isn't it? It's a single double quoted string. And when Perl sees a double quoted string it expands any variables in the string. The result of that expansion is then passed to print which then prints it. So we need to find out how Perl expands arrays in double-quoted. That's defined in the perldata manual page.
Array Interpolation
Arrays and slices are interpolated into double-quoted strings by
joining the elements with the delimiter specified in the $" variable
($LIST_SEPARATOR if "use English;" is specified), space by default.
So, by default, Perl interpolates arrays into double-quoted strings by inserting spaces between the elements. you can change that behaviour by changing the value of $".
These two examples might look the same, but they are actually very different.
Related
This question already has answers here:
Bash: Split string into character array
(20 answers)
Closed 6 months ago.
I am reading a file that basically contains a list of binary data (fixed at 8 bits wide per line):
01011100
11110001
...
For each line that is read, I need to "remap" the bits in chunks of 4 bits to specific positions. So for example in the 1st line above, bits 1100 and 0101 will each be remapped that will follow this formula: bit 0 goes to bit 3 position, bit 1 to 2, bit 3 to 1, and lastly bit 2 to 0.
To do this, I coded as follows:
function remap {
echo "Remapper";
IFS=
read -ra din <<< $1;
echo ${#din};
echo ${din[1]};
## above line is just displaying blank as seen in below result
echo ${din[*]};
## do actual remapping here
};
for line in `cat $infile`;
do
data0=${line:0:4};
data1=${line:4:4};
echo "Read line";
echo $data0;
echo $data1;
remap $data0;
remap $data1;
done
I don't know why I'm not seeing the echoed array element. This is the output from the 1st read line:
Read line
0101
1100
Remapper
4
0101
Remapper
4
1100
I haven't gotten to coding the actual remapping itself because I couldn't even verify that I'm able to properly split the $1 variable of remap() function into the din array.
Thank you in advance for the help!
Unlike other languages, setting IFS to empty string does not split a string
into each character array. Instead you can use fold -w1 to
add a newline after each character:
remap() {
echo "Remapper"
mapfile -t din < <(fold -w1 <<< "$1")
echo "${din[3]}${din[2]}${din[0]}${din[1]}"
}
As it will be inefficient to invoke fold command every time, it
may be better to say:
remap() {
echo "Remapper"
echo "${1:3:1}${1:2:1}${1:0:1}${1:1:1}"
}
As a side note, you don't need to append semicolon after each command.
There are a number of confusions here. The biggest is that read -ra din is not splitting the string into characters. read will split its input into words delimited by the characters in IFS; normally that's whitespace, but since you set IFS to the empty string, there are no delimiters and the string won't be split at all. Anyway, you don't want to split it based on delimiters, you want to split it into characters, so read is the wrong tool.
Another source of confusion is that ${#din} isn't the length of the array, it's the length (in characters) of the first element of the array. ${#din[#]} would get the number of elements in the array, and in this case it'd be 1. More generally, declare -p din would be a better way to see what din is and what it contains; here, it'd print something like declare -a din='([0]="0101")', showing that it's an array with a single four-character element, numbered 0.
What I'd do here is skip trying to split the characters into array elements entirely, and just index them as characters in $1 -- that is, ${1:0:1} will get the first character (character #0) from $1, ${1:1:1} will get the second (#1), etc. So to print the bits in the order third, first, second, fourth, you'd use:
echo "${1:2:1}${1:0:1}${1:1:1}${1:3:1}"
Other recommendations: It's best to double-quote variable expansions (like I did above) to prevent weird parsing problems. for var in $(cat somefile) is a fragile way to read lines from a file; while read var; do ... done <somefile is generally better. I'd recommend remap() { instead of the nonstandard function remap { syntax, and semicolons are redundant at the end of lines (well... with a few weird exceptions). shellcheck.net will point most of these out, and is a good tool to sanity-check your scripts for common mistakes.
I'm working on a project where I need to take an input from the user and then cut it up into its individual characters for later use (to shift them up one character) but I'm having trouble getting the input into an array and printing it out to check that its in there. Currently my code is
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $count=0; # this block just creates variables
my $userinput;
print "Input?";
$userinput=<STDIN>; # this block just gets input and creates the array
my #userarray=();
while(<#userarray>) {
#userarray = split('', $userinput); #this block should loop as many times as there are characters in the input while separating the characters
}
print Dumper(#userarray); #this should print the array
My output should look some like this if their input is "house"
#userarray[0]= "h"
#userarray[1]= "o"
#userarray[2]= "u"
#userarray[3]= "s"
#userarray[4]= "e"
But when I do enter something in it just print back a blank screen despite strict and warnings not coming back with anything. Where did I go wrong?
<D> reads and returns one (the next) record (that's a line if the record separator $/ hasn't been changed) from the file handle D in scalar context. In list context all remaining records are returned (as an array).
Said that, this section is the problem:
$userinput=<STDIN>; # this block just gets input and creates the array
my #userarray=();
while(<#userarray>) {
#userarray = split('', $userinput); #this block should loop as many times as there are characters in the input while separating the characters
}
<#userarray> returns nothing as #userarray sure isn't a valid file handle. So that loop is never entered.
If you want the user to input only one line, don't use the loop at all. Read one line and split it.
$userinput=<STDIN>; # this block just gets input and creates the array
chomp($userinput);
my #userarray=();
#userarray = split('', $userinput);
But that loop may indicate, that you want the user to be able to enter more than one line. If so, loop until there's no input left (EOF), reading the input line by line. Split the line and push the result into your array.
while(my $line = <STDIN>) {
chomp($line);
push(#userarray, split('', $line));
print(join(',', #userarray) . "\n");
}
For both ways: chomp() removes the trailing record separator (new line) at the end of the record (line). Don't use it, if you want to keep these. I assumed you don't.
This is a common Perl pattern. You want to loop so the user can enter more data. Try something like this:
print "Input?";
while (my $userinput = <STDIN>) {
chomp $userinput; # remove trailing newline
my #userarray = split //, $userinput;
print Dumper(\#userarray);
}
Just learning some basic Ruby concepts as a beginner. Not really looking for code as such, rather some fundamental principles behind the following question (obviously feel free to express yourself with code if you need to :0)
In a simple redact text exercise, a user enters some text, then enters the word to be redacted, I'm fine with this and can make it work a number of ways.
However...
to deal with the possibility the user could enter upper and/or lower case letters for either the text or redacted word, I would need to create variables .downcase! again no problem there. But what if once the program runs, you want to return the words to their original state?
I thought perhaps you would need to create an array for the original text, where each word has an index within the array, create a corresponding array with the lowercase letters and if a word is NOT redacted, then you would compare the index from the lowercase array and write the corresponding index from the original array... does this sound correct or am I over thinking it, is there an easier way?
Thanks for your help
puts " What is your message"
text1 = gets.chomp
text2 = text1.downcase
puts "What is your secret word"
redact = gets.chomp.downcase!
words = text2.split (" ")
words.each do |x|
if
x == redact
print "REDACTED" + " "
else
print x + " "
end
end
I've added my working code, you can see that I've separated text1 the original from text2 which isn't strictly necessary as it stands, but to maintain the original formatting
Your solution sounds like it could work and as a beginner it may be useful to write a complete solution like that. But don't forget that ruby can do a lot of fun stuff for you.
Lets say we take input into sentence and the string to redact is stored in redact.
We can do something as simple as this:
sentence.gsub(/#{redact}/i, "*" * redact.length)
gsub finds all occurrences of the first argument and replaces it with the second, returning a new string.
First notice that we are using the redacted string as a regular expression for the first arg and the i indicates that it should match case insensitive, as you wanted.
Now the second arg is simply a string of asterisks of equivalent length to the redacted string.
For example if we have the following:
sentence = 'this is My Sentence'
redact = 'my'
puts sentence.gsub(/#{redact}/i, "*" * redact.length)
The above method will print this is ** Sentence.
Just one extra thing to note: this regex will match all occurrences of the string. For example, if redact = 'is', the resulting sentence will be th** ** My Sentence. You can re-write the regex to avoid this if that's not the expected use case.
I am starting to learn C and have a question about arrays.
Here I have a 2-dimensional simple array of characters:
char memory[100][6];
...
// populate indexes
and here I try to print out the first row:
puts(memory[0]);
Here is the output. Why does every row print out?
00P00300P10404P1ZZZZ0000ZZ0010
I come from Java where if you run the same code you will only get the first row. Am I missing something or is this just how C handles arrays? If so, how can I just get the first row?
The puts function is strictly for C-style strings. If you pass it a pointer to something other than a C-style string, garbage is likely to result. How are you expecting it to know how many characters to output?
I have an array with one element, words in array are separated by tab (tab-key):
cli::array<String^> ^ PnkFld = {"Good bye cruel world"};
and program output should look like this with words being split in separate elements:
cli::array<String^> ^ PnkFld = {"Good","bye","cruel","world"};
You want the String::Split method. If you don't specify any parameters, you'll get this overload, which splits on whitespace when the parameter contains no characters.