perl - replace every nth (and multiples) occurrences of a character with another character
Small perl hack to solve the problem. Using the index
function to find the commas, modulus to replace the right one, and substr
to perform the replacement.
use strict;
use warnings;
while (<>) {
my $x=index($_,",");
my $i = 0;
while ($x != -1) {
$i++;
unless ($i % 3) {
$_ = substr($_,0,$x) ."|". substr($_,$x+1);
}
$x = index($_,",",$x + 1)
}
print;
}
Run with perl script.pl file.csv
.
Note: You can place the declaration my $i
before the while(<>)
loop in order to do a global count, instead of a separate count for each line. Not quite sure I understood your question in that regard.
Replace every nth Occurrence in a Perl Script
Here's another option:
use strict;
use warnings;
my $i = 0;
my $n = 3;
while (<>) {
s/no_access/read/ if !( ++$i % $n );
print;
}
Usage: perl script.pl inFile [>outFile]
The last, optional parameter directs output to a file.
Hope this helps!
Replacing all occurrence after nth occurrence in a line in perl
You can use the perl
solution like
perl -pe 's~^(?:[^:]*:){2}(*SKIP)(?!)|:~~g if /^:account_id:/' test.txt
See the online demo and the regex demo.
The ^(?:[^:]*:){2}(*SKIP)(?!)|:
regex means:
^(?:[^:]*:){2}(*SKIP)(?!)
- match^
- start of string (here, a line)(?:[^:]*:){2}
- two occurrences of any zero or more chars other than a:
and then a:
char(*SKIP)(?!)
- skip the match and go on to search for the next match from the failure position
|
- or:
- match a:
char.
And only run the replacement if the current line starts with :account_id:
(see if /^:account_id:/'
).
Or an awk
solution like
awk 'BEGIN{OFS=FS=":"} /^:account_id:/ {result="";for (i=1; i<=NF; ++i) { result = result (i > 2 ? $i : $i OFS)}; print result}' test.txt
See this online demo. Details:
BEGIN{OFS=FS=":"}
- sets the input/output field separator to:
/^:account_id:/
- line must start with:account_id:
result=""
- setsresult
variable to an empty stringfor (i=1; i<=NF; ++i) { result = result (i > 2 ? $i : $i OFS)}; print result}
- iterates over the fields and if the field number is greater than2
, just append the current field value toresult
, else, append the value + output field separator; then print theresult
.
search and replace nth occurence in a file
$.
holds line number for current file handle and can be used for given input file like,
perl -i -pe 's/number\s+555/number 666/ if $. == 2' hello.txt
or if number
part can be dropped out,
perl -i -pe 's/555/666/ if $. == 2' hello.txt
Perl replace nth substring in a string
This question might be interesting: Perl regex replace count
You might do something like this:
use strict;
use warnings;
my $count = 3;
my $str = "blublublublublu";
$str =~ s/(lu)/--$count == 0 ? "LA":$1/ge;
print $str;
How can I substitute the nth occurrence of a match in a Perl regex?
Or you can do something as this
use strict;
use warnings;
my $string = "'How can I','use' .... 'perl','to process this' 'line'";
my $cont =0;
sub replacen { # auxiliar function: replaces string if incremented counter equals $index
my ($index,$original,$replacement) = @_;
$cont++;
return $cont == $index ? $replacement: $original;
}
#replace the $index n'th match (1-based counting) from $string by $rep
sub replace_quoted {
my ($string, $index,$replacement) = @_;
$cont = 0; # initialize match counter
$string =~ s/'(.*?)'/replacen($index,$1,$replacement)/eg;
return $string;
}
my $result = replace_quoted ( $string, 3 ,"PERL");
print "RESULT: $result\n";
A little ugly the "global" $cont variable, that could be polished, but you get the idea.
Update: a more compact version:
use strict;
my $string = "'How can I','use' .... 'perl','to process this' 'line'";
#replace the $index n'th match (1-based counting) from $string by $replacement
sub replace_quoted {
my ($string, $index,$replacement) = @_;
my $cont = 0; # initialize match counter
$string =~ s/'(.*?)'/$cont++ == $index ? $replacement : $1/eg;
return $string;
}
my $result = replace_quoted ( $string, 3 ,"PERL");
print "RESULT: $result\n";
Regex to find(/replace) multiple instances of character in string
The reason your code doesn't work is that /g
doesn't rescan the string after a substitution. It finds all non-overlapping matches of the given regex and then substitutes the replacement part in.
In [abc@def"ghi"jkl'123]
, there is only a single match (which is the [abc@def"
part of the string, with $1 = '[abc@def'
and $2 = ''
), so only the first "
is removed.
After the first match, Perl scans the remaining string (ghi"jkl'123]
) for another match, but it doesn't find another [
(or @
).
I think the most straightforward solution is to use a nested search/replace operation. The outer match identifies the string within which to substitute, and the inner match does the actual replacement.
In code:
s{ \[ [^\[\]\@]* \@ \K ([^\[\]]*) (?= \] ) }{ $1 =~ tr/a-zA-Z0-9//cdr }xe;
Or to replace each match by X
:
s{ \[ [^\[\]\@]* \@ \K ([^\[\]]*) (?= \] ) }{ $1 =~ tr/a-zA-Z0-9/X/cr }xe;
We match a prefix of [
, followed by 0 or more characters that are not [
or ]
or @
, followed by @
.
\K
is used to mark the virtual beginning of the match (i.e. everything matched so far is not included in the matched string, which simplifies the substitution).
We match and capture 0 or more characters that are not [
or ]
.
Finally we match a suffix of ]
in a look-ahead (so it's not part of the matched string either).
The replacement part is executed as a piece of code, not a string (as indicated by the /e
flag). Here we could have used $1 =~ s/[^a-zA-Z0-9]//gr
or $1 =~ s/[^a-zA-Z0-9]/X/gr
, respectively, but since each inner match is just a single character, it's also possible to use a transliteration.
We return the modified string (as indicated by the /r
flag) and use it as the replacement in the outer s
operation.
Replace multiple occurrences between two strings
Yes, it's best to use Perl
perl -pe's/xx(.+?)zz/"xx".$1=~s|a|hello|gr."zz"/ge' file.txt
Perl replace every occurrence differently
I was actually trying to do something like this the other day, here's what I came up with
$fasta =~ s/\>[^_]+_([^\/]+)[^\n]+/
sub {
# return random string
}->();
/eg;
the \e
modifier interprets the substitution as code, not text. I use an anonymous code ref so that I can return at any point.
Related Topics
Sed,Awk.Grep - Add a Line to the End of a Configuration Section If the Line Doesn't Already Exist
Linux: Differencebetween These Two Symbolic Link Commands
How to Print Formatted HTML in Linux Server
Split File into Multiple File Using Awk, But in Date Format
Print Differences of File1 to File2 Without Deleting Anything from File2
Using Ssh to Run a Cleartool Command with Agruments on Remote a Linux MAChine
Bash Script to Remove Directories Based on Modified File Date
Phusion Passenger Nginx Module Installer V3.0.17 Issue on Debian 6.0.5 Amd64 Due to Broken Package
How to Delete Files Over (N) Days Old But Leave (N) Files Regardless of Age
Python3 Unicodeencodeerror When Run via Synology Task Scheduler
Wget: Unsupported Scheme on Non-Http Url
Extracting Variable in Yaml from a Shell Script
Escaping Single Quotes in Shell for Postgresql
Replace Parentheses and Spaces in Filenames with Underscore
"Bad Interpreter" Error Message When Trying to Run Awk Executable
Stop Being Root in the Middle of a Script That Was Run with Sudo