0

I'm trying to match a string which is only valid if all characters after the first occurrence of the target character, are also the the target character.

To better understand the structure, for example our target character is .. The string is divided into two parts. The matching string has the following structure:

  1. substring without the target character
  2. substring with no other characters than the target character

Let's examine some examples:

""
// true - 1: "" doesn't contain target - 2: "" doesn't contain not target

"2"
// true - 1: "2" doesn't contain target - 2: "" doesn't contain not target

"."
// true - 1: "" doesn't contain target - 2: "." doesn't contain not target (only target)

"2.."
// true - 1: "2" doesn't contain target - 2: ".." doesn't contain not target (only target)

"...."
// true - 1: "" doesn't contain target - 2: "...." doesn't contain not target (only target)

"..2"
// false - 1: "..2" contains target - 2: "" doesn't contain not target

"2.2"
// false - 1: "2.2" contains target - 2: "" doesn't contain not target

"2.2."
// false - 1: "2.2" contains target - 2: "." doesn't contain not target (only target)

I was approaching the problem first with String methods (JS) by checking index of first occurrence, then counting number of occurrences, comparing with the length of string to check if there are any other characters between the ending, which was solving the problem, but wasn't looking too nice, and I don't think it's the most efficient way to solve the issue.

It looks like this:

const validate = (string, targetChar) => {
  const firstTargetIndex = string.indexOf(targetChar);
  if (firstTargetIndex === -1) return true; //no chance of not target following a target

  const substringAfterFirstTarget = string.substr(firstTargetIndex);
  const numberOfTargets = substringAfterFirstTarget.split(targetChar).length - 1;
  return substringAfterFirstTarget.length === numberOfTargets;
}

Then I was researching regex methods to solve the problem, but I only found methods for checking occurrence, number of occurrence, if string ends with (even n times, but ignoring if there are occurrences between other characters), but couldn't figure a method to match the above test.

Gergő Horváth
  • 3,195
  • 4
  • 28
  • 64
  • 1
    please make an attempt before asking – depperm Aug 23 '21 at 14:35
  • You want to know my search history or my failed attempts? How would the results I've found not solving the issue help anyone? – Gergő Horváth Aug 23 '21 at 14:37
  • 1
    And you may be way closer to the solution than you expect – Cid Aug 23 '21 at 14:40
  • 1
    is this javascript or some other language? do you need to use regex alone or can you use other logic? – depperm Aug 23 '21 at 14:40
  • shouldn't "2.2." be true? – Andrew Aug 23 '21 at 14:49
  • No, only true if it ends with. If we would loop through the string, from the end, and keep removing the last characters if it’s a dot, we should end up with a string without dots. If there’s still a dot, it doesn’t match – Gergő Horváth Aug 23 '21 at 15:03
  • it looks to me like it DOES end with. I guess you mean "...is not in it, XOR ends with it..." OK, try: "^[^.]*\.\.*$|^[^.]$|^$" – Andrew Aug 23 '21 at 15:09
  • @GergőHorváth by showing at least one thing you've tried, it helps us get a sense of what you do or do not understand about how to approach the problem. It helps an answerer determine whether to explain everything from scratch, explain just one aspect of the solution, clear up a specific misunderstanding, or just point out a face-palm. – Wyck Aug 23 '21 at 15:15
  • 1
    Is this a valid rewording? _"All characters after the first occurrence of the specified character, must also be the specified character."_ – Wyck Aug 23 '21 at 15:38
  • Yes, it perfectly explains the goal – Gergő Horváth Aug 23 '21 at 16:13

3 Answers3

2

The regex ^[^.]*\.*$ should work. It can accept any none . character 0 or more times ([^.]*) and then it can be followed by any number of . (\.*)

const regex = /^[^.]*\.*$/gm;
const str = ['','2','.','2..','....','..2','2.2','2.2.'];

console.log(str.map(s=>s.match(regex)?'true':'false'))

// example from comments does return false
console.log(regex.test('..2.'))
depperm
  • 10,606
  • 4
  • 43
  • 67
  • If you only want `true` or `false`, you might as well use `regex.test(s)`. ([And remove the `gm` flags](https://stackoverflow.com/questions/1520800/why-does-a-regexp-with-global-flag-give-wrong-results).) – Ivar Aug 23 '21 at 15:02
  • ..2. returns true but should return false – Andrew Aug 23 '21 at 15:25
  • 1
    @Andrew Are you sure? I tried it and '..2.' **does** return false. – Wyck Aug 23 '21 at 15:43
  • I posted my results in my answer; please check. Maybe I missed something. – Andrew Aug 23 '21 at 16:32
0

In English: match if it is zero or more non-dot chars followed by zero or more dot chars:

Mac_3.2.57$cat test.txt | egrep "^[^.]*\.*$"
2
.
2..
....

x

Mac_3.2.57$cat test.txt

2
.
2..
....
..2
2.2
2.2.

x
Mac_3.2.57$

PS The previous answer did not work for me:

> const str = ['','2','.','2..','....','..2','2.2','2.2.'];
Uncaught SyntaxError: Identifier 'str' has already been declared
> console.log(str.map(s=>s.match(regex)?'true':'false'))
[
  'true',  'true',
  'true',  'true',
  'true',  'false',
  'false', 'false'
]
undefined
> const str = ['','2','.','2..','..2.','..2','2.2','2.2.'];
Uncaught SyntaxError: Identifier 'str' has already been declared
> console.log(str.map(s=>s.match(regex)?'true':'false'))
[
  'true',  'true',
  'true',  'true',
  'true',  'false',
  'false', 'false'
]
undefined
> 
Andrew
  • 1
  • 4
  • 19
0
  1. Case where dot is not in string: ^[\.]*$
  2. Case where dot ends the string: \.$

Put them together with alternation, you get dot at end or not at all

(\.$)|(^[\.]*$)
Chris Maurer
  • 2,339
  • 1
  • 9
  • 8