powershell: how to remove first # in each row of text? - string

E.g. I've got a text file like this:
abc,#32432,he#llo
xyz,#989,wor#ld
I wish to change it to be:
abc,32432,he#llo
xyz,989,wor#ld
Just remove first "#" from each row. How to do that?
Thanks.

To only change the first # in a string you could use a regular expression like this
"abc,#32432,he#llo" -replace '(.*?)#(.*$)', '${1}${2}'
Explanation as by Regexbuddy
# (.*?)#(.*$)
#
# Options: Case insensitive; Exact spacing; Dot doesn't match line breaks; ^$ match at line breaks; Parentheses capture
#
# Match the regex below and capture its match into backreference number 1 «(.*?)»
# Match any single character that is NOT a line break character (line feed) «.*?»
# Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?»
# Match the character “#” literally «#»
# Match the regex below and capture its match into backreference number 2 «(.*$)»
# Match any single character that is NOT a line break character (line feed) «.*»
# Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
# Assert position at the end of a line (at the end of the string or before a line break character) (line feed) «$»

You can use simple regex that replaces a hash that follows a section of non hashes with nothing, something like this:
Get-Content input_file.txt | % {$_ -replace '(?<=^[^#]+)#'} | Set-Content output_file.txt

Related

How to find lines that contain 3 specific characters?

Is there a way to remove lines that contain three specific characters?
For example the characters should be U S E
So for these lines, it should just remove USER AND UDES:
USER
USAD
UDES
Thanks
Ctrl+H
Find what: ^(?=.*U)(?=.*S)(?=.*E).+\R?
Replace with: LEAVE EMPTY
TICK Match case
TICK Wrap around
SELECT Regular expression
UNTICK . matches newline
Replace all
Explanation:
^ # beginning of line
(?= # positive lookahead, make sure we have after:
.* # 0 or more any character but newline
U # letter uppercase U
) # end lookahead
(?=.*S) # same as above for letter S
(?=.*E) # same as above for letter E
.+ # 1 or more any character but newline
\R? # any kind of linebreak, optional
Screenshot (before):
Screenshot (after):

Regex to detect pattern and remove spaces from that pattern in Python

I have a file that contains segments that form a word in the following format <+segment1 segment2 segment3 segment4+>, what I want to have is an output with all the segments beside each other to form one word (So basically I want to remove the space between the segments and the <+ +> sign surronding the segments). So for example:
Input:
<+play ing+> <+game s .+>
Output:
playing games.
I tried first detecting the pattern using \<\+(.*?)\+\> but I cannot seem to know how to remove the spaces
Use this Python code:
import re
line = '<+play ing+> <+game s .+>'
line = re.sub(r'<\+\s*(.*?)\s*\+>', lambda z: z.group(1).replace(" ", ""), line)
print(line)
Results: playing games.
The lambda removes spaces additionally.
REGEX EXPLANATION
--------------------------------------------------------------------------------
< '<'
--------------------------------------------------------------------------------
\+ '+'
--------------------------------------------------------------------------------
\s* whitespace (\n, \r, \t, \f, and " ") (0 or
more times (matching the most amount
possible))
--------------------------------------------------------------------------------
( group and capture to \1:
--------------------------------------------------------------------------------
.*? any character except \n (0 or more times
(matching the least amount possible))
--------------------------------------------------------------------------------
) end of \1
--------------------------------------------------------------------------------
\s* whitespace (\n, \r, \t, \f, and " ") (0 or
more times (matching the most amount
possible))
--------------------------------------------------------------------------------
\+ '+'
--------------------------------------------------------------------------------
> '>'
I assume that spaces can be converted to empty strings except when they are preceded by '>' and are followed by '<'. That is, the space in the string '> <' is not to be replaced by an empty string.
You can replace each match of the following regular expression with an empty string:
<\+|\+>|(?<!>) | (?!<)
Regex demo<¯\(ツ)/¯>Python code
This expression can be broken down as follows.
<\+ # Match '<+'
| # or
\+> # Match '<+'
| # or
(?<!>) # Negative lookbehind asserts current location is not preceded by '>'
[ ] # Match a space
| # or
[ ] # Match a space
(?!<) # Negative lookahead asserts current location is not followed by '<'
I've placed each space in a character class above so it is visible.

how to match only once occurrence of a double space of a line?

line A
foo bar bar foo bar foo
line B
foo bar bar foo
In line A, there are multiple occurrence of double space.
I only want to match lines like line B which has only once double space occurrence.
I tried
^.*\s{2}.*$
but it will match both.
How may I have the desired output? Thank you.
If you wish to match strings that contain no more than one string of two or more spaces between words you could use following regular expression.
r'^(?!(?:.*(?<! ) {2,}(?! )){2})'
Start your engine!
Note that this expression matches
abc de fgh
where there are four spaces between 'c' and 'd'.
Python's regex engine performs the following operations.
^
(?! : begin negative lookahead
(?: : begin non-capture group
.* : match 0+ characters other than line terminators
(?<! : begin negative lookbehind
[ ]{2,} : match 2+ spaces
(?! ) : negative lookahead asserts match is not followed by a space
) : end negative lookbehind
) : end non-capture group
{2} : execute non-capture group twice
) : end negative lookahead
You can do:
^(?!.*[ \t]{2,}.*[ \t]{2,})
# Negative look ahead assertion that states 'only start the match
# on this line IF there are NOT 2 (or potentially more) breaks with
# two (or potentially more) of tabs or spaces'.
Demo 1
If you want to require ONE double space in the line but not more:
^(?=.*[ \t]{2,})(?!.*[ \t]{2,}.*[ \t]{2,})
# Positive look ahead that states 'only start this match if there is
# at least one break with two tabs or spaces'
# BUT
# Negative look ahead assertion that states 'only start the match
# on this line IF there are NOT 2 (or potentially more) breaks with
# two (or potentially more) of tabs or spaces'.
Demo 2
If you want to limit to only two spaces (not tabs and not more than 2 spaces):
^(?=.*[ ]{2})(?!.*[ ]{2}.*[ ]{2})
# Same as above but remove the tabs as part of the assertion
Demo 3
Note: In your regex you have \s as the class for a space. That also matches [\r\n\t\f\v ] so both horizontal and vertical space characters.
Note 2:
You can do this without a regex as well (assuming you only want lines that have 1 and only 1 double space in them):
txt='''\
line A
foo bar bar foo bar foo
line B
foo bar bar foo'''
>>> [line for line in txt.splitlines() if len(line.split(' '))==2]
['foo bar bar foo']
You can get the match without lookarounds by starting the match with 1+ non whitespace chars.
Then optionally repeat a single whitespace char followed by non whitespace chars before and after matching a double whitespace char.
The negated character class [^\S\r\n] will match any whitespace chars except a newline or carriage return. If you want to allow matching newlines as well, you could use \s
^\S+(?:[^\S\r\n]\S+)*[^\S\r\n]{2}(?:\S+[^\S\r\n])*\S+$
Explanation
^ Start of string
\S+ Match 1+ non whitespace chars
(?: Non capture group
[^\S\r\n]\S+ Match a whitespace char without a newline
)* Close group and repeat 0+ times
[^\S\r\n]{2} Match the 2 whitespace chars without a newline
(?: Non capture group
\S+[^\S\r\n] Match 1+ non whitespace chars followed by a whitespace char without a newline
)* Close group a and repeat 1+ times
\S+ Match 1+ non whitespace chars
$ End of string
Regex demo

perl: print remaining string only if there is no character before the matched value.

The following prints the entire content of the line after "B. "
perl -ne'print if /B[.] (.*)/s' $string > file
How can I match/print the line only if there is no other character before the "B. "? In other words, if there is a character before the "B. " ie. "TAB." skip the line / do not print.
The correct "B." is always on a new line, the only correct line to match appears as follows:
B. some text here
A regex with a leading carat indicates that the expression should match only if it is the first item on the line. The pattern /^B[.] (.*)/s should get you the result you're looking for.
Put ^ in front of the B. It means match the word starts with B. So your regex should be /^B\. (.*)/. Then no need you s flag in your pattern match.

Perl: Count number of times a word appears in text and print out surrounding words

I want to do two things:
1) count the number of times a given word appears in a text file
2) print out the context of that word
This is the code I am currently using:
my $word_delimiter = qr{
[^[:alnum:][:space:]]*
(?: [[:space:]]+ | -- | , | \. | \t | ^ )
[^[:alnum:]]*
}x;
my $word = "hello";
my $count = 0;
#
# here, a file's contents are loaded into $lines, code not shown
#
$lines =~ s/\R/ /g; # replace all line breaks with blanks (cannot just erase them, because this might connect words that should not be connected)
$lines =~ s/\s+/ /g; # replace all multiple whitespaces (incl. blanks, tabs, newlines) with single blanks
$lines = " ".$lines." "; # add a blank at beginning and end to ensure that first and last word can be found by regex pattern below
while ($lines =~ m/$word_delimiter$word$word_delimiter/g ) {
++$count;
# here, I would like to print the word with some context around it (i.e. a few words before and after it)
}
Three problems:
1) Is my $word_delimiter pattern catching all reasonable characters I can expect to separate words? Of course, I would not want to separate hyphenated words, etc. [Note: I am using UTF-8 throughout but only English and German text; and I understand what reasonably separates a word might be a matter of judgment]
2) When the file to be analzed contains text like "goodbye hello hello goodbye", the counter is incremented only once, because the regex only matches the first occurence of " hello ". After all, the second time it could find "hello", it is not preceeded by another whitespace. Any ideas on how to catch the second occurence, too? Should I maybe somehow reset pos()?
3) How to (reasonably efficiently) print out a few words before and after any matched word?
Thanks!
1. Is my $word_delimiter pattern catching all reasonable characters I can expect to separate words?
Word characters are denoted by the character class \w. It also matches digits and characters from non-roman scripts.
\W represents the negated sense (non-word characters).
\b represents a word boundary and has zero-length.
Using these already available character classes should suffice.
2. Any ideas on how to catch the second occurence, too?
Use zero-length word boundaries.
while ( $lines =~ /\b$word\b/g ) {
++$count;
}

Resources