How to use SAS to find the last sentence in a document? - sas

I am trying to create a variable that contains the last sentence of a document.
The last sentence of the text can be separated by periods, question marks or exclamation points. The ending punctuation may be omitted.
if find(text, '.') >0 then last = strip(scan(text,-1,'.'));
else if find(text, '?') >0 then last = strip(scan(text,-1,'?'));
else if find(text, '!') >0 then last = strip(scan(text,-1,'!'));

SCAN(string, count <, character-list <, modifier>>)
Try
last = scan ( text, -1, '.?!', 't' );
The scan function will go from right to left when the count is negative. Use your sentence delimiters as the character-list to interpret a sentence as a 'word'. Use t modifier to trim the string before scanning.

Related

CSV Regex skipping first comma

I am using regex for CSV processing where data can be in Quotes, or no quotes. But if there is just a comma at the starting column, it skips it.
Here is the regex I am using:
(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?|)(?=$|,)
Now the example data I am using is:
,"data",moredata,"Data"
Which should have 4 matches ["","data","moredata","Data"], but it always skips the first comma. It is fine if there is quotes on the first column, or it is not blank, but if it is empty with no quotes, it ignores it.
Here is a sample code I am using for testing purposes, it is written in Dart:
void main() {
String delimiter = ",";
String rawRow = ',,"data",moredata,"Data"';
RegExp exp = new RegExp(r'(?:'+ delimiter + r'"|^")(^,|""|[\w\W]*?)(?="'+ delimiter + r'|"$)|(?:'+ delimiter + '(?!")|^(?!"))([^'+ delimiter + r']*?)(?=$|'+ delimiter + r')');
Iterable<Match> matches = exp.allMatches(rawRow.replaceAll("\n","").replaceAll("\r","").trim());
List<String> row = new List();
matches.forEach((Match m) {
//This checks to see which match group it found the item in.
String cellValue;
if (m.group(2) != null) {
//Data found without speech marks
cellValue = m.group(2);
} else if (m.group(1) != null) {
//Data found with speech marks (so it removes escaped quotes)
cellValue = m.group(1).replaceAll('""', '"');
} else {
//Anything left
cellValue = m.group(0).replaceAll('""', '"');
}
row.add(cellValue);
});
print(row.toString());
}
Investigating your expression
(,"|^")
(""|[\w\W]*?)
(?=",|"$)
|
(,(?!")|^(?!"))
([^,]*?|)
(?=$|,)
(,"|^")(""|[\w\W]*?)(?=",|"$) This part is to match quoted strings, that seem to work for you
Going through this part (,(?!")|^(?!"))([^,]*?|)(?=$|,)
(,(?!")|^(?!")) start with comma not followed by " OR start of line not followed by "
([^,]*?|) Start of line or comma zero or more non greedy and |, why |
(?=$|,) end of line or , .
In CSV this ,,,3,4,5 line should give 6 matches but the above only gets 5
You could add (^(?=,)) at the begining of second part, the part that matches non quoted sections.
Second group with match of start and also added non capture to groups
(?:^(?=,))|(?:,(?!")|^(?!"))(?:[^,]*?)(?=$|,)
Complete: (?:,"|^")(?:""|[\w\W]*?)(?=",|"$)|(?:^(?=,))|(?:,(?!")|^(?!"))(?:[^,]*?)(?=$|,)
Here is another that might work
(?:(?:"(?:[^"]|"")*"|(?<=,)[^,]*(?=,))|^[^,]+|^(?=,)|[^,]+$|(?<=,)$)
How that works i described here: Build CSV parser using regex

Regex find most center

I need to manully hyphante words that are too long. Using hyphen.js, I get soft hyphens between every syllable, like below.
I want to find the hyphen closes to the middle. All words will be more than 14 characters long. Regex that works in https://regex101.com/ or node/js example.
Basically, find the middle character excluding hyphens, check if there is a hyphen there, then step backwards one step and then forwards one step, then backwards to steps etc.
re-spon-si-bil-i-ties => [re-spon-si,-bil-i-ties]
com-pe-ten-cies. => [com-pe,-ten-cies.]
ini-tia-tives. => [ini-tia,-tives]
vul-ner-a-bil-i-ties => [vul-ner-a,-bil-i-ties]
Here's a simple js approach based on string splitting. There could be a binary search style algorithm as you mentioned which would avoid the array allocation but that seems overkill for these small data sets.
function halve(str) {
var right = str.split('-');
var left = right.splice(0, Math.ceil(right.length / 2));
return right.length > 0 ? [left.join('-'), '-' + right.join('-')] : left;
}
console.log(halve('re-spon-si-bil-i-ties'));
console.log(halve('com-pe-ten-cies.'));
console.log(halve('ini-tia-tives.'));
console.log(halve('vul-ner-a-bil-i-ties'));
console.log(halve('none')); // no hyphens returns ["none"]
You can work this out with this method:
Get middle point of string
From the middle point, and checking each character in both directions (left from middle, right from middle) check if that position is the - character. Set the index to the first such match.
If it matches that character, stop the loop and split the string on that index, otherwise return the original word.
words = [
're-spon-si-bil-i-ties',
'com-pe-ten-cies.',
'ini-tia-tives.',
'vul-ner-a-bil-i-ties',
'test',
'-aa',
'aa-'
];
split = '-'
for(word of words) {
m=Math.floor(word.length/2),offset=0,i=null
do{
if(word[m-offset] == split) i = m-offset
else if(word[m+offset] == split) i = m+offset
else offset++
}while(offset<=m && i == null)
if(i!=null && i>0) console.log([word.substring(0,i),word.substring(i)])
else console.log(word)
}
You can achieve this with:
var words = [
're-spon-si-bil-i-ties',
'com-pe-ten-cies.',
'ini-tia-tives.',
'vul-ner-a-bil-i-ties',
're-ports—typ-i-cal-ly',
'none'
];
for(var i = 0; i < words.length; ++i){
var matches = words[i]
.match(
new RegExp(
'^((?:[^-]+?-?){' // Start the regex
+parseInt(
words[i].replace( /-/g, '' ).length/2 // Round down the halfway point of this word's length without the hyphens
)
+'})(-.+)?$' // End the regex
)
)
.slice( 1 ); // Remove position 0 because it is the entire word
console.log( matches );
}
Regex explanation for re-spon-si-bil-i-ties:
^((?:[^-]+?-?){8})(-.+)$
^( - start the capture group leading up to the half way point
(?:[^-]+?-?) - find everything not a hyphen with an optional hyphen after it. Make the hyphen optional so that the second capture group can greedily claim it
{8} - 8 times; this will get us half way
) - close the half way capture group
(-.+)?$ - greedily get the hyphen and everything after it till the end of the string

Split a word with regexp in matlab; startIndex for 'split'?

My aim is to generate the phonetic transcription for any word according to a set of rules.
First, I want to split words into their syllables. For example, I want an algorithm to find 'ch' in a word and then separate it like shown below:
Input: 'aachbutcher'
Output: 'a' 'a' 'ch' 'b' 'u' 't' 'ch' 'e' 'r'
I have come so far:
check=regexp('aachbutcher','ch');
if (isempty(check{1,1})==0) % Returns 0, when 'ch' was found.
[match split startIndex endIndex] = regexp('aachbutcher','ch','match','split')
%Now I split the 'aa', 'but' and 'er' into single characters:
for i = 1:length(split)
SingleLetters{i} = regexp(split{1,i},'.','match');
end
end
My problem is: How do I put the cells together, such that they are formatted like the desired output? I only have the starting indexes for the match parts ('ch') but not for the split parts ('aa', 'but','er').
Any ideas?
You don't need to work with the indices or length. Simple logic: Process first element from match, then first from split, then second from match etc....
[match,split,startIndex,endIndex] = regexp('aachbutcher','ch','match','split');
%Now I split the 'aa', 'but' and 'er' into single characters:
SingleLetters=regexp(split{1,1},'.','match');
for i = 2:length(split)
SingleLetters=[SingleLetters,match{i-1},regexp(split{1,i},'.','match')];
end
So, you know the length of 'ch', it's 2. You know where you found it from regex, as those indices are stored in startIndex. I'm assuming (Please, correct me if I'm wrong) that you want to split all other letters of the word into single-letter cells, like in your output above. So, you can just use the startIndex data to construct your output, using conditionals, like this:
check=regexp('aachbutcher','ch');
if (isempty(check{1,1})==0) % Returns 0, when 'ch' was found.
[match split startIndex endIndex] = regexp('aachbutcher','ch','match','split')
%Now I split the 'aa', 'but' and 'er' into single characters:
for i = 1:length(split)
SingleLetters{i} = regexp(split{1,i},'.','match');
end
end
j = 0;
for i = 1 : length('aachbutcher')
if (i ~= startIndex(1)) && (i ~= startIndex(2))
j = j +1;
output{end+1} = SingleLetters{j};
else
i = i + 1;
output{end+1} = 'ch';
end
end
I don't have MATLAB right now, so I can't test it. I hope it works for you! If not, let me know and I'll take anther shot at it.

End of Line Word Counting (C++)

I need to create a program that reads in a file, counts the words inside of it, and lists unique words with their frequency. The program considers any series of characters without spaces a word (so things like "hello." "hello" and ",.?" are all different words). I am having difficulty with using an if statement and adding a word at the end of the line to my word count. It counts the words that have spaces after them but not '/n'. This is the code I have for counting the words:
in.get(last);
in.get(current);
while(!in.eof())
{
if((current == ' ' && last != ' ') || (current == '/n' && last != ' ' && last != '/n'))
count++;
last = current;
in.get(current);
}
This is a painful way to do it... You are better off reading strings, which are automatically delimited by whitespace.
string word;
map<string,int> freq;
while( in >> word ) {
freq[word]++;
}
Note that in the example you gave, you used '/n', which should be '\n'. In my example, you don't even need it.
I would createca map,http://www.cplusplus.com/reference/map/map/, and if the word exists increment frequency otherwise add the word to the map.
This way you quickly check if the word exists, to have a unique list.

use regular expression to find and replace but only every 3 characters for DNA sequence

Is it possible to do a find/replace using regular expressions on a string of dna such that it only considers every 3 characters (a codon of dna) at a time.
for example I would like the regular expression to see this:
dna="AAACCCTTTGGG"
as this:
AAA CCC TTT GGG
If I use the regular expressions right now and the expression was
Regex.Replace(dna,"ACC","AAA") it would find a match, but in this case of looking at 3 characters at a time there would be no match.
Is this possible?
Why use a regex? Try this instead, which is probably more efficient to boot:
public string DnaReplaceCodon(string input, string match, string replace) {
if (match.Length != 3 || replace.Length != 3)
throw new ArgumentOutOfRangeException();
var output = new StringBuilder(input.Length);
int i = 0;
while (i + 2 < input.Length) {
if (input[i] == match[0] && input[i+1] == match[1] && input[i+2] == match[2]) {
output.Append(replace);
} else {
output.Append(input[i]);
output.Append(input[i]+1);
output.Append(input[i]+2);
}
i += 3;
}
// pick up trailing letters.
while (i < input.Length) output.Append(input[i]);
return output.ToString();
}
Solution
It is possible to do this with regex. Assuming the input is valid (contains only A, T, G, C):
Regex.Replace(input, #"\G((?:.{3})*?)" + codon, "$1" + replacement);
DEMO
If the input is not guaranteed to be valid, you can just do a check with the regex ^[ATCG]*$ (allow non-multiple of 3) or ^([ATCG]{3})*$ (sequence must be multiple of 3). It doesn't make sense to operate on invalid input anyway.
Explanation
The construction above works for any codon. For the sake of explanation, let the codon be AAA. The regex will be \G((?:.{3})*?)AAA.
The whole regex actually matches the shortest substring that ends with the codon to be replaced.
\G # Must be at beginning of the string, or where last match left off
((?:.{3})*?) # Match any number of codon, lazily. The text is also captured.
AAA # The codon we want to replace
We make sure the matches only starts from positions whose index is multiple of 3 with:
\G which asserts that the match starts from where the previous match left off (or the beginning of the string)
And the fact that the pattern ((?:.{3})*?)AAA can only match a sequence whose length is multiple of 3.
Due to the lazy quantifier, we can be sure that in each match, the part before the codon to be replaced (matched by ((?:.{3})*?) part) does not contain the codon.
In the replacement, we put back the part before the codon (which is captured in capturing group 1 and can be referred to with $1), follows by the replacement codon.
NOTE
As explained in the comment, the following is not a good solution! I leave it in so that others will not fall for the same mistake
You can usually find out where a match starts and ends via m.start() and m.end(). If m.start() % 3 == 0 you found a relevant match.