Multisplit: Difference between revisions

77,484 bytes added ,  1 month ago
Added Easylang
m (→‎Using Regular expressions: handle edge cases consistently.)
(Added Easylang)
 
(204 intermediate revisions by 85 users not shown)
Line 1:
{{task}} [[Category:String manipulation]]
{{draft task}}Code to split string with several separators.<br>
It is often necessary to split a string into pieces
Input: string, list of separators<br>
based on several different (potentially multi-character) separator strings,
Output: [Sub0, [Sep0Num, Sep0Pos], Sub1, [Sep1Num, Sep1Pos], ..., SubN]<br>
while still retaining the information about which separators were present in the input.
Note: Sub - substring, SepNum - separator number in input list, SepPos - separator position in input string.<br>
Input order of separators is important: they are considered in that order.
 
This is particularly useful when doing small parsing tasks. <br>
=={{header|J}}==
The task is to write code to demonstrate this.
 
The function (or procedure or method, as appropriate) should
<lang j>multisplit=:4 :0
take an input string and an ordered collection of separators.
'sep begin'=.|:t=. y /:~&.:(|."1)@;@(i.@#@[ ,.L:0"0 I.@E.L:0) x
 
end=. begin + sep { #@>y
The order of the separators is significant: <br>
last=.next=.0
The delimiter order represents priority in matching, with the first defined delimiter having the highest priority.
r=.2 0$0
In cases where there would be an ambiguity as to
while.next<#begin do.
which separator to use at a particular point
r=.r,.(last}.x{.~next{begin);next{t
(e.g., because one separator is a prefix of another)
last=.next{end
the separator with the highest priority should be used.
next=.1 i.~(begin>next{begin)*.begin>:last
Delimiters can be reused and the output from the function should be an ordered sequence of substrings.
 
Test your code using the input string “<code>a!===b=!=c</code>” and the separators “<code>==</code>”, “<code>!=</code>” and “<code>=</code>”.
 
For these inputs the string should be parsed as <code>"a" (!=) "" (==) "b" (=) "" (!=) "c"</code>, where matched delimiters are shown in parentheses, and separated strings are quoted, so our resulting output is <code>"a", empty string, "b", empty string, "c"</code>.
Note that the quotation marks are shown for clarity and do not form part of the output.
 
'''Extra Credit:''' provide information that indicates which separator was matched at each separation point and where in the input string that separator was matched.
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">F multisplit(text, sep)
V lastmatch = 0
V i = 0
V matches = ‘’
L i < text.len
L(s) sep
V j = L.index
I text[i..].starts_with(s)
I i > lastmatch
matches ‘’= text[lastmatch .< i]
matches ‘’= ‘{’s‘}’
lastmatch = i + s.len
i += s.len
L.break
L.was_no_break
i++
I i > lastmatch
matches ‘’= text[lastmatch .< i]
R matches
 
print(multisplit(‘a!===b=!=c’, [‘==’, ‘!=’, ‘=’]))</syntaxhighlight>
 
{{out}}
<pre>
a{!=}{==}b{=}{!=}c
</pre>
 
=={{header|Ada}}==
multisplit.adb:
<syntaxhighlight lang="ada">with Ada.Containers.Indefinite_Doubly_Linked_Lists;
with Ada.Text_IO;
 
procedure Multisplit is
package String_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists
(Element_Type => String);
use type String_Lists.Cursor;
 
function Split
(Source : String;
Separators : String_Lists.List)
return String_Lists.List
is
Result : String_Lists.List;
Next_Position : Natural := Source'First;
Prev_Position : Natural := Source'First;
Separator_Position : String_Lists.Cursor;
Separator_Length : Natural;
Changed : Boolean;
begin
loop
Changed := False;
Separator_Position := Separators.First;
while Separator_Position /= String_Lists.No_Element loop
Separator_Length :=
String_Lists.Element (Separator_Position)'Length;
if Next_Position + Separator_Length - 1 <= Source'Last
and then Source
(Next_Position .. Next_Position + Separator_Length - 1) =
String_Lists.Element (Separator_Position)
then
if Next_Position > Prev_Position then
Result.Append
(Source (Prev_Position .. Next_Position - 1));
end if;
Result.Append (String_Lists.Element (Separator_Position));
Next_Position := Next_Position + Separator_Length;
Prev_Position := Next_Position;
Changed := True;
exit;
end if;
Separator_Position := String_Lists.Next (Separator_Position);
end loop;
if not Changed then
Next_Position := Next_Position + 1;
end if;
if Next_Position > Source'Last then
Result.Append (Source (Prev_Position .. Source'Last));
exit;
end if;
end loop;
return Result;
end Split;
 
Test_Input : constant String := "a!===b=!=c";
Test_Separators : String_Lists.List;
Test_Result : String_Lists.List;
Pos : String_Lists.Cursor;
begin
Test_Separators.Append ("==");
Test_Separators.Append ("!=");
Test_Separators.Append ("=");
Test_Result := Split (Test_Input, Test_Separators);
Pos := Test_Result.First;
while Pos /= String_Lists.No_Element loop
Ada.Text_IO.Put (" " & String_Lists.Element (Pos));
Pos := String_Lists.Next (Pos);
end loop;
Ada.Text_IO.New_Line;
-- other order of separators
Test_Separators.Clear;
Test_Separators.Append ("=");
Test_Separators.Append ("!=");
Test_Separators.Append ("==");
Test_Result := Split (Test_Input, Test_Separators);
Pos := Test_Result.First;
while Pos /= String_Lists.No_Element loop
Ada.Text_IO.Put (" " & String_Lists.Element (Pos));
Pos := String_Lists.Next (Pos);
end loop;
end Multisplit;</syntaxhighlight>
 
{{out}}
<pre> a != == b = != c
a != = = b = != c</pre>
 
=={{header|ALGOL 68}}==
<syntaxhighlight lang="algol68"># split a string based on a number of separators #
 
# MODE to hold the split results #
MODE SPLITINFO = STRUCT( STRING text # delimited string, may be empty #
, INT position # starting position of the token #
, STRING delimiter # the delimiter that terminated the token #
);
# calculates the length of string s #
OP LENGTH = ( STRING s )INT: ( UPB s + 1 ) - LWB s;
# returns TRUE if s starts with p, FALSE otherwise #
PRIO STARTSWITH = 5;
OP STARTSWITH = ( STRING s, p )BOOL: IF LENGTH p > LENGTH s THEN FALSE ELSE s[ LWB s : ( LWB s + LENGTH p ) - 1 ] = p FI;
# returns an array of SPLITINFO describing the tokens in str based on the delimiters #
# zero-length delimiters are ignored #
PRIO SPLIT = 5;
OP SPLIT = ( STRING str, []STRING delimiters )[]SPLITINFO:
BEGIN
# count the number of tokens #
# allow there to be as many tokens as characters in the string + 2 #
# that would cater for a string composed of delimiters only #
[ 1 : ( UPB str + 3 ) - LWB str ]SPLITINFO tokens;
INT token count := 0;
INT str pos := LWB str;
INT str max = UPB str;
BOOL token pending := FALSE;
# construct the tokens #
str pos := LWB str;
INT prev pos := LWB str;
token count := 0;
token pending := FALSE;
WHILE str pos <= str max
DO
BOOL found delimiter := FALSE;
FOR d FROM LWB delimiters TO UPB delimiters WHILE NOT found delimiter DO
IF LENGTH delimiters[ d ] > 0 THEN
IF found delimiter := str[ str pos : ] STARTSWITH delimiters[ d ] THEN
token count +:= 1;
tokens[ token count ] := ( str[ prev pos : str pos - 1 ], prev pos, delimiters[ d ] );
str pos +:= LENGTH delimiters[ d ];
prev pos := str pos;
token pending := FALSE
FI
FI
OD;
IF NOT found delimiter THEN
# the current character is part of s token #
token pending := TRUE;
str pos +:= 1
FI
OD;
IF token pending THEN
# there is an additional token after the final delimiter #
token count +:= 1;
tokens[ token count ] := ( str[ prev pos : ], prev pos, "" )
FI;
# return an array of the actual tokens #
tokens[ 1 : token count ]
END # SPLIT # ;
 
 
# test the SPLIT operator #
[]SPLITINFO test tokens = "a!===b=!=c" SPLIT []STRING( "==", "!=", "=" );
FOR t FROM LWB test tokens TO UPB test tokens DO
SPLITINFO token = test tokens[ t ];
print( ( "token: [", text OF token, "] at: ", whole( position OF token, 0 ), " delimiter: (", delimiter OF token, ")", newline ) )
OD</syntaxhighlight>
{{out}}
<pre>
token: [a] at: 1 delimiter: (!=)
token: [] at: 4 delimiter: (==)
token: [b] at: 6 delimiter: (=)
token: [] at: 8 delimiter: (!=)
token: [c] at: 10 delimiter: ()
</pre>
 
=={{header|Arturo}}==
 
<syntaxhighlight lang="rebol">print split.by:["==" "!=" "="] "a!===b=!=c"</syntaxhighlight>
 
{{out}}
 
<pre>a b c</pre>
 
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">Str := "a!===b=!=c"
Sep := ["==","!=", "="]
Res := StrSplit(Str, Sep)
for k, v in Res
Out .= (Out?",":"") v
MsgBox % Out
for k, v in Sep
N .= (N?"|":"") "\Q" v "\E"
MsgBox % RegExReplace(str, "(.*?)(" N ")", "$1 {$2}")</syntaxhighlight>
{{out}}
<pre>a,,b,,c
a {!=} {==}b {=} {!=}c</pre>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
# syntax: GAWK -f MULTISPLIT.AWK
BEGIN {
str = "a!===b=!=c"
sep = "(==|!=|=)"
printf("str: %s\n",str)
printf("sep: %s\n\n",sep)
n = split(str,str_arr,sep,sep_arr)
printf("parsed: ")
for (i=1; i<=n; i++) {
printf("'%s'",str_arr[i])
if (i<n) { printf(" '%s' ",sep_arr[i]) }
}
printf("\n\nstrings: ")
for (i=1; i<=n; i++) {
printf("'%s' ",str_arr[i])
}
printf("\n\nseparators: ")
for (i=1; i<n; i++) {
printf("'%s' ",sep_arr[i])
}
printf("\n")
exit(0)
}
</syntaxhighlight>
{{out}}
<pre>
str: a!===b=!=c
sep: (==|!=|=)
 
parsed: 'a' '!=' '' '==' 'b' '=' '' '!=' 'c'
 
strings: 'a' '' 'b' '' 'c'
 
separators: '!=' '==' '=' '!='
</pre>
 
=={{header|BBC BASIC}}==
<syntaxhighlight lang="bbcbasic"> DIM sep$(2)
sep$() = "==", "!=", "="
PRINT "String splits into:"
PRINT FNmultisplit("a!===b=!=c", sep$(), FALSE)
PRINT "For extra credit:"
PRINT FNmultisplit("a!===b=!=c", sep$(), TRUE)
END
DEF FNmultisplit(s$, d$(), info%)
LOCAL d%, i%, j%, m%, p%, o$
p% = 1
REPEAT
m% = LEN(s$)
FOR i% = 0 TO DIM(d$(),1)
d% = INSTR(s$, d$(i%), p%)
IF d% IF d% < m% m% = d% : j% = i%
NEXT
IF m% < LEN(s$) THEN
o$ += """" + MID$(s$, p%, m%-p%) + """"
IF info% o$ += " (" + d$(j%) + ") " ELSE o$ += ", "
p% = m% + LEN(d$(j%))
ENDIF
UNTIL m% = LEN(s$)
= o$ + """" + MID$(s$, p%) + """"</syntaxhighlight>
{{out}}
<pre>
String splits into:
"a", "", "b", "", "c"
For extra credit:
"a" (!=) "" (==) "b" (=) "" (!=) "c"
</pre>
 
=={{header|Bracmat}}==
This is a surprisingly difficult task to solve in Bracmat, because in a naive solution using a alternating pattern ("=="|"!="|"=") the shorter pattern <code>"="</code> would have precedence over <code>"=="</code>. In the solution below the function <code>oneOf</code> iterates (by recursion) over the operators, trying to match the start of the current subject string <code>sjt</code> with one operator at a time, until success or reaching the end of the list with operators, whichever comes first. If no operator is found at the start of the current subject string, the variable <code>nonOp</code> is extended with one byte, thereby shifting the start of the current subject string one byte to the right. Then a new attempt is made to find an operator. This is repeated until either an operator is found, in which case the unparsed string is restricted to the part of the input after the found operator, or no operator is found, in which case the <code>whl</code> loop terminates.
<syntaxhighlight lang="bracmat">( ( oneOf
= operator
. !arg:%?operator ?arg
& ( @(!sjt:!operator ?arg)&(!operator.!arg)
| oneOf$!arg
)
)
& "a!===b=!=c":?unparsed
& "==" "!=" "=":?operators
& whl
' ( @( !unparsed
: ?nonOp [%(oneOf$!operators:(?operator.?unparsed))
)
& put$(!nonOp str$("{" !operator "} "))
)
& put$!unparsed
& put$\n
);</syntaxhighlight>
{{out}}
<pre>a {!=} {==} b {=} {!=} c</pre>
 
=={{header|C}}==
What kind of silly parsing is this?
<syntaxhighlight lang="c">#include <stdio.h>
#include <string.h>
 
void parse_sep(const char *str, const char *const *pat, int len)
{
int i, slen;
while (*str != '\0') {
for (i = 0; i < len || !putchar(*(str++)); i++) {
slen = strlen(pat[i]);
if (strncmp(str, pat[i], slen)) continue;
printf("{%.*s}", slen, str);
str += slen;
break;
}
}
}
 
int main()
{
const char *seps[] = { "==", "!=", "=" };
parse_sep("a!===b=!=c", seps, 3);
 
return 0;
}</syntaxhighlight>
{{out}}<syntaxhighlight lang="text">a{!=}{==}b{=}{!=}c</syntaxhighlight>
 
=={{header|C sharp}}==
 
'''Extra Credit Solution'''
 
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Multisplit
{
internal static class Program
{
private static void Main(string[] args)
{
foreach (var s in "a!===b=!=c".Multisplit(true, "==", "!=", "=")) // Split the string and return the separators.
{
Console.Write(s); // Write the returned substrings and separators to the console.
}
Console.WriteLine();
}
 
private static IEnumerable<string> Multisplit(this string s, bool returnSeparators = false,
params string[] delimiters)
{
var currentString = new StringBuilder(); /* Initiate the StringBuilder. This will hold the current string to return
* once we find a separator. */
 
int index = 0; // Initiate the index counter at 0. This tells us our current position in the string to read.
 
while (index < s.Length) // Loop through the string.
{
// This will get the highest priority separator found at the current index, or null if there are none.
string foundDelimiter =
(from delimiter in delimiters
where s.Length >= index + delimiter.Length &&
s.Substring(index, delimiter.Length) == delimiter
select delimiter).FirstOrDefault();
 
if (foundDelimiter != null)
{
yield return currentString.ToString(); // Return the current string.
if (returnSeparators) // Return the separator, if the user specified to do so.
yield return
string.Format("{{\"{0}\", ({1}, {2})}}",
foundDelimiter,
index, index + foundDelimiter.Length);
currentString.Clear(); // Clear the current string.
index += foundDelimiter.Length; // Move the index past the current separator.
}
else
{
currentString.Append(s[index++]); // Add the character at this index to the current string.
}
}
 
if (currentString.Length > 0)
yield return currentString.ToString(); // If we have anything left over, return it.
}
}
}</syntaxhighlight>
 
{{out}}
<pre>a{"!=", (1, 3)}{"==", (3, 5)}b{"=", (6, 7)}{"!=", (7, 9)}c
</pre>
 
=={{header|C++}}==
using the Boost library tokenizer!
<syntaxhighlight lang="cpp">#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
 
int main( ) {
std::string str( "a!===b=!=c" ) , output ;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer ;
boost::char_separator<char> separator ( "==" , "!=" ) , sep ( "!" ) ;
tokenizer mytok( str , separator ) ;
tokenizer::iterator tok_iter = mytok.begin( ) ;
for ( ; tok_iter != mytok.end( ) ; ++tok_iter )
output.append( *tok_iter ) ;
tokenizer nexttok ( output , sep ) ;
for ( tok_iter = nexttok.begin( ) ; tok_iter != nexttok.end( ) ;
++tok_iter )
std::cout << *tok_iter << " " ;
std::cout << '\n' ;
return 0 ;
}</syntaxhighlight>
{{out}}
<PRE>a b c</PRE>
 
===Without external libraries===
<syntaxhighlight lang="c++">
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
 
struct Split_data {
std::string segment;
int32_t index;
std::string separator;
};
 
std::vector<Split_data> multi_split(const std::string& text, const std::vector<std::string>& separators) {
std::vector<Split_data> result;
uint64_t i = 0;
std::string segment = "";
while ( i < text.length() ) {
bool found = false;
for ( std::string separator : separators ) {
if ( text.substr(i, separator.length()) == separator ) {
found = true;
result.emplace_back(segment, i, separator);
i += separator.length();
segment = "";
break;
}
}
 
if ( ! found ) {
segment += text[i];
i += 1;
}
}
result.emplace_back(segment, i, "");
return result;
}
 
int main() {
for ( Split_data splits : multi_split("a!===b=!=c", { "==", "!=", "=" } ) ) {
std::cout << std::left << std::setw(3) << "\"" + splits.segment + "\""
<< std::setw(18) << " ( split with \"" + splits.separator + "\""
<< " at index " << splits.index << " )" << std::endl;
}
}
</syntaxhighlight>
{{ out }}
<pre>
"a" ( split with "!=" at index 1 )
"" ( split with "==" at index 3 )
"b" ( split with "=" at index 6 )
"" ( split with "!=" at index 7 )
"c" ( split with "" at index 10 )
</pre>
 
 
===C++23===
<syntaxhighlight lang="c++">
/* multisplit.cpp */
#include <features.h>
#include <iostream>
#include <string>
#include <vector>
#include <format>
 
/* C++23 example for Multisplit 6 Jan 2024
email:
spikeysnack@gmail.com
 
compile:
g++-13 -std=c++23 -Wall -o multisplit multisplit.cpp
*/
 
// extra info
#define _EXTRA
 
// aliases
using std::string;
using std::vector;
using str_vec = vector<string>;
using std::cout;
 
 
// constants
constexpr static const size_t npos = -1;
 
// function signatures
string replace_all(string& str, string& remove, string& insert );
 
str_vec split_on_delim(string& str, const string& delims);
 
str_vec Multisplit( string& input, const str_vec& seps);
 
// functions
 
// replace all substrings in string
// a = "dogs and cats and dogs and cats and birds"
// replace(a, "cats" , "fish");
// ==> "dogs and fish and dogs and fish and birds"
 
string replace_all(string& str,
const string& remove,
const string& insert ){
string s{str};
string::size_type pos = 0;
 
#ifdef _EXTRA
const string rightarrow{"\u2B62"}; //unicode arrow
auto ex = std::format("match: {}\t{} ", remove, rightarrow);
std::cerr << ex;
#endif
while ((pos = s.find(remove, pos)) != npos){
s.replace(pos, remove.size(), insert);
pos++;
}
 
return s;
}
 
 
// create a string vector from a string,
// split on a delimiter string
// x = "ab:cde:fgh:ijk"
// split_on_delim( x, ":");
// ==> { "ab", "cde", "fgh", "ijk" }
 
str_vec split_on_delim(string& str, const string& delims) {
string::size_type beg, pos = 0;
str_vec sv;
string tmp;
while ( (beg = str.find_first_not_of(delims, pos)) != npos ){
 
pos = str.find_first_of(delims, beg + 1);
 
tmp = { str.substr(beg, pos - beg) };
 
sv.push_back(tmp);
}
return sv;
}
 
 
str_vec Multisplit( string& input, const str_vec& seps) {
 
string s1{input};
str_vec sv;
 
for( auto sep : seps){
s1 = replace_all(s1, sep, "^"); // space sep
 
#ifdef _EXTRA
std::cerr << s1 << "\n";
#endif
sv = split_on_delim(s1, "^"); // split
}
return sv;
}
 
 
/* main program */
 
int main(){
string sample{"a!===b=!=c"};
 
const str_vec seps {"!=", "==", "="};
 
auto s = std::format("sample: \t{}\n", sample);
 
cout << s;
 
auto sv = Multisplit(sample, seps);
 
for( auto s : sv){
auto out = std::format( "{}\t" , s);
cout << out;
}
cout << "\n";
return 0;
}
 
// end
</syntaxhighlight>
 
{{ out }}
<pre>
sample: a!===b=!=c
match: != ⭢ a^==b=^c
match: == ⭢ a^^b=^c
match: = ⭢ a^^b^^c
a b c
 
</pre>
 
=={{header|CoffeeScript}}==
<syntaxhighlight lang="coffeescript">
multi_split = (text, separators) ->
# Split text up, using separators to break up text and discarding
# separators.
#
# Returns an array of strings, which can include empty strings when
# separators are found either adjacent to each other or at the
# beginning/end of the text.
#
# Separators have precedence, according to their order in the array,
# and each separator should be at least one character long.
result = []
i = 0
s = ''
while i < text.length
found = false
for separator in separators
if text.substring(i, i + separator.length) == separator
found = true
i += separator.length
result.push s
s = ''
break
if !found
s += text[i]
i += 1
result.push s
result
console.log multi_split 'a!===b=!=c', ['==', '!=', '='] # [ 'a', '', 'b', '', 'c' ]
console.log multi_split '', ['whatever'] # [ '' ]
</syntaxhighlight>
 
=={{header|D}}==
<syntaxhighlight lang="d">import std.stdio, std.array, std.algorithm;
 
string[] multiSplit(in string s, in string[] divisors) pure nothrow {
string[] result;
auto rest = s.idup;
 
while (true) {
bool done = true;
string delim;
{
string best;
foreach (const div; divisors) {
const maybe = rest.find(div);
if (maybe.length > best.length) {
best = maybe;
delim = div;
done = false;
}
}
}
result.length++;
if (done) {
result.back = rest.idup;
return result;
} else {
const t = rest.findSplit(delim);
result.back = t[0].idup;
rest = t[2];
}
}
}
 
void main() {
"a!===b=!=c"
.multiSplit(["==", "!=", "="])
.join(" {} ")
.writeln;
}</syntaxhighlight>
{{out}} (separator locations indicated by braces):
<pre>a {} {} b {} {} c</pre>
=={{header|Delphi}}==
{{libheader| System.SysUtils}}
<syntaxhighlight lang="delphi">
program Multisplit;
 
{$APPTYPE CONSOLE}
 
uses
System.SysUtils;
 
begin
write('[');
for var s in 'a!===b=!=c'.Split(['==', '!=', '=']) do
write(s.QuotedString('"'), ' ');
write(']');
readln;
end.</syntaxhighlight>
{{out}}
<pre>["a" "" "b" "" "c" ]</pre>
=={{header|EasyLang}}==
<syntaxhighlight>
proc multisplit str$ sep$[] . .
repeat
min = 1 / 0
for sep$ in sep$[]
pos = strpos str$ sep$
if pos > 0 and pos < min
min = pos
msep$ = sep$
.
.
until min = 1 / 0
write substr str$ 1 (min - 1) & "{" & msep$ & "}"
str$ = substr str$ (min + len msep$) 9999
.
print str$
.
multisplit "a!===b=!=c" [ "==" "!=" "=" ]
</syntaxhighlight>
{{out}}
<pre>
a{!=}{==}b{=}{!=}c
</pre>
 
=={{header|Elixir}}==
{{trans|Erlang}}
<syntaxhighlight lang="elixir">iex(1)> Regex.split(~r/==|!=|=/, "a!====b=!=c")
["a", "", "", "b", "", "c"]</syntaxhighlight>
 
=={{header|Erlang}}==
<pre>
20> re:split("a!===b=!=c", "==|!=|=",[{return, list}]).
["a",[],"b",[],"c"]
</pre>
 
 
=={{header|F_Sharp|F#}}==
 
If we ignore the "Extra Credit" requirements and skip 'ordered separators' condition (i.e. solving absolute different task), this is exactly what one of the overloads of .NET's <code>String.Split</code> method does. Using F# Interactive:
<syntaxhighlight lang="fsharp">> "a!===b=!=c".Split([|"=="; "!="; "="|], System.StringSplitOptions.None);;
val it : string [] = [|"a"; ""; "b"; ""; "c"|]
 
> "a!===b=!=c".Split([|"="; "!="; "=="|], System.StringSplitOptions.None);;
val it : string [] = [|"a"; ""; ""; "b"; ""; "c"|]</syntaxhighlight>
 
<code>System.StringSplitOptions.None</code> specifies that empty strings should be included in the result.
 
=={{header|Factor}}==
<syntaxhighlight lang="factor">USING: arrays fry kernel make sequences ;
 
IN: rosetta-code.multisplit
 
: first-subseq ( seq separators -- n separator )
tuck
[ [ subseq-index ] dip 2array ] withd map-index sift-keys
[ drop f f ] [ [ first ] infimum-by first2 rot nth ] if-empty ;
 
: multisplit ( string separators -- seq )
'[
[ dup _ first-subseq dup ] [
length -rot cut-slice [ , ] dip swap tail-slice
] while 2drop ,
] { } make ;</syntaxhighlight>
 
{{out}}
<pre>> "a!===b=!=c" { "==" "!=" "=" } multisplit [ >string ] map .
 
{ "a" "" "b" "" "c" }</pre>
 
=={{header|FreeBASIC}}==
FreeBASIC does not have a built in 'split' function so we need to write one:
<syntaxhighlight lang="freebasic">' FB 1.05.0 Win64
 
Sub Split(s As String, sepList() As String, result() As String, removeEmpty As Boolean = False, showSepInfo As Boolean = False)
If s = "" OrElse UBound(sepList) = -1 Then
Redim result(0)
result(0) = s
Return
End If
Dim As Integer i = 0, j, count = 0, empty = 0, length
Dim As Integer position(len(s) + 1)
Dim As Integer sepIndex(1 To len(s))
Dim As Integer sepLength(len(s))
position(0) = 0 : sepLength(0) = 1
While i < Len(s)
For j = lbound(sepList) To ubound(sepList)
length = len(sepList(j))
If length = 0 Then Continue For '' ignore blank separators
If mid(s, i + 1, length) = sepList(j) Then
count += 1
position(count) = i + 1
sepIndex(count) = j
sepLength(count) = length
i += length - 1
Exit For
End If
Next j
i += 1
Wend
 
Redim result(count)
If count = 0 Then
If showSepInfo Then
Print "No delimiters were found" : Print
End If
result(0) = s
Return
End If
position(count + 1) = len(s) + 1
For i = 1 To count + 1
length = position(i) - position(i - 1) - sepLength(i - 1)
result(i - 1 - empty) = Mid(s, position(i - 1) + sepLength(i - 1), length)
If removeEmpty AndAlso cbool(length = 0) Then empty += 1
Next
 
If empty > 0 Then Redim Preserve result(count - empty)
 
If showSepInfo Then
Print "The 1-based indices of the delimiters found are : "
Print
For x As Integer = 1 To count
Print "At index"; position(x), sepList(sepIndex(x))
Next
Print
End If
End Sub
 
 
Dim s As String = "a!===b=!=c"
Print "The string to be split is : "; s
Print
Dim a() As String '' to hold results
Dim b(1 To 3) As String = {"==", "!=", "="} '' separators to be used in order of priority (highest first)
split s, b(), a(), False, True '' show separator info
Print "The sub-strings are : "
Print
For i As integer = 0 To ubound(a)
Print Using "##"; i + 1;
Print " : "; a(i)
Next
Print
Print "Press any key to quit"
Sleep</syntaxhighlight>
 
{{out}}
<pre>
The 1-based indices of the delimiters found are :
 
At index 2 !=
At index 4 ==
At index 7 =
At index 8 !=
 
The sub-strings are :
 
1 : a
2 :
3 : b
4 :
5 : c
</pre>
 
=={{header|Go}}==
<syntaxhighlight lang="go">package main
 
import (
"fmt"
"strings"
)
 
func ms(txt string, sep []string) (ans []string) {
for txt > "" {
sepMatch := ""
posMatch := len(txt)
for _, s := range sep {
if p := strings.Index(txt, s); p >= 0 && p < posMatch {
sepMatch = s
posMatch = p
}
}
ans = append(ans, txt[:posMatch])
txt = txt[posMatch+len(sepMatch):]
}
return
}
 
func main() {
fmt.Printf("%q\n", ms("a!===b=!=c", []string{"==", "!=", "="}))
}</syntaxhighlight>
{{out}}
<pre>
["a" "" "b" "" "c"]
</pre>
 
=={{header|Haskell}}==
<syntaxhighlight lang="haskell">import Data.List
( genericLength,
intercalate,
isPrefixOf,
stripPrefix,
)
------------------------ MULTISPLIT ----------------------
 
multisplit :: [String] -> String -> [(String, String, Int)]
multisplit delims = go [] 0
where
go acc pos [] = [(acc, [], pos)]
go acc pos l@(s : sx) =
case trysplit delims l of
Nothing -> go (s : acc) (pos + 1) sx
Just (d, sxx) ->
(acc, d, pos) :
go [] (pos + genericLength d) sxx
 
trysplit :: [String] -> String -> Maybe (String, String)
trysplit delims s =
case filter (`isPrefixOf` s) delims of
[] -> Nothing
(d : _) -> Just (d, (\(Just x) -> x) $ stripPrefix d s)
 
--------------------------- TEST -------------------------
main :: IO ()
main = do
let parsed = multisplit ["==", "!=", "="] "a!===b=!=c"
mapM_
putStrLn
[ "split string:",
intercalate "," $ map (\(a, _, _) -> a) parsed,
"with [(string, delimiter, offset)]:",
show parsed
]</syntaxhighlight>
{{out}}
<pre>split string:
a,,b,,c
with [(string, delimiter, offset)]:
[("a","!=",1),("","==",3),("b","=",6),("","!=",7),("c","",10)]</pre>
 
Or as a fold:
 
<syntaxhighlight lang="haskell">import Data.List (find, isPrefixOf, foldl') --'
import Data.Bool (bool)
 
multiSplit :: [String] -> String -> [(String, String, Int)]
multiSplit ds s =
let (ts, ps, o) =
foldl' --'
(\(tokens, parts, offset) (c, i) ->
let inDelim = offset > i
in maybe
(bool (c : tokens) tokens inDelim, parts, offset)
(\x -> ([], (tokens, x, i) : parts, i + length x))
(bool (find (`isPrefixOf` drop i s) ds) Nothing inDelim))
([], [], 0)
(zip s [0 ..])
in reverse $ (ts, [], length s) : ps
main :: IO ()
main = print $ multiSplit ["==", "!=", "="] "a!===b=!=c"</syntaxhighlight>
{{Out}}
<pre>[("a","!=",1),("","==",3),("b","=",6),("","!=",7),("c","",10)]</pre>
 
=={{header|Icon}} and {{header|Unicon}}==
<syntaxhighlight lang="icon">procedure main()
s := "a!===b=!=c"
# just list the tokens
every writes(multisplit(s,["==", "!=", "="])," ") | write()
# list tokens and indices
every ((p := "") ||:= t := multisplit(s,sep := ["==", "!=", "="])) | break write() do
if t == !sep then writes(t," (",*p+1-*t,") ") else writes(t," ")
end
 
procedure multisplit(s,L)
s ? while not pos(0) do {
t := =!L | 1( arb(), match(!L)|pos(0) )
suspend t
}
end
 
procedure arb()
suspend .&subject[.&pos:&pos <- &pos to *&subject + 1]
end</syntaxhighlight>
 
{{out}}
<pre>a != == b = != c
a != (2) == (4) b = (7) != (8) c</pre>
 
=={{header|J}}==
<syntaxhighlight lang="j">multisplit=: {{
'begin sep'=. |:bs=. _,~/:~;(,.&.>i.@#) y I.@E.L:0 x NB.
len=. #@>y NB.
r=. i.3 0
j=. k=. 0
while.j<#x do.
while. j>k{begin do. k=.k+1 end.
'b s'=. k{bs NB. character index where separator appears, separator index
if. _=b do. r,.(j}.x);'';'' return. end.
txt=. (j + i. b-j){x
j=. b+s{len
r=.r,.txt;(s{::y);b
end.
}}</syntaxhighlight>
r=.r,.'';~last}.x
)</lang>
 
Explanation:
 
First find all potentially relevant separator instances, and sort them in increasing order, by starting location and separator index. <code>sep</code> is separator index, and <code>begin</code> is starting location. <code>end</code> is ending location.
 
Then, loop through the possibilities, skipping over those separators which would overlap with previously used separators.
 
The result consists of three rows: The first row is the extracted substrings, the second and third rows are the "extra credit" part -- for each extracted substring: the following separator and the position in the original string where that separator started. Note that the very last substring does not have a separator following it, so the extra credit part is blank for that substring.
Then, loop through the possibilities, skipping over those which conflict with the currently selected sequence.
 
Example use:
 
<langsyntaxhighlight lang="j"> S multisplit '==';'!=';'='
┌──┬──┬─┬──┬─┐
┌───┬───┬───┬───┬─┐
│a │b │ │b│ │c│
├──┼──┼─┼──┼─┤
├───┼───┼───┼───┼─┤
│!=│==│=│!=│ │
│1 1│0 3│2 6│1 7│ │
├──┼──┼─┼──┼─┤
└───┴───┴───┴───┴─┘
│1 │3 │6│7 │ │
└──┴──┴─┴──┴─┘
S multisplit '=';'!=';'=='
┌──┬─┬─┬─┬──┬─┐
┌───┬───┬───┬───┬───┬─┐
│a │ │b │b│ │c│
├──┼─┼─┼─┼──┼─┤
├───┼───┼───┼───┼───┼─┤
│!=│=│=│=│!=│ │
│1 1│0 3│0 4│0 6│1 7│ │
├──┼─┼─┼─┼──┼─┤
└───┴───┴───┴───┴───┴─┘
│1 │3│4│6│7 │ │
└──┴─┴─┴─┴──┴─┘
'X123Y' multisplit '1';'12';'123';'23';'3'
┌─┬──┬─┐
┌───┬───┬─┐
│X │ │X│ │Y│
├─┼──┼─┤
├───┼───┼─┤
│0 1│3 2││1│23│
├─┼──┼─┤
└───┴───┴─┘</lang>
│1│2 │ │
└─┴──┴─┘</syntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang="java">import java.util.*;
 
public class MultiSplit {
 
public static void main(String[] args) {
System.out.println("Regex split:");
System.out.println(Arrays.toString("a!===b=!=c".split("==|!=|=")));
 
System.out.println("\nManual split:");
for (String s : multiSplit("a!===b=!=c", new String[]{"==", "!=", "="}))
System.out.printf("\"%s\" ", s);
}
 
static List<String> multiSplit(String txt, String[] separators) {
List<String> result = new ArrayList<>();
int txtLen = txt.length(), from = 0;
 
for (int to = 0; to < txtLen; to++) {
for (String sep : separators) {
int sepLen = sep.length();
if (txt.regionMatches(to, sep, 0, sepLen)) {
result.add(txt.substring(from, to));
from = to + sepLen;
to = from - 1; // compensate for the increment
break;
}
}
}
if (from < txtLen)
result.add(txt.substring(from));
return result;
}
}</syntaxhighlight>
 
<pre>Regex split:
[a, , b, , c]
 
Manual split:
"a" "" "b" "" "c"</pre>
 
=={{header|JavaScript}}==
===ES5===
Based on Ruby example.
{{libheader|Underscore.js}}
<syntaxhighlight lang="javascript">RegExp.escape = function(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
 
multisplit = function(string, seps) {
var sep_regex = RegExp(_.map(seps, function(sep) { return RegExp.escape(sep); }).join('|'));
return string.split(sep_regex);
}</syntaxhighlight>
 
===ES6===
Without importing an external library, and generalizing to allow for any set of delimiters (avoiding the need for a hand-crafted regex):
 
{{Trans|Haskell}} (Multisplit by fold example)
<syntaxhighlight lang="javascript">(() => {
 
/// Delimiter list -> String -> list of parts, delimiters, offsets
 
// multiSplit :: [String] -> String ->
// [{part::String, delim::String, offset::Int}]
const multiSplit = (ds, s) => {
const
dcs = map(chars, ds),
xs = chars(s),
dct = foldl(
(a, c, i, s) => {
const
inDelim = a.offset > i,
mb = inDelim ? (
nothing('')
) : find(d => isPrefixOf(d, drop(i, xs)), dcs);
return mb.nothing ? {
tokens: a.tokens.concat(inDelim ? (
[]
) : [c]),
parts: a.parts,
offset: a.offset
} : {
tokens: [],
parts: append(a.parts, [{
part: intercalate('', a.tokens),
delim: intercalate('', mb.just),
offset: i
}]),
offset: i + length(mb.just)
};
}, {
tokens: [],
parts: [],
offset: 0
}, xs
);
return append(dct.parts, [{
part: intercalate('', dct.tokens),
delim: "",
offset: length(s)
}]);
};
 
// GENERIC FUNCTIONS -----------------------------------------------------
 
// append (++) :: [a] -> [a] -> [a]
const append = (xs, ys) => xs.concat(ys);
 
// chars :: String -> [Char]
const chars = s => s.split('');
 
// drop :: Int -> [a] -> [a]
// drop :: Int -> String -> String
const drop = (n, xs) => xs.slice(n);
 
// find :: (a -> Bool) -> [a] -> Maybe a
const find = (p, xs) => {
for (var i = 0, lng = xs.length; i < lng; i++) {
var x = xs[i];
if (p(x)) return just(x);
}
return nothing('Not found');
};
 
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = (f, a, xs) => xs.reduce(f, a);
 
// intercalate :: String -> [String] -> String
const intercalate = (s, xs) => xs.join(s);
 
// isPrefixOf takes two lists or strings and returns
// true iff the first is a prefix of the second.
// isPrefixOf :: [a] -> [a] -> Bool
// isPrefixOf :: String -> String -> Bool
const isPrefixOf = (xs, ys) => {
const pfx = (xs, ys) => xs.length ? (
ys.length ? xs[0] === ys[0] && pfx(
xs.slice(1), ys.slice(1)
) : false
) : true;
return typeof xs !== 'string' ? pfx(xs, ys) : ys.startsWith(xs);
};
 
// just :: a -> Just a
const just = x => ({
nothing: false,
just: x
});
 
// length :: [a] -> Int
const length = xs => xs.length;
 
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
 
// nothing :: () -> Nothing
const nothing = (optionalMsg) => ({
nothing: true,
msg: optionalMsg
});
 
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[1], null, x[0]] : x
);
 
// TEST ------------------------------------------------------------------
const
strTest = 'a!===b=!=c',
delims = ['==', '!=', '='];
 
return show(2,
multiSplit(delims, strTest)
);
})();</syntaxhighlight>
{{Out}}
<pre>[
{
"part": "a",
"delim": "!=",
"offset": 1
},
{
"part": "",
"delim": "==",
"offset": 3
},
{
"part": "b",
"delim": "=",
"offset": 6
},
{
"part": "",
"delim": "!=",
"offset": 7
},
{
"part": "c",
"delim": "",
"offset": 10
}
]</pre>
 
=={{header|jq}}==
{{Works with|jq|1.4}}
This version does not use regular expressions, which are only supported in later versions of jq.
 
multisplit(delims) produces the desired parse using an intermediate parse produced by multisplit_parse(delims).
 
Both helper functions could be made inner functions of the main function, but are kept separate here for clarity.
 
<syntaxhighlight lang="jq"># peeloff(delims) either peels off a delimiter or
# a single character from the input string.
# The input should be a nonempty string, and delims should be
# a non-empty array of delimiters;
# return [peeledoff, remainder]
# where "peeledoff" is either [delim] or the peeled off character:
def peeloff(delims):
delims[0] as $delim
| if startswith($delim) then [ [$delim], .[ ($delim|length):]]
elif (delims|length)>1 then peeloff(delims[1:])
else [ .[0:1], .[1:]]
end ;
 
# multisplit_parse(delims) produces an intermediate parse.
# Input must be of the parse form: [ string, [ delim ], ... ]
# Output is of the same form.
def multisplit_parse(delims):
if (delims|length) == 0 or length == 0 then .
else
.[length-1] as $last
| .[0:length-1] as $butlast
| if ($last|type) == "array" then . # all done
elif $last == "" then .
else
($last | peeloff(delims)) as $p # [ peeledoff, next ]
| $p[0] as $peeledoff
| $p[1] as $next
| if ($next|length) > 0
then $butlast + [$peeledoff] + ([$next]|multisplit_parse(delims))
else $butlast + $p
end
end
end ;
 
def multisplit(delims):
[.] | multisplit_parse(delims)
# insert "" between delimiters, compress strings, remove trailing "" if any
| reduce .[] as $x ([];
if length == 0 then [ $x ]
elif ($x|type) == "array"
then if (.[length-1]|type) == "array" then . + ["", $x]
else . + [$x]
end
elif .[length-1]|type == "string"
then .[0:length-1] + [ .[length-1] + $x ]
else . + [$x]
end ) ;</syntaxhighlight>
'''Examples'''
("a!===b=!=c",
"aaa!===bbb=!=ccc") | multisplit( ["==", "!=", "="] )
{{Out}}
$ jq -n -M -c -f multisplit.jq
["a",["!="],"",["=="],"b",["="],"",["!="],"c"]
["aaa",["!="],"",["=="],"bbb",["="],"",["!="],"ccc"]
 
=={{header|Julia}}==
From REPL:
<syntaxhighlight lang="julia">
julia> split(s, r"==|!=|=")
5-element Array{SubString{String},1}:
"a"
""
"b"
""
"c"
</syntaxhighlight>
 
=={{header|Kotlin}}==
<syntaxhighlight lang="scala">// version 1.0.6
 
fun main(args: Array<String>) {
val input = "a!===b=!=c"
val delimiters = arrayOf("==", "!=", "=")
val output = input.split(*delimiters).toMutableList()
for (i in 0 until output.size) {
if (output[i].isEmpty()) output[i] = "empty string"
else output[i] = "\"" + output[i] + "\""
}
println("The splits are:")
println(output)
 
// now find positions of matched delimiters
val matches = mutableListOf<Pair<String, Int>>()
var index = 0
while (index < input.length) {
var matched = false
for (d in delimiters) {
if (input.drop(index).take(d.length) == d) {
matches.add(d to index)
index += d.length
matched = true
break
}
}
if (!matched) index++
}
println("\nThe delimiters matched and the indices at which they occur are:")
println(matches)
}</syntaxhighlight>
 
{{out}}
<pre>
The splits are:
["a", empty string, "b", empty string, "c"]
 
The delimiters matched and the indices at which they occur are:
[(!=, 1), (==, 3), (=, 6), (!=, 7)]
</pre>
 
=={{header|Lua}}==
The function I've written here is really excessive for this task but it has historically been hard to find example code for a good Lua split function on the Internet. This one behaves the same way as Julia's Base.split and I've included a comment describing its precise operation.
<syntaxhighlight lang="lua">--[[
Returns a table of substrings by splitting the given string on
occurrences of the given character delimiters, which may be specified
as a single- or multi-character string or a table of such strings.
If chars is omitted, it defaults to the set of all space characters,
and keep is taken to be false. The limit and keep arguments are
optional: they are a maximum size for the result and a flag
determining whether empty fields should be kept in the result.
]]
function split (str, chars, limit, keep)
local limit, splitTable, entry, pos, match = limit or 0, {}, "", 1
if keep == nil then keep = true end
if not chars then
for e in string.gmatch(str, "%S+") do
table.insert(splitTable, e)
end
return splitTable
end
while pos <= str:len() do
match = nil
if type(chars) == "table" then
for _, delim in pairs(chars) do
if str:sub(pos, pos + delim:len() - 1) == delim then
match = string.len(delim) - 1
break
end
end
elseif str:sub(pos, pos + chars:len() - 1) == chars then
match = string.len(chars) - 1
end
if match then
if not (keep == false and entry == "") then
table.insert(splitTable, entry)
if #splitTable == limit then return splitTable end
entry = ""
end
else
entry = entry .. str:sub(pos, pos)
end
pos = pos + 1 + (match or 0)
end
if entry ~= "" then table.insert(splitTable, entry) end
return splitTable
end
 
local multisplit = split("a!===b=!=c", {"==", "!=", "="})
 
-- Returned result is a table (key/value pairs) - display all entries
print("Key\tValue")
print("---\t-----")
for k, v in pairs(multisplit) do
print(k, v)
end</syntaxhighlight>
{{Out}}
<pre>Key Value
--- -----
1 a
2
3 b
4
5 c</pre>
 
=={{header|M2000 Interpreter}}==
Code from BBC BASIC with little changes to fit in M2000.
 
<syntaxhighlight lang="m2000 interpreter">
Module CheckIt {
DIM sep$()
sep$() = ("==", "!=", "=")
PRINT "String splits into:"
FNmultisplit("a!===b=!=c", sep$(), FALSE)
PRINT "For extra credit:"
FNmultisplit("a!===b=!=c", sep$(), TRUE)
END
SUB FNmultisplit(s$, d$(), info%)
LOCAL d%, i%, j%, m%, p%, o$
p% = 1
REPEAT {
m% = LEN(s$)
FOR i% = 0 TO DIMENSION(d$(),1)-1
d% = INSTR(s$, d$(i%), p%)
IF d% THEN IF d% < m% THEN m% = d% : j% = i%
NEXT I%
IF m% < LEN(s$) THEN {
o$ += """" + MID$(s$, p%, m%-p%) + """"
IF info% THEN {o$ += " (" + d$(j%) + ") "} ELSE o$ += ", "
p% = m% + LEN(d$(j%))
}
} UNTIL m% = LEN(s$)
PRINT o$ + """" + MID$(s$, p%) + """"
END SUB
}
CheckIt
</syntaxhighlight>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
Just use the built-in function "StringSplit":
<syntaxhighlight lang="mathematica">StringSplit["a!===b=!=c", {"==", "!=", "="}]</syntaxhighlight>
{{Out}}
<pre>{a,,b,,c}</pre>
 
=={{header|MiniScript}}==
<syntaxhighlight lang="miniscript">parseSep = function(s, pats)
result = []
startPos = 0
pos = 0
while pos < s.len
for pat in pats
if s[pos : pos+pat.len] != pat then continue
result.push s[startPos : pos]
result.push "{" + pat + "}"
startPos = pos + pat.len
pos = startPos - 1
break
end for
pos = pos + 1
end while
return result
end function
 
print parseSep("a!===b=!=c", ["==", "!=", "="])</syntaxhighlight>
{{Out}}
<pre>["a", "{!=}", "", "{==}", "b", "{=}", "", "{!=}"]</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import strutils
iterator tokenize(text: string; sep: openArray[string]): tuple[token: string, isSep: bool] =
var i, lastMatch = 0
while i < text.len:
for j, s in sep:
if text[i..text.high].startsWith s:
if i > lastMatch: yield (text[lastMatch ..< i], false)
yield (s, true)
lastMatch = i + s.len
i += s.high
break
inc i
if i > lastMatch: yield (text[lastMatch ..< i], false)
for token, isSep in "a!===b=!=c".tokenize(["==", "!=", "="]):
if isSep: stdout.write '{',token,'}'
else: stdout.write token
echo ""</syntaxhighlight>
 
{{out}}
<pre>a{!=}{==}b{=}{!=}c</pre>
 
=={{header|Perl}}==
 
<syntaxhighlight lang="perl">sub multisplit {
my ($sep, $string, %opt) = @_ ;
$sep = join '|', map quotemeta($_), @$sep;
$sep = "($sep)" if $opt{keep_separators};
split /$sep/, $string, -1;
}
 
print "'$_' " for multisplit ['==','!=','='], "a!===b=!=c";
print "\n";
print "'$_' " for multisplit ['==','!=','='], "a!===b=!=c", keep_separators => 1;
print "\n";</syntaxhighlight>
 
{{Out}}
<pre>
'a' '' 'b' '' 'c'
'a' '!=' '' '==' 'b' '=' '' '!=' 'c'
</pre>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">multisplit</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">delims</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">kdx</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">kmin</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">delims</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">ki</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">match</span><span style="color: #0000FF;">(</span><span style="color: #000000;">delims</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">text</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ki</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">kmin</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ki</span><span style="color: #0000FF;"><</span><span style="color: #000000;">kmin</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">kmin</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ki</span>
<span style="color: #000000;">kdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">token</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">..</span><span style="color: #000000;">kmin</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span>
<span style="color: #000000;">delim</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">kmin</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">?</span><span style="color: #008000;">""</span><span style="color: #0000FF;">:</span><span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">", delimiter (%s) at %d"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">delims</span><span style="color: #0000FF;">[</span><span style="color: #000000;">kdx</span><span style="color: #0000FF;">],</span><span style="color: #000000;">kmin</span><span style="color: #0000FF;">}))</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Token: [%s] at %d%s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">token</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k</span><span style="color: #0000FF;">,</span><span style="color: #000000;">delim</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">kmin</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">kmin</span><span style="color: #0000FF;">+</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">delims</span><span style="color: #0000FF;">[</span><span style="color: #000000;">kdx</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #000000;">multisplit</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"a!===b=!=c"</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"=="</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"!="</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"="</span><span style="color: #0000FF;">})</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Token: [a] at 1, delimiter (!=) at 2
Token: [] at 4, delimiter (==) at 4
Token: [b] at 6, delimiter (=) at 7
Token: [] at 8, delimiter (!=) at 8
Token: [c] at 10
</pre>
 
=={{header|PicoLisp}}==
<syntaxhighlight lang="picolisp">(de multisplit (Str Sep)
(setq Sep (mapcar chop Sep))
(make
(for (S (chop Str) S)
(let L
(make
(loop
(T (find head Sep (circ S))
(link
(list
(- (length Str) (length S))
(pack (cut (length @) 'S)) ) ) )
(link (pop 'S))
(NIL S (link NIL)) ) )
(link (pack (cdr (rot L))))
(and (car L) (link @)) ) ) ) )
 
(println (multisplit "a!===b=!=c" '("==" "!=" "=")))
(println (multisplit "a!===b=!=c" '("=" "!=" "==")))</syntaxhighlight>
{{out}}
<pre>("a" (1 "!=") NIL (3 "==") "b" (6 "=") NIL (7 "!=") "c")
("a" (1 "!=") NIL (3 "=") NIL (4 "=") "b" (6 "=") NIL (7 "!=") "c")</pre>
 
=={{header|Pike}}==
<syntaxhighlight lang="pike">string input = "a!===b=!=c";
array sep = ({"==", "!=", "=" });
 
array result = replace(input, sep, `+("\0", sep[*], "\0"))/"\0";
result;
Result: ({ "a", "!=", "", "==", "b", "=", "", "!=", "c" })
 
int pos = 0;
foreach(result; int index; string data)
{
if ((<"==", "!=", "=">)[data])
result[index] = ({ data, pos });
pos+=sizeof(data);
}
 
result;
Result: ({"a", ({"!=", 1}), "", ({"==", 3}), "b", ({"=", 6}), "", ({"!=", 7}), "c"})</syntaxhighlight>
 
=={{header|PowerShell}}==
<syntaxhighlight lang="powershell">
$string = "a!===b=!=c"
$separators = [regex]"(==|!=|=)"
 
$matchInfo = $separators.Matches($string) |
Select-Object -Property Index, Value |
Group-Object -Property Value |
Select-Object -Property @{Name="Separator"; Expression={$_.Name}},
Count,
@{Name="Position" ; Expression={$_.Group.Index}}
 
$matchInfo
</syntaxhighlight>
{{Out}}
<pre>
Separator Count Position
--------- ----- --------
!= 2 {1, 7}
== 1 3
= 1 6
</pre>
 
=={{header|Prolog}}==
Works with SWI-Prolog.
<syntaxhighlight lang="prolog">multisplit(_LSep, '') -->
{!},
[].
 
multisplit(LSep, T) -->
{next_sep(LSep, T, [], Token, Sep, T1)},
( {Token \= '' },[Token], {!}; []),
( {Sep \= '' },[Sep], {!}; []),
multisplit(LSep, T1).
 
next_sep([], T, Lst, Token, Sep, T1) :-
% if we can't find any separator, the game is over
( Lst = [] ->
Token = T, Sep = '', T1 = '';
 
% we sort the list to get nearest longest separator
predsort(my_sort, Lst, [(_,_, Sep)|_]),
atomic_list_concat([Token|_], Sep, T),
atom_concat(Token, Sep, Tmp),
atom_concat(Tmp, T1, T)).
 
next_sep([HSep|TSep], T, Lst, Token, Sep, T1) :-
sub_atom(T, Before, Len, _, HSep),
next_sep(TSep, T, [(Before, Len,HSep) | Lst], Token, Sep, T1).
 
next_sep([_HSep|TSep], T, Lst, Token, Sep, T1) :-
next_sep(TSep, T, Lst, Token, Sep, T1).
 
 
my_sort(<, (N1, _, _), (N2, _, _)) :-
N1 < N2.
 
my_sort(>, (N1, _, _), (N2, _, _)) :-
N1 > N2.
 
my_sort(>, (N, N1, _), (N, N2, _)) :-
N1 < N2.
 
my_sort(<, (N, N1, _), (N, N2, _)) :-
N1 > N2.
</syntaxhighlight>
{{out}}
<pre>?- multisplit(['==', '!=', '='], 'ax!===b=!=c', Lst, []).
Lst = [ax,'!=',==,b,=,'!=',c] .
</pre>
 
=={{header|Python}}==
===Procedural===
===Using Regular expressions===
Using regular expressions:
<lang python>>>> import re
<syntaxhighlight lang="python">>>> import re
>>> def ms2(txt="a!===b=!=c", sep=["==", "!=", "="]):
if not txt or not sep:
Line 63 ⟶ 1,748:
['a', (1, 1), '', (0, 3), 'b', (2, 6), '', (1, 7), 'c']
>>> ms2(txt="a!===b=!=c", sep=["=", "!=", "=="])
['a', (1, 1), '', (0, 3), '', (0, 4), 'b', (0, 6), '', (1, 7), 'c']</langsyntaxhighlight>
 
===Not using RE's===regular expressions:
'''Inspired by C-version'''
<lang python>>>> def ms(txt="a!===b=!=c", sep=["==", "!=", "="]):
<syntaxhighlight lang="python">def multisplit(text, sep):
size = [len(s) for s in sep]
ans, pos0 lastmatch = [],i = 0
matches = []
def getfinds():
while i < len(text):
return [(-txt.find(s, pos0), -sepnum, size[sepnum])
for sepnumj, s in enumerate(sep):
if text[i:].startswith(s):
if s in txt[pos0:]]
if i > lastmatch:
matches.append(text[lastmatch:i])
matches.append((j, i)) # Replace the string containing the matched separator with a tuple of which separator and where in the string the match occured
lastmatch = i + len(s)
i += len(s)
break
else:
i += 1
if i > lastmatch:
matches.append(text[lastmatch:i])
return matches
 
>>> multisplit('a!===b=!=c', ['==', '!=', '='])
finds = getfinds()
['a', (1, 1), (0, 3), 'b', (2, 6), (1, 7), 'c']
while finds:
>>> multisplit('a!===b=!=c', ['!=', '==', '='])
pos, snum, sz = max(finds)
['a', (0, 1), (1, 3), 'b', (2, 6), (0, 7), 'c']
pos, snum = -pos, -snum
</syntaxhighlight>
ans += [ txt[pos0:pos], [snum, pos] ]
pos0 = pos+sz
finds = getfinds()
if txt[pos0:]: ans += [ txt[pos0:] ]
return ans
 
>>> ms()
['a', [1, 1], '', [0, 3], 'b', [2, 6], '', [1, 7], 'c']
>>> ms(txt="a!===b=!=c", sep=["=", "!=", "=="])
['a', [1, 1], '', [0, 3], '', [0, 4], 'b', [0, 6], '', [1, 7], 'c']</lang>
Small inaccuracy in the version above: ms("", ["="]) outputs [] instead of [<nowiki>''</nowiki>].
 
'''Alternative version'''
<langsyntaxhighlight lang="python">def min_pos(List):
return List.index(min(List))
 
Line 103 ⟶ 1,789:
DeltaPos = len(Sub)
Pos = Start
while 1True:
Pos = S.find(Sub, Pos, End)
if Pos == -1:
Line 116 ⟶ 1,802:
SepNumList = []
ListCount = 0
for i, Sep in range(lenenumerate(SepList)):
Sep = SepList[i]
SepPosList = find_all(S, Sep, 0, SLen, IsOverlapped = 1)
if SepPosList != []:
Line 131 ⟶ 1,816:
MinPosPos = min_pos(MinPosList)
Res = []
while 1True:
Res.append( S[SepEnd : MinPosList[MinPosPos]] )
Res.append([SepNumList[MinPosPos], MinPosList[MinPosPos]])
SepEnd = MinPosList[MinPosPos] + len(SepList[SepNumList[MinPosPos]])
while 1True:
MinPosPos = min_pos(MinPosList)
if MinPosList[MinPosPos] < SepEnd:
del( SepPosListList[MinPosPos][0])
if len(SepPosListList[MinPosPos]) == 0:
del( SepPosListList[MinPosPos])
del( MinPosList[MinPosPos])
del( SepNumList[MinPosPos])
ListCount -= 1
if ListCount == 0:
Line 158 ⟶ 1,843:
S = "a!===b=!=c"
multisplit(S, ["==", "!=", "="]) # output: ['a', [1, 1], '', [0, 3], 'b', [2, 6], '', [1, 7], 'c']
multisplit(S, ["=", "!=", "=="]) # output: ['a', [1, 1], '', [0, 3], '', [0, 4], 'b', [0, 6], '', [1, 7], 'c']</syntaxhighlight>
 
</lang>
===Functional===
In terms of a fold (reduce), without use of regular expressions:
{{Works with|Python|3.7}}
<syntaxhighlight lang="python">'''Multisplit'''
 
 
from functools import reduce
 
 
# multiSplit :: [String] -> String -> [(String, String, Int)]
def multiSplit(separators):
'''List of triples:
[(token, separator, start index of separator].
'''
def go(s):
def f(tokensPartsOffset, ic):
tokens, parts, offset = tokensPartsOffset
i, c = ic
inDelim = offset > i
return maybe(
(
tokens if inDelim
else c + tokens, parts, offset
)
)(
lambda x: (
'',
[(tokens, x, i)] + parts,
i + len(x)
)
)(
None if inDelim else find(
s[i:].startswith
)(separators)
)
ts, ps, _ = reduce(f, enumerate(s), ('', [], 0))
return list(reversed(ps)) + [(ts, '', len(s))]
return go
 
 
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''String split on three successive separators.'''
print(
multiSplit(['==', '!=', '='])(
'a!===b=!=c'
)
)
 
 
# ------------------ GENERIC FUNCTIONS -------------------
 
# find :: (a -> Bool) -> [a] -> (a | None)
def find(p):
'''Just the first element in the list that matches p,
or None if no elements match.
'''
def go(xs):
try:
return next(x for x in xs if p(x))
except StopIteration:
return None
return go
 
 
# maybe :: b -> (a -> b) -> (a | None) -> b
def maybe(v):
'''Either the default value v, if m is None,
or the application of f to x.
'''
return lambda f: lambda m: v if (
None is m
) else f(m)
 
 
# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
<pre>[('a', '!=', 1), ('', '==', 3), ('b', '=', 6), ('', '!=', 7), ('c', '', 10)]</pre>
 
=={{header|Racket}}==
 
<syntaxhighlight lang="racket">
#lang racket
(regexp-match* #rx"==|!=|=" "a!===b=!=c" #:gap-select? #t #:match-select values)
;; => '("a" ("!=") "" ("==") "b" ("=") "" ("!=") "c")
</syntaxhighlight>
 
=={{header|Raku}}==
(formerly Perl 6)
<syntaxhighlight lang="raku" line>sub multisplit($str, @seps) { $str.split: / ||@seps /, :v }
 
my @chunks = multisplit 'a!===b=!=c==d', < == != = >;
 
# Print the strings.
say @chunks».Str.raku;
 
# Print the positions of the separators.
for grep Match, @chunks -> $s {
say "{$s.fmt: '%2s'} from {$s.from.fmt: '%2d'} to {$s.to.fmt: '%2d'}";
}</syntaxhighlight>
{{out}}
<pre>("a", "!=", "", "==", "b", "=", "", "!=", "c", "==", "d")
!= from 1 to 3
== from 3 to 5
= from 6 to 7
!= from 7 to 9
== from 10 to 12</pre>
Using the array <tt>@seps</tt> in a pattern automatically does alternation.
By default this would do longest-term matching (that is, <tt>|</tt> semantics), but we can force it to do left-to-right matching by embedding the array in a short-circuit alternation (that is, <tt>||</tt> semantics).
As it happens, with the task's specified list of separators, it doesn't make any difference.
<p>
Raku automatically returns Match objects that will stringify to the matched pattern, but can also be interrogated for their match positions, as illustrated above by post-processing the results two different ways.
 
=={{header|REXX}}==
<syntaxhighlight lang="rexx">/*REXX program splits a (character) string based on different separator delimiters.*/
parse arg $ /*obtain optional string from the C.L. */
if $='' then $= "a!===b=!=c" /*None specified? Then use the default*/
say 'old string:' $ /*display the old string to the screen.*/
null= '0'x /*null char. It can be most anything.*/
seps= '== != =' /*list of separator strings to be used.*/
/* [↓] process the tokens in SEPS. */
do j=1 for words(seps) /*parse the string with all the seps. */
sep=word(seps,j) /*pick a separator to use in below code*/
/* [↓] process characters in the sep.*/
do k=1 for length(sep) /*parse for various separator versions.*/
sep=strip(insert(null, sep, k), , null) /*allow imbedded "nulls" in separator, */
$=changestr(sep, $, null) /* ··· but not trailing "nulls". */
/* [↓] process strings in the input. */
do until $==old; old=$ /*keep changing until no more changes. */
$=changestr(null || null, $, null) /*reduce replicated "nulls" in string. */
end /*until*/
/* [↓] use BIF or external program.*/
sep=changestr(null, sep, '') /*remove true nulls from the separator.*/
end /*k*/
end /*j*/
 
showNull= ' {} ' /*just one more thing, display the ··· */
$=changestr(null, $, showNull) /* ··· showing of "null" chars. */
say 'new string:' $ /*now, display the new string to term. */
/*stick a fork in it, we're all done. */</syntaxhighlight>
Some older REXXes don't have a &nbsp; '''changestr''' &nbsp; BIF, so one is included here &nbsp; ──► &nbsp; [[CHANGESTR.REX]].
<br><br>'''output''' &nbsp; when using the default input:
<pre>
old string=a!===b=!=c
new string=a {} b {} c
</pre>
 
=={{header|Ring}}==
<syntaxhighlight lang="ring">
# Project : Multisplit
 
str = "a!===b=!=c"
sep = "=== != =! b =!="
sep = str2list(substr(sep, " ", nl))
for n = 1 to len(sep)
pos = substr(str, sep[n])
see "" + n + ": " + substr(str, 1, pos-1) + " Sep By: " + sep[n] + nl
next
</syntaxhighlight>
Output:
<pre>
1: a! Sep By: ===
2: a Sep By: !=
3: a!===b Sep By: =!
4: a!=== Sep By: b
5: a!===b Sep By: =!=
</pre>
 
=={{header|Ruby}}==
The simple method, using a regular expression to split the text.
 
<syntaxhighlight lang="ruby">text = 'a!===b=!=c'
separators = ['==', '!=', '=']
 
def multisplit_simple(text, separators)
text.split(Regexp.union(separators))
end
 
p multisplit_simple(text, separators) # => ["a", "", "b", "", "c"]
</syntaxhighlight>
 
The version that also returns the information about the separations.
 
<syntaxhighlight lang="ruby">def multisplit(text, separators)
sep_regex = Regexp.union(separators)
separator_info = []
pieces = []
i = prev = 0
while i = text.index(sep_regex, i)
separator = Regexp.last_match(0)
pieces << text[prev .. i-1]
separator_info << [separator, i]
i = i + separator.length
prev = i
end
pieces << text[prev .. -1]
[pieces, separator_info]
end
 
p multisplit(text, separators)
# => [["a", "", "b", "", "c"], [["!=", 1], ["==", 3], ["=", 6], ["!=", 7]]]</syntaxhighlight>
 
Also demonstrating a method to rejoin the string given the separator information.
 
<syntaxhighlight lang="ruby">def multisplit_rejoin(info)
str = info[0].zip(info[1])[0..-2].inject("") {|str, (piece, (sep, idx))| str << piece << sep}
str << info[0].last
end
 
p multisplit_rejoin(multisplit(text, separators)) == text
# => true</syntaxhighlight>
 
=={{header|Run BASIC}}==
<syntaxhighlight lang="runbasic">str$ = "a!===b=!=c"
sep$ = "=== != =! b =!="
 
while word$(sep$,i+1," ") <> ""
i = i + 1
theSep$ = word$(sep$,i," ")
split$ = word$(str$,1,theSep$)
print i;" ";split$;" Sep By: ";theSep$
wend</syntaxhighlight>
{{out}}
<pre>1 a! Sep By: ===
2 a Sep By: !=
3 a!===b Sep By: =!
4 a!=== Sep By: b
5 a!===b Sep By: =!=</pre>
 
=={{header|Scala}}==
<syntaxhighlight lang="scala">import scala.annotation.tailrec
def multiSplit(str:String, sep:Seq[String])={
def findSep(index:Int)=sep find (str startsWith (_, index))
@tailrec def nextSep(index:Int):(Int,Int)=
if(index>str.size) (index, 0) else findSep(index) match {
case Some(sep) => (index, sep.size)
case _ => nextSep(index + 1)
}
def getParts(start:Int, pos:(Int,Int)):List[String]={
val part=str slice (start, pos._1)
if(pos._2==0) List(part) else part :: getParts(pos._1+pos._2, nextSep(pos._1+pos._2))
}
getParts(0, nextSep(0))
}
 
println(multiSplit("a!===b=!=c", Seq("!=", "==", "=")))</syntaxhighlight>
{{out}}
<pre>List(a, , b, , c)</pre>
 
=={{header|Scheme}}==
{{works with|Gauche Scheme}}
<syntaxhighlight lang="scheme">(use srfi-13)
(use srfi-42)
 
(define (shatter separators the-string)
(let loop ((str the-string) (tmp ""))
(if (string=? "" str)
(list tmp)
(if-let1 sep (find (^s (string-prefix? s str)) separators)
(cons* tmp sep
(loop (string-drop str (string-length sep)) ""))
(loop (string-drop str 1) (string-append tmp (string-take str 1)))))))
 
(define (glean shards)
(list-ec (: x (index i) shards)
(if (even? i)) x))</syntaxhighlight>
<b>Testing:</b>
<pre>
(glean (shatter '("==" "!=" "=") "a!===b=!=c"))
("a" "" "b" "" "c")
 
(shatter '("==" "!=" "=") "a!===b=!=c")
("a" "!=" "" "==" "b" "=" "" "!=" "c")
</pre>
 
=={{header|SenseTalk}}==
First approach, using line delimiters. Lines are delimited by an array of separator strings, normally [CRLF, LF, CR, lineSeparator(0x2028), paragraphSeparator(0x2029)]. Supplying an alternate set of delimiters lets us split a string by a different (ordered) set of strings:
 
<syntaxhighlight lang="sensetalk">set source to "a!===b=!=c"
set separators to ["==", "!=", "="]
 
put each line delimited by separators of source</syntaxhighlight>
Output:
<syntaxhighlight lang="sensetalk">(a,,b,,c)</syntaxhighlight>
 
Second approach, using a pattern. SenseTalk's pattern language lets us define a pattern (a regex) which can then be used to split the string and also to display the actual separators that were found.
<syntaxhighlight lang="sensetalk">set source to "a!===b=!=c"
set separatorPattern to <"==" or "!=" or "=">
 
put source split by separatorPattern
 
put each occurrence of separatorPattern in source
</syntaxhighlight>
Output:
<syntaxhighlight lang="sensetalk">(a,,b,,c)
(!=,==,=,!=)</syntaxhighlight>
 
=={{header|Sidef}}==
<syntaxhighlight lang="ruby">func multisplit(sep, str, keep_sep=false) {
sep = sep.map{.escape}.join('|');
var re = Regex.new(keep_sep ? "(#{sep})" : sep);
str.split(re, -1);
}
 
[false, true].each { |bool|
say multisplit(%w(== != =), 'a!===b=!=c', keep_sep: bool);
}</syntaxhighlight>
{{out}}
<pre>
["a", "", "b", "", "c"]
["a", "!=", "", "==", "b", "=", "", "!=", "c"]
</pre>
 
=={{header|Swift}}==
 
Swift strings are purposefully not index by integers to avoid confusion and performance traps when dealing with unicode. As such the indexes returned by this method are not very helpful to a human reader, but can be used to manipulate the original string.
 
{{trans|Python}}
 
<syntaxhighlight lang="swift">extension String {
func multiSplit(on seps: [String]) -> ([Substring], [(String, (start: String.Index, end: String.Index))]) {
var matches = [Substring]()
var matched = [(String, (String.Index, String.Index))]()
var i = startIndex
var lastMatch = startIndex
 
main: while i != endIndex {
for sep in seps where self[i...].hasPrefix(sep) {
if i > lastMatch {
matches.append(self[lastMatch..<i])
} else {
matches.append("")
}
 
lastMatch = index(i, offsetBy: sep.count)
matched.append((sep, (i, lastMatch)))
i = lastMatch
 
continue main
}
 
i = index(i, offsetBy: 1)
}
 
if i > lastMatch {
matches.append(self[lastMatch..<i])
}
 
return (matches, matched)
}
}
 
let (matches, matchedSeps) = "a!===b=!=c".multiSplit(on: ["==", "!=", "="])
 
print(matches, matchedSeps.map({ $0.0 }))</syntaxhighlight>
 
 
{{out}}
 
<pre>["a", "", "b", "", "c"] ["!=", "==", "=", "!="]</pre>
 
=={{header|Tcl}}==
This simple version does not retain information about what the separators were:
<syntaxhighlight lang="tcl">proc simplemultisplit {text sep} {
set map {}; foreach s $sep {lappend map $s "\uffff"}
return [split [string map $map $text] "\uffff"]
}
puts [simplemultisplit "a!===b=!=c" {"==" "!=" "="}]</syntaxhighlight>
{{out}}
<pre>a {} b {} c</pre>
However, to keep the match information a more sophisticated technique is best. Note that the most natural model of result here
is to return the split substrings as a separate list
to the match information (because the two collections of information
are of different lengths).
<syntaxhighlight lang="tcl">proc multisplit {text sep} {
foreach s $sep {lappend sr [regsub -all {\W} $s {\\&}]}
set sepRE [join $sr "|"]
set pieces {}
set match {}
set start 0
while {[regexp -indices -start $start -- $sepRE $text found]} {
lassign $found x y
lappend pieces [string range $text $start [expr {$x-1}]]
lappend match [lsearch -exact $sep [string range $text {*}$found]] $x
set start [expr {$y + 1}]
}
return [list [lappend pieces [string range $text $start end]] $match]
}</syntaxhighlight>
Demonstration code:
<syntaxhighlight lang="tcl">set input "a!===b=!=c"
set matchers {"==" "!=" "="}
lassign [multisplit $input $matchers] substrings matchinfo
puts $substrings
puts $matchinfo</syntaxhighlight>
{{out}}
<pre>
a {} b {} c
1 1 0 3 2 6 1 7
</pre>
 
=={{header|TXR}}==
 
===Using text-extraction pattern language===
 
Here, the separators are embedded into the syntax rather than appearing as a datum. Nevertheless, this illustrates how to do that small tokenizing task with various separators.
 
The clauses of <code>choose</code> are applied in parallel, and all potentially match at the current position in the text.
However <code>:shortest tok</code> means that only that clause survives (gets to propagate its bindings and position advancement) which minimizes the length of the string which is bound to the <code>tok</code> variable.
The <code>:gap 0</code> makes the horizontal collect repetitions strictly adjacent. This means that <code>coll</code> will quit when faced with a nonmatching suffix portion of the data rather than scan forward (no gap allowed!). This creates an opportunity for the <code>tail</code> variable to grab the suffix which remains, which may be an empty string.
 
<syntaxhighlight lang="txr">@(next :args)
@(coll :gap 0)@(choose :shortest tok)@\
@tok@{sep /==/}@\
@(or)@\
@tok@{sep /!=/}@\
@(or)@\
@tok@{sep /=/}@\
@(end)@(end)@tail
@(output)
@(rep)"@tok" {@sep} @(end)"@tail"
@(end)</syntaxhighlight>
 
Runs:
 
<pre>$ ./txr multisplit.txr 'a!===b=!=c'
"a" {!=} "" {==} "b" {=} "" {!=} "c"
$ ./txr multisplit.txr 'a!===!==!=!==b'
"a" {!=} "" {==} "" {!=} "" {=} "" {!=} "" {!=} "" {=} "b"
$ ./txr multisplit.txr ''
""
$ ./txr multisplit.txr 'a'
"a"
$ ./txr multisplit.txr 'a='
"a" {=} ""
$ ./txr multisplit.txr '='
"" {=} ""
$ ./txr multisplit.txr '=='
"" {==} ""
$ ./txr multisplit.txr '==='
"" {==} "" {=} ""</pre>
 
===Using the <code>tok-str</code> function===
 
{{trans|Racket}}
 
<syntaxhighlight lang="sh">$ txr -p '(tok-str "a!===b=!=c" #/==|!=|=/ t)'
("a" "!=" "" "==" "b" "=" "" "!=" "c")</syntaxhighlight>
 
Here the third boolean argument means "keep the material between the tokens", which in the Racket version seems to be requested by the argument <code>#:gap-select? #:t</code>.
 
=={{header|UNIX Shell}}==
{{works with|bash}}
<syntaxhighlight lang="bash">multisplit() {
local str=$1
shift
local regex=$( IFS='|'; echo "$*" )
local sep
while [[ $str =~ $regex ]]; do
sep=${BASH_REMATCH[0]}
words+=( "${str%%${sep}*}" )
seps+=( "$sep" )
str=${str#*$sep}
done
words+=( "$str" )
}
 
words=() seps=()
 
original="a!===b=!=c"
recreated=""
 
multisplit "$original" "==" "!=" "="
 
for ((i=0; i<${#words[@]}; i++)); do
printf 'w:"%s"\ts:"%s"\n' "${words[i]}" "${seps[i]}"
recreated+="${words[i]}${seps[i]}"
done
 
if [[ $original == $recreated ]]; then
echo "successfully able to recreate original string"
fi</syntaxhighlight>
 
{{out}}
<pre>w:"a" s:"!="
w:"" s:"=="
w:"b" s:"="
w:"" s:"!="
w:"c" s:""
successfully able to recreate original string</pre>
 
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
Function multisplit(s,sep)
arr_sep = Split(sep,"|")
For i = 0 To UBound(arr_sep)
arr_s = Split(s,arr_sep(i))
s = Join(arr_s,",")
Next
multisplit = s
End Function
Function multisplit_extra(s,sep)
Set dict_sep = CreateObject("Scripting.Dictionary")
arr_sep = Split(sep,"|")
For i = 0 To UBound(arr_sep)
dict_sep.Add i,"(" & arr_sep(i) & ")"
arr_s = Split(s,arr_sep(i))
s = Join(arr_s,i)
Next
For Each key In dict_sep.Keys
s = Replace(s,key,dict_sep.Item(key))
Next
multisplit_extra = s
End Function
WScript.StdOut.Write "Standard: " & multisplit("a!===b=!=c","!=|==|=")
WScript.StdOut.WriteLine
WScript.StdOut.Write "Extra Credit: " & multisplit_extra("a!===b=!=c","!=|==|=")
WScript.StdOut.WriteLine</syntaxhighlight>
{{out}}
<pre>
Standard: a,,b,,c
Extra Credit: a(!=)(==)b(=)(!=)c
</pre>
 
=={{header|V (Vlang)}}==
Without using additional libraries or regular expressions:
<syntaxhighlight lang="v (vlang)">fn main() {
str := "a!===b=!=c"
sep := ["==","!=","="]
println(ms(str, sep))
}
 
fn ms(txt string, sep []string) (map[int]string, []string, []string) {
mut ans, mut extra := []string{}, []string{}
mut place := map[int]string{}
mut temp :=''
mut vlen := 0
 
for slen in sep {if slen.len > vlen {vlen = slen.len}}
for cidx, cval in txt {
temp += cval.ascii_str()
for value in sep {
if temp.contains(value) && temp.len >= vlen {
place[cidx] = value
temp =''
}
}
}
 
for tidx, tval in txt {
for pkey, pval in place {
if tidx == pkey {
ans << ''
extra << '(' + pval + ')'
}
}
if sep.any(it.contains(tval.ascii_str())) == false {
ans << tval.ascii_str()
extra << tval.ascii_str()
}
}
println('Ending indices: $place')
println('Answer: $ans')
println('Extra: $extra')
return place, ans, extra
}</syntaxhighlight>
{{out}}
<pre>
Ending indices: {2: '!=', 4: '==', 6: '=', 8: '!='}
Answer: ['a', '', '', 'b', '', '', 'c']
Extra: ['a', '(!=)', '(==)', 'b', '(=)', '(!=)', 'c']
({2: '!=', 4: '==', 6: '=', 8: '!='}, ['a', '', '', 'b', '', '', 'c'], ['a', '(!=)', '(==)', 'b', '(=)', '(!=)', 'c'])
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-pattern}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./pattern" for Pattern
import "./fmt" for Fmt
 
var input = "a!===b=!=c"
var p = Pattern.new("[/=/=|!/=|/=]")
var separators = p.findAll(input)
System.print("The separators matched and their starting/ending indices are:")
for (sep in separators) {
System.print(" %(Fmt.s(-4, Fmt.q(sep.text))) between %(sep.span)")
}
var parts = p.splitAll(input)
System.print("\nThe substrings between the separators are:")
System.print(parts.map { |p| (p != "") ? Fmt.q(p) : "empty string" }.toList)</syntaxhighlight>
 
{{out}}
<pre>
The separators matched and their starting/ending indices are:
"!=" between [1, 2]
"==" between [3, 4]
"=" between [6, 6]
"!=" between [7, 8]
 
The substrings between the separators are:
["a", empty string, "b", empty string, "c"]
</pre>
 
=={{header|XPL0}}==
<syntaxhighlight lang "XPL0">include xpllib; \for StrLen, StrNCmp, and Print
 
proc MultiSplit(Str, Seps, N);
char Str; int Seps, N;
int S, Ch, SepLen;
[while Str(0) # 0 do
[for S:= 0 to N-1 do
[SepLen:= StrLen(Seps(S));
if StrNCmp(Str, Seps(S), SepLen) = 0 then
[Print(" (%s) ", Seps(S));
Str:= Str + SepLen;
S:= 100;
];
];
if S < 100 then
[Ch:= Str(0); Str:= Str+1;
if Ch # 0 then ChOut(0, Ch);
];
];
];
 
MultiSplit("a!===b=!=c", ["==", "!=", "="], 3)</syntaxhighlight>
{{out}}
<pre>
a (!=) (==) b (=) (!=) c</pre>
 
=={{header|Yabasic}}==
<syntaxhighlight lang="yabasic">t$ = "a!===b=!=c"
s$ = "==,!=,="
 
dim n$(1)
 
n = token(s$, n$(), ",")
dim p(n)
 
do
l = len(t$)
j = 0
for i = 1 to n
p(i) = instr(t$, n$(i))
if p(i) and p(i) < l then l = p(i) : j = i end if
next
if not j print t$ : break
print left$(t$, l - 1), " with separator ", n$(j)
t$ = right$(t$, len(t$) - (l + len(n$(j))) + 1)
loop</syntaxhighlight>
 
=={{header|zkl}}==
{{trans|Python}}
<syntaxhighlight lang="zkl">fcn multisplit(text, sep){
lastmatch := i := 0; matches := List();
while(i < text.len()){
foreach j,s in ([0..].zip(sep)){
if(i == text.find(s,i)){
if(i > lastmatch) matches.append(text[lastmatch,i-lastmatch]);
matches.append(T(j,i)); # Replace the string containing the matched separator with a tuple of which separator and where in the string the match occured
lastmatch = i + s.len();
i += s.len()-1;
break;
}
}
i += 1;
}
if(i > lastmatch) matches.append(text[lastmatch,i-lastmatch]);
return(matches);
}</syntaxhighlight>
<syntaxhighlight lang="zkl">multisplit("a!===b=!=c", T("==", "!=", "=")).println();
multisplit("a!===b=!=c", T("!=", "==", "=")).println();</syntaxhighlight>
{{out}}
<pre>
L("a",L(1,1),L(0,3),"b",L(2,6),L(1,7),"c")
L("a",L(0,1),L(1,3),"b",L(2,6),L(0,7),"c")
</pre>
2,069

edits