Jump to content
7erra

Count string in string

Recommended Posts

Hello everyone,

 

I wanted to count the linebreaks "<br/>" inside of another string but couldn't find a way. Example: "My first line<br/>My second line<br/>Third line<br/>you guessed it, fifth line<br/>I lied that was the fourth." should return 4. With toString [10] it would be easier since you could use

_lineCount = {_x == 10} count (toArray _string);

but not with parts that are longer than one character.

Share this post


Link to post
Share on other sites

How about this (untested):

 

CountNonOverlappingOccurrences = {
  params ["_array","_pattern"];
  private _patternSize = count _pattern;
  if (_patternSize == 0) exitWith {0};
  private _result = 0;
  // Let _i denote the start of potential match in _array
  for "_i" from 0 to (count _array - patternSize) do {
    scopeName "main";
    // Let _j denote the number of characters matched so far.
    private _j = 0;
    while ((_array select (_i + _j)) isEqualTo (_pattern select j)) do {
      _j = _j + 1;
      if (_j == _patternSize) then {
        //Got a match. Move next search to after current match
        _i = _i + _j;
        _result = _result + 1;
        breakTo "main";
      };
    };
  };
  _result
};

private _lineBreaks = [toArray _string, toArray "<br/>"] call CountNonOverlappingOccurrences;

 

  • Like 1

Share this post


Link to post
Share on other sites

 

Another way:

_str = "My first line<br/>My second line<br/>Third line<br/>you guessed it, fifth line<br/>I lied that was the fourth.";
_lines = count (_str splitString "/");

hint str (_lines); // 5

 

  • Like 1

Share this post


Link to post
Share on other sites

I would do:

 

_manipulate = "My first line<br/>My second line<br/>Third line<br/>you guessed it, fifth line<br/>I lied that was the fourth.";
_manipulate = _manipulate splitString "<>";
_manipulate = _manipulate select {_x find "br/" == -1};
diag_log _manipulate

 

Should return this:

 

["My first line","My second line","Third line","you guessed it, fifth line","I lied that was the fourth."]

Then you can do all the endl stuff with forEach to use it in a tooltip or whatever.

  • Like 2

Share this post


Link to post
Share on other sites

The only thing to be careful of with  splitString is that it will split on character in the delimiter. So if you split on "<>" you are fine with what @das attorney suggests, since you text will likely not contain those symbols. But if it did, you would be in trouble.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Muzzleflash said:

The only thing to be careful of with  splitString is that it will split on character in the delimiter.

That is the reason why I avoided using splitString but @das attorney's method is really simple and fast.

 

Here are the test results:

My own solution, worst of all:

Spoiler

_text = "Line<br/>";
for "_i" from 1 to 100 do {
	_text = _text +format ["Line %1<br/>",_i];
};
_tCount = count _text;
_duration = _tCount/20;
_lineHeight = 0.0607143;
_br = ["<br/>","<br />"];
_10 = toString [10];
_editString = _text;
{
	_char = count _x;
	_no = _editString find _x;
	private _loop = 0;
	while {-1 != _editString find _x} do {
		_no = _editString find _x;
		_splitStr = _editString splitString "";
		_splitStr deleteRange [(_no +1), _char -1];
		_splitStr set [_no, _10];
		_editString = _splitStr joinString "";
	};
} forEach _br;
_lineCount = {_x == 10} count (toArray _editString);
systemChat str _lineCount;

_deltaY = _lineCount;
[
	_text,
	-1,
	0 * safezoneH + safezoneY,
	_duration,
	1,
	_deltaY,
	789
] spawn BIS_fnc_dynamicText;

/*
	Result:
	13.6622 ms

	Cycles:
	74/10000
*/

 

 

@Muzzleflash's solution:

Spoiler


_text = "Line<br/>";
for "_i" from 1 to 100 do {
	_text = _text +format ["Line %1<br/>",_i];
};
// _text will later on be the input text
_tCount = count _text;
_duration = _tCount/20;
_lineHeight = 0.0607143;
_br = ["<br/>","<br />"];

_textArray = toArray _text;
_lineCount = 0;
{
	_brArray = toArray _x;
	private _patternSize = count _brArray;
	if (_patternSize == 0) exitWith {0};
	// Let _i denote the start of potential match in _textArray
	for "_i" from 0 to (count _textArray - _patternSize) do {
	scopeName "main";
	// Let _j denote the number of characters matched so far.
	private _j = 0;
	while {(_textArray select (_i + _j)) isEqualTo (_brArray select _j)} do {
		_j = _j + 1;
		if (_j == _patternSize) then {
			//Got a match. Move next search to after current match
			_i = _i + _j;
			_lineCount = _lineCount + 1;
			breakTo "main";
		};
	};
	};
} forEach _br;

systemChat str _lineCount;
_deltaY = _lineCount;
[
	_text,
	-1,
	0 * safezoneH + safezoneY,
	_duration,
	1,
	_deltaY,
	789
] spawn BIS_fnc_dynamicText;

/*
	Result:
	5.85965 ms

	Cycles:
	171/10000
*/

I edited some script errors and removed the call necessity.

 

And the best of them all:

@das attorney's script:


_text = "Line<br/>";
for "_i" from 1 to 100 do {
	_text = _text +format ["Line %1<br/>",_i];
};
_tCount = count _text;
_duration = _tCount/20;
_lineHeight = 0.0607143;
_br = ["<br/>","<br />"];

_manipulate = _text;
_manipulate = _manipulate splitString "<>";
_lineCount = {_x find "br/" != -1 OR _x find "br /" != -1} count _manipulate;
systemChat str _lineCount;

_deltaY = _lineCount;
[
	_text,
	-1,
	0 * safezoneH + safezoneY,
	_duration,
	1,
	_deltaY,
	789
] spawn BIS_fnc_dynamicText;

/*
	Result:
	0.499251 ms

	Cycles:
	2003/10000
*/

The splitString won't be a problem since im only counting "br/" and "br /".

 

Thanks everyone :)

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×