Split a character string based on change of character

From Rosetta Code
Task
Split a character string based on change of character
You are encouraged to solve this task according to the task description, using any language you may know.


Task

Split a (character) string into comma (plus a blank) delimited strings based on a change of character   (left to right).

Show the output here   (use the 1st example below).


Blanks should be treated as any other character   (except they are problematic to display clearly).   The same applies to commas.


For instance, the string:

 gHHH5YY++///\ 

should be split and show:

 g, HHH, 5, YY, ++, ///, \ 



ALGOL 68[edit]

Works with: ALGOL 68G version Any - tested with release 2.8.3.win32
BEGIN
# returns s with ", " added between each change of character #
PROC split on characters = ( STRING s )STRING:
IF s = "" THEN
# empty string #
""
ELSE
# allow for 3 times as many characters as in the string #
# this would handle a string of unique characters #
[ 3 * ( ( UPB s - LWB s ) + 1 ) ]CHAR result;
INT r pos := LWB result;
INT s pos := LWB s;
CHAR s char := s[ LWB s ];
FOR s pos FROM LWB s TO UPB s DO
IF s char /= s[ s pos ] THEN
# change of character - insert ", " #
result[ r pos ] := ",";
result[ r pos + 1 ] := " ";
r pos +:= 2;
s char := s[ s pos ]
FI;
result[ r pos ] := s[ s pos ];
r pos +:= 1
OD;
# return the used portion of the result #
result[ 1 : r pos - 1 ]
FI ; # split on characters #
 
print( ( split on characters( "gHHH5YY++///\" ), newline ) )
END
Output:
g, HHH, 5, YY, ++, ///, \

AppleScript[edit]

Translation of: JavaScript
intercalate(", ", ¬
map(curry(intercalate)'s |λ|(""), ¬
group("gHHH5YY++///\\")))
 
--> "g, HHH, 5, YY, ++, ///, \\"
 
 
-- GENERIC FUNCTIONS ----------------------------------------------------------
-- curry :: (Script|Handler) -> Script
on curry(f)
script
on |λ|(a)
script
on |λ|(b)
|λ|(a, b) of mReturn(f)
end |λ|
end script
end |λ|
end script
end curry
 
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
 
-- group :: Eq a => [a] -> [[a]]
on group(xs)
script eq
on |λ|(a, b)
a = b
end |λ|
end script
 
groupBy(eq, xs)
end group
 
-- groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
on groupBy(f, xs)
set mf to mReturn(f)
 
script enGroup
on |λ|(a, x)
if length of (active of a) > 0 then
set h to item 1 of active of a
else
set h to missing value
end if
 
if h is not missing value and mf's |λ|(h, x) then
{active:(active of a) & x, sofar:sofar of a}
else
{active:{x}, sofar:(sofar of a) & {active of a}}
end if
end |λ|
end script
 
if length of xs > 0 then
tell foldl(enGroup, {active:{item 1 of xs}, sofar:{}}, tail(xs))
if length of (its active) > 0 then
its sofar & its active
else
{}
end if
end tell
else
{}
end if
end groupBy
 
-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
set {dlm, my text item delimiters} to {my text item delimiters, strText}
set strJoined to lstText as text
set my text item delimiters to dlm
return strJoined
end intercalate
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
 
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
 
-- tail :: [a] -> [a]
on tail(xs)
if length of xs > 1 then
items 2 thru -1 of xs
else
{}
end if
end tail
Output:
g, HHH, 5, YY, ++, ///, \

BBC BASIC[edit]

REM >split
PRINT FN_split( "gHHH5YY++///\" )
END
:
DEF FN_split( s$ )
LOCAL c$, d$, split$, i%
c$ = LEFT$( s$, 1 )
split$ = ""
FOR i% = 1 TO LEN s$
d$ = MID$( s$, i%, 1 )
IF d$ <> c$ THEN
split$ += ", "
c$ = d$
ENDIF
split$ += d$
NEXT
= split$
Output:
g, HHH, 5, YY, ++, ///, \

C[edit]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *split(char *str);
int main(int argc,char **argv)
{
char input[13]="gHHH5YY++///\\";
printf("%s\n",split(input));
}
char *split(char *str)
{
char last=*str,*result=malloc(3*strlen(str)),*counter=result;
for (char *c=str;*c;c++) {
if (*c!=last) {
strcpy(counter,", ");
counter+=2;
last=*c;
}
*counter=*c;
counter++;
}
*(counter--)='\0';
return realloc(result,strlen(result));
}
Output:
g, HHH, 5, YY, ++, ///, \

C#[edit]

using System;
using System.Linq;
using System.Collections.Generic;
 
public class Program
{
string s = @"gHHH5YY++///\";
Console.WriteLine(s.RunLengthSplit().Delimit(", "));
}
 
public static class Extensions
{
public static IEnumerable<string> RunLengthSplit(this string source) {
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext()) yield break;
char previous = enumerator.Current;
int count = 1;
while (enumerator.MoveNext()) {
if (previous == enumerator.Current) {
count++;
} else {
yield return new string(Enumerable.Repeat(previous, count).ToArray());
previous = enumerator.Current;
count = 1;
}
}
yield return new string(Enumerable.Repeat(previous, count).ToArray());
}
}
 
public static string Delimit<T>(this IEnumerable<T> source, string separator = "") => string.Join(separator ?? "", source);
}
Output:
g, HHH, 5, YY, ++, ///, \

C++[edit]

 
// Solution for http://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character
#include<string>
#include<iostream>
auto spliter(const std::string &input) {
auto firstCommaPast = false;
std::string res ="";
auto prev = '\0';
for(auto it = input.cbegin(); it != input.cend();++it) {
if(*it!=prev) {
if(!firstCommaPast) {
firstCommaPast = true;
} else {
res+=", ";
}
}
res+=*it;
prev=*it;
}
return res;
}
 
int main() {
auto input = R"(gHHH5 ))YY++,,,///\)";
std::cout<<spliter(input);
}
Output:
g, HHH, 5,   , )), YY, ++, ,,,, ///, \

Clojure[edit]

(defn print-cchanges [s]
(println (clojure.string/join ", " (map first (re-seq #"(.)\1*" s)))))
 
(print-cchanges "gHHH5YY++///\\")
 
Output:
g, HHH, 5, YY, ++, ///, \

Common Lisp[edit]

(defun split (string)
(loop :for prev := nil :then c
:for c :across string
:do (format t "~:[~;, ~]~c" (and prev (char/= c prev)) c)))
 
(split "gHHH5YY++///\\")
 
Output:
g, HHH, 5, YY, ++, ///, \

Doing more work that what's being ask, the following solution builds a list of strings then output it:

(defun split (string)
(flet ((make-buffer ()
(make-array 0 :element-type 'character :adjustable t :fill-pointer t)))
(loop with buffer = (make-buffer)
with result
for prev = nil then c
for c across string
when (and prev (char/= c prev))
do (push buffer result)
(setf buffer (make-buffer))
do (vector-push-extend c buffer)
finally (push buffer result)
(format t "~{~A~^, ~}"(nreverse result)))))
 
(split "gHHH5YY++///\\")
Output:
g, HHH, 5, YY, ++, ///, \

Elixir[edit]

split = fn str ->
IO.puts " input string: #{str}"
String.graphemes(str)
|> Enum.chunk_by(&(&1))
|> Enum.map_join(", ", &Enum.join &1)
|> fn s -> IO.puts "output string: #{s}" end.()
end
 
split.("gHHH5YY++///\\")
Output:
 input string: gHHH5YY++///\
output string: g, HHH, 5, YY, ++, ///, \

F#[edit]

open System.Text.RegularExpressions
let splitRuns s = Regex("""(.)\1*""").Matches(s) |> Seq.cast<Match> |> Seq.map (fun m -> m.Value) |> Seq.toList
printfn "%A" (splitRuns """gHHH5YY++///\""")
Output:
["g"; "HHH"; "5"; "YY"; "++"; "///"; "\"]

Forth[edit]

Works with: Gforth version 0.7.3
CREATE A 0 ,               
: [email protected]+ A @ C@ [ 1 CHARS ]L A +! ;
: SPLIT. ( c-addr u --) SWAP A ! A @ C@
BEGIN OVER WHILE
[email protected]+ TUCK <> IF ." , " THEN
DUP EMIT SWAP 1- SWAP
REPEAT DROP ;
: TEST OVER OVER
." input: " TYPE CR
." split: " SPLIT. CR ;
s" gHHH5YY++///\" TEST
s" gHHH5 ))YY++,,,///\" TEST
BYE
Output:
input: gHHH5YY++///\
split: g, HHH, 5, YY, ++, ///, \
input: gHHH5  ))YY++,,,///\
split: g, HHH, 5,   , )), YY, ++, ,,,, ///, \

Fortran[edit]

This is F77 style, except for the END SUBROUTINE SPLATTER which would be just END, which for F90 is also allowable outside of the MODULE protocol. Linking the start/stop markers by giving the same name is helpful, especially when the compiler checks for this. The $ symbol at the end of a FORMAT code sequence is a common F77 extension, meaning "do not finish the line" so that a later output will follow on. This is acceptable to F90 and is less blather than adding the term ,ADVANCE = "NO" inside a WRITE statement that would otherwise be required. Output is to I/O unit 6 which is the modern default for "standard output". The format code is A meaning "any number of characters" rather than A1 for "one character" so as to accommodate not just the single character from TEXT but also the two characters of ", " for the splitter between sequences. Alas, there is no provision to change fount or colour for this, to facilitate the reader's attempts to parse the resulting list especially when the text includes commas or spaces of its own. By contrast, with quoted strings, the standard protocol is to double contained quotes.

An alternative method would be to prepare the entire output in a CHARACTER variable then write that, but this means answering the maddening question "how long is a piece of string?" for that variable, though later Fortran has arrangements whereby a text variable is resized to suit on every assignment, as in TEMP = TEMP // more - but this means repeatedly copying the text to the new manifestation of the variable. Still another approach would be to prepare an array of fingers to each split point (as in Phrase_reversals#Fortran) so that the final output would be a single WRITE using that array, and again, how big must the array be? At most, as big as the number of characters in TEXT. With F90, subroutines can declare arrays of a size determined on entry, with something like INTEGER A(LEN(TEXT))

If the problem were to be solved by writing a "main line" only, there would have to be a declaration of the text variable there but since a subroutine can receive a CHARACTER variable of any size (the actual size is passed as a secret parameter), this can be dodged.

For this example a DO-loop stepping along the text is convenient, but in a larger context it would probably be most useful to work along the text with fingers L1 and L2 marking the start and finish positions of each sequence.
      SUBROUTINE SPLATTER(TEXT)	!Print a comma-separated list. Repeated characters constitute one item.
Can't display the inserted commas in a different colour so as not to look like any commas in TEXT.
CHARACTER*(*) TEXT !The text.
INTEGER L !A finger.
CHARACTER*1 C !A state follower.
IF (LEN(TEXT).LE.0) RETURN !Prevent surprises in the following..
C = TEXT(1:1) !Syncopation: what went before.
DO L = 1,LEN(TEXT) !Step through the text.
IF (C.NE.TEXT(L:L)) THEN !A change of character?
C = TEXT(L:L) !Yes. This is the new normal.
WRITE (6,1) ", " !Set off from what went before. This is not from TEXT.
END IF !So much for changes.
WRITE (6,1) C !Roll the current character. (=TEXT(L:L))
1 FORMAT (A,$) !The $ sez: do not end the line.
END DO !On to the next character.
WRITE (6,1) !Thus end the line. No output item means that the $ is not reached, so the line is ended.
END SUBROUTINE SPLATTER !TEXT with spaces, or worse, commas, will produce an odd-looking list.
 
PROGRAM POKE
CALL SPLATTER("gHHH5YY++///\") !The example given.
END

Unfortunately, the syntax highlighter has failed to notice the terminating quote character, presumably because the preceding backslash might be an "escape sequence" trigger, a facility not used in Fortran text literals except possibly as a later modernist option.

Output:
g, HHH, 5, YY, ++, ///, \

Go[edit]

Treating "character" as a byte:

package main
 
import (
"bytes"
"fmt"
)
 
func main() {
fmt.Println(scc(`gHHH5YY++///\`))
}
 
func scc(s string) string {
if len(s) < 2 {
return s
}
var b bytes.Buffer
p := s[0]
b.WriteByte(p)
for _, c := range []byte(s[1:]) {
if c != p {
b.WriteString(", ")
}
b.WriteByte(c)
p = c
}
return b.String()
}
Output:
g, HHH, 5, YY, ++, ///, \

Haskell[edit]

import Data.List (group, intercalate)
 
main :: IO ()
main = putStrLn $ intercalate ", " (group "gHHH5YY++///\\")
Output:
g, HHH, 5, YY, ++, ///, \

J[edit]

Solution:

splitChars=: (1 ,~ 2 ~:/\ ]) <;.2 ]
delimitChars=: ', ' joinstring splitChars

Example Usage:

   delimitChars 'gHHH5YY++///\'
g, HHH, 5, YY, ++, ///, \

Java[edit]

package org.rosettacode;
 
import java.util.ArrayList;
import java.util.List;
 
 
/**
* This class provides a main method that will, for each arg provided,
* transform a String into a list of sub-strings, where each contiguous
* series of characters is made into a String, then the next, and so on,
* and then it will output them all separated by a comma and a space.
*/

public class SplitStringByCharacterChange {
 
public static void main(String... args){
for (String string : args){
 
List<String> resultStrings = splitStringByCharacter(string);
String output = formatList(resultStrings);
System.out.println(output);
}
}
 
/**
* @param string String - String to split
* @return List<\String> - substrings of contiguous characters
*/

public static List<String> splitStringByCharacter(String string){
 
List<String> resultStrings = new ArrayList<>();
StringBuilder currentString = new StringBuilder();
 
for (int pointer = 0; pointer < string.length(); pointer++){
 
currentString.append(string.charAt(pointer));
 
if (pointer == string.length() - 1
|| currentString.charAt(0) != string.charAt(pointer + 1)) {
resultStrings.add(currentString.toString());
currentString = new StringBuilder();
}
}
 
return resultStrings;
}
 
/**
* @param list List<\String> - list of strings to format as a comma+space-delimited string
* @return String
*/

public static String formatList(List<String> list){
 
StringBuilder output = new StringBuilder();
 
for (int pointer = 0; pointer < list.size(); pointer++){
output.append(list.get(pointer));
 
if (pointer != list.size() - 1){
output.append(", ");
}
}
 
return output.toString();
}
}
Output:
g, HHH, 5, YY, ++, ///, \

JavaScript[edit]

ES6[edit]

Translation of: Haskell
(() => {
// GENERIC FUNCTIONS ------------------------------------------------------
 
// concat :: [[a]] -> [a] | [String] -> String
const concat = xs =>
xs.length > 0 ? (() => {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs);
})() : [];
 
// group :: Eq a => [a] -> [[a]]
const group = xs => groupBy((a, b) => a === b, xs);
 
// groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
const groupBy = (f, xs) => {
const dct = xs.slice(1)
.reduce((a, x) => {
const
h = a.active.length > 0 ? a.active[0] : undefined,
blnGroup = h !== undefined && f(h, x);
return {
active: blnGroup ? a.active.concat([x]) : [x],
sofar: blnGroup ? a.sofar : a.sofar.concat([a.active])
};
}, {
active: xs.length > 0 ? [xs[0]] : [],
sofar: []
});
return dct.sofar.concat(dct.active.length > 0 ? [dct.active] : []);
};
 
// intercalate :: String -> [a] -> String
const intercalate = (s, xs) => xs.join(s);
 
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
 
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[0], null, x[1]] : x
);
 
// stringChars :: String -> [Char]
const stringChars = s => s.split('');
 
 
// TEST -------------------------------------------------------------------
return show(
intercalate(', ',
map(concat, group(stringChars('gHHH5YY++///\\')))
)
);
 
// -> "g, HHH, 5, YY, ++, ///, \\"
})();
Output:
g, HHH, 5, YY, ++, ///, \

jq[edit]

# input: a string
# output: a stream of runs
def runs:
def init:
explode as $s
| $s[0] as $i
| (1 | until( $s[.] != $i; .+1));
if length == 0 then empty
elif length == 1 then .
else init as $n | .[0:$n], (.[$n:] | runs)
end;
 
"gHHH5YY++///\\" | [runs] | join(", ")
Output:

Using the -r ("raw output") command-line option of jq:

g, HHH, 5, YY, ++, ///, \

Kotlin[edit]

// version 1.0.6
 
fun splitOnChange(s: String): String {
if (s.length < 2) return s
var t = s.take(1)
for (i in 1 until s.length)
if (t.last() == s[i]) t += s[i]
else t += ", " + s[i]
return t
}
 
fun main(args: Array<String>) {
val s = """gHHH5YY++///\"""
println(splitOnChange(s))
}
Output:
g, HHH, 5, YY, ++, ///, \

Lua[edit]

Note that the backslash must be quoted as a double backslash as Lua uses C-like escape sequences.

function charSplit (inStr)
local outStr, nextChar = inStr:sub(1, 1)
for pos = 2, #inStr do
nextChar = inStr:sub(pos, pos)
if nextChar ~= outStr:sub(#outStr, #outStr) then
outStr = outStr .. ", "
end
outStr = outStr .. nextChar
end
return outStr
end
 
print(charSplit("gHHH5YY++///\\"))
Output:
g, HHH, 5, YY, ++, ///, \

Mathematica[edit]

The backslash (\) must be escaped with another backslash when defining the string.

StringJoin@@Riffle[StringCases["gHHH5YY++///\\", p : (x_) .. -> p], ", "]
Output:
g, HHH, 5, YY, ++, ///, \

Nim[edit]

This example does not show the output mentioned in the task description on this page (or a page linked to from here). Please ensure that it meets all task requirements and remove this message.
Note that phrases in task descriptions such as "print and display" and "print and show" for example, indicate that (reasonable length) output be a part of a language's solution.


 proc splitOnDiff(str: string) : string =
result = ""
 
if str.len < 1: return result
 
var prevChar : char = str[0]
 
for idx in 0 .. < str.len:
if str[idx] != prevChar:
result &= ", "
prevChar = str[idx]
 
result &= str[idx]
 
 
assert splitOnDiff("""X""") == """X"""
assert splitOnDiff("""XX""") == """XX"""
assert splitOnDiff("""XY""") == """X, Y"""
assert splitOnDiff("""gHHH5YY++///\""") == """g, HHH, 5, YY, ++, ///, \"""
 
echo splitOnDiff("""gHHH5YY++///\""")

ooRexx[edit]

Parse Arg str  .                                  /*obtain optional arguments from the CL*/
If str=='' Then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
i=1
ol=''
Do Forever
j=verify(str,substr(str,i,1),'N',i,99) /* find first character that's different */
If j=0 Then Do /* End of strin reached */
ol=ol||substr(str,i) /* the final substring */
Leave
End
ol=ol||substr(str,i,j-i)', ' /* add substring and delimiter */
i=j
End
Say ol
Output:
g, HHH, 5, YY, ++, ///, \

Perl 6[edit]

Works with: Rakudo version 2017.05
sub group-chars ($str) { $str.comb: / (.) $0* / }
 
# Testing:
 
for Q[gHHH5YY++///\], Q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦‍♂️👨‍👩‍👧‍👦] -> $string {
put 'Original: ', $string;
put ' Split: ', group-chars($string).join(', ');
}
Output:
Original: gHHH5YY++///\
   Split: g, HHH, 5, YY, ++, ///, \
Original: fffn⃗n⃗n⃗»»»  ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦‍♂️👨‍👩‍👧‍👦
   Split: fff, , n⃗n⃗n⃗, »»»,   , ℵℵ, ☄☄, ☃, ☃̂, ☃, 🤔, 🇺🇸, 🤦‍♂️, 👨‍👩‍👧‍👦

The second test-case is to show that Perl 6 works with strings on the Unicode grapheme level, handles whitespace, combiners, and zero width characters up to Unicode Version 9.0, and multi-byte Emoji characters up to Version 4.0 correctly. (Perl 6 provisionally handles Unicode Versions 10.0 and Emoji Version 5.0 but they aren't released yet so aren't officially supported.) For those of you with browsers unable to display the second string, it consists of:

  • {LATIN SMALL LETTER F} x 3
  • {ZERO WIDTH NO-BREAK SPACE} x 3
  • {LATIN SMALL LETTER N, COMBINING RIGHT ARROW ABOVE} x 3
  • {RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK} x 3
  • {SPACE} x 2,
  • {ALEF SYMBOL} x 2,
  • {COMET} x 2,
  • {SNOWMAN} x 1,
  • {SNOWMAN, COMBINING CIRCUMFLEX ACCENT} x 1
  • {SNOWMAN} x 1,
  • {THINKING FACE} x 1
  • {REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S} x 1
  • {FACE PALM, ZERO WIDTH JOINER, MALE SIGN, VARIATION SELECTOR-16} x 1
  • {MAN, ZERO WIDTH JOINER, WOMAN, ZERO WIDTH JOINER, GIRL, ZERO WIDTH JOINER, BOY} x 1

Phix[edit]

function split_on_change(string in)
string out = ""
if length(in) then
integer prev = in[1]
for i=1 to length(in) do
integer ch = in[i]
if ch!=prev then
out &= ", "
prev = ch
end if
out &= ch
end for
end if
return out
end function
 
puts(1,split_on_change(`gHHH5YY++///\`))
Output:
g, HHH, 5, YY, ++, ///, \

PowerShell[edit]

Translation of: BBC BASIC
 
function Split-String ([string]$String)
{
[string]$c = $String.Substring(0,1)
[string]$splitString = $c
 
for ($i = 1; $i -lt $String.Length; $i++)
{
[string]$d = $String.Substring($i,1)
 
if ($d -ne $c)
{
$splitString += ", "
$c = $d
}
 
$splitString += $d
}
 
$splitString
}
 
 
Split-String "gHHH5YY++///\"
 
Output:
g, HHH, 5, YY, ++, ///, \

Python[edit]

import itertools
 
try: input = raw_input
except: pass
 
s = input()
groups = []
for _, g in itertools.groupby(s):
groups.append(''.join(g))
print(' input string:  %s' % s)
print(' output string:  %s' % ', '.join(groups))
Output:
  when using the default input:
      input string:  gHHH5YY++///\
     output string:  g, HHH, 5, YY, ++, ///, \

Racket[edit]

Translation of: Python
#lang racket
(define (split-strings-on-change s)
(map list->string (group-by values (string->list s) char=?)))
 
(displayln (string-join (split-strings-on-change #<<<
gHHH5YY++///\
<
)
", "))
Output:
g, HHH, 5, YY, ++, ///, \

REXX[edit]

version 1[edit]

/*REXX program splits a string based on change of character ───► a comma delimited list.*/
parse arg str /*obtain optional arguments from the CL*/
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
p=left(str, 1) /*placeholder for the "previous" string*/
$= /* " " " output " */
do j=1 for length(str); @=substr(str,j,1) /*obtain a character from the string. */
if @\==p then $=$', ' /*Not replicated char? Append delimiter*/
p=@; $=$ || @ /*append a character to the $ string.*/
end /*j*/ /* [↓] keep peeling chars until done. */
say ' input string: ' str /*display the original string & output.*/
say ' output string: ' $ /*stick a fork in it, we're all done. */
output   when using the default input:
          input string:  gHHH5YY++///\
         output string:  g, HHH, 5, YY, ++, ///, \

version 2[edit]

Parse arg str                         /*obtain optional arguments from the CL*/
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
input=str
x=''
cp=''
result=''
Do While str<>''
Parse Var str c +1 str
If c==cp Then x=x||c
Else Do
If x>>'' Then
result=result||x', '
x=c
End
cp=c
End
result=result||x
say ' input string: ' input
say ' output string: ' result

Ring[edit]

 
see split("gHHH5YY++///\")
 
func split(s )
c =left (s, 1)
split = ""
for i = 1 to len(s)
d = substr(s, i, 1)
if d != c
split = split + ", "
c = d
ok
split = split + d
next
return split
 

Output:

g, HHH, 5, YY, ++, ///, \

Ruby[edit]

def split(str)
puts " input string: #{str}"
s = str.chars.chunk(&:itself).map{|_,a| a.join}.join(", ")
puts "output string: #{s}"
s
end
 
split("gHHH5YY++///\\")
Output:
 input string: gHHH5YY++///\
output string: g, HHH, 5, YY, ++, ///, \

Rust[edit]

fn splitter(string: &str) -> String {
let chars: Vec<char> = string.chars().collect();
let mut result = Vec::<String>::new();
let mut last_mismatch = 0;
for i in 0..chars.len() {
if chars.len() == 1 {
return chars[0..1].iter().map(|c| c ).collect();
}
if i > 0 && chars[i-1] != chars[i] {
let temp_result: String = chars[last_mismatch..i].iter().map(|c| c).collect();
result.push( temp_result );
last_mismatch = i;
}
if i == chars.len() -1{
let temp_result: String = chars[last_mismatch..chars.len()].iter().map(|c| c).collect();
result.push( temp_result );
}
}
return result.join(", ");
}
 
fn main() {
let test_string = "g";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
let test_string = "";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
let test_string = "gHHH5YY++///\\";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
}
 
Output:
input string: g
output string: g
input string: 
output string: 
input string: gHHH5YY++///\
output string: g, HHH, 5, YY, ++, ///, \

Sed[edit]

This example does not show the output mentioned in the task description on this page (or a page linked to from here). Please ensure that it meets all task requirements and remove this message.
Note that phrases in task descriptions such as "print and display" and "print and show" for example, indicate that (reasonable length) output be a part of a language's solution.


 
echo 'gHHH5YY++///\' | sed 's/\(.\)\1*/&, /g;s/, $//'
 

Sidef[edit]

func group(str) {
gather {
while (var match = (str =~ /((.)\g{-1}*)/g)) {
take(match[0])
}
}
}
 
say group(ARGV[0] \\ 'gHHH5YY++///\\').join(', ')
Output:
g, HHH, 5, YY, ++, ///, \

Standard ML[edit]

(*
* Head-Tail implementation of grouping
*)
fun group' ac nil = [ac]
| group' nil (y::ys) = group' [y] ys
| group' (x::ac) (y::ys) = if x=y then group' (y::x::ac) ys else (x::ac) :: group' [y] ys
 
fun group xs = group' nil xs
 
fun groupString str = String.concatWith ", " (map implode (group (explode str)))
Output:
- groupString "gHHH5YY++///\\";
val it = "g, HHH, 5, YY, ++, ///, \\" : string

Tcl[edit]

This example does not show the output mentioned in the task description on this page (or a page linked to from here). Please ensure that it meets all task requirements and remove this message.
Note that phrases in task descriptions such as "print and display" and "print and show" for example, indicate that (reasonable length) output be a part of a language's solution.


This is most concise with regular expressions. Note well the two steps: it could be achieved in one very clever regexp, but being that clever is usually a bad idea (for both readability and performance, in this case).

set string "gHHH5YY++///\\"
 
regsub -all {(.)\1*} $string {\0, } string
regsub {, $} $string {} string
puts $string

zkl[edit]

fcn group(str){
C,out := str[0],Sink(C);
foreach c in (str[1,*]){ out.write(if(c==C) c else String(", ",C=c)) }
out.close();
}
group("gHHH5YY++///\\").println();
Output:
g, HHH, 5, YY, ++, ///, \

ZX Spectrum Basic[edit]

 10 LET s$="gHHH5YY++///\"
20 LET c$=s$(1)
30 LET n$=c$
40 FOR i=2 TO LEN s$
50 IF s$(i)<>c$ THEN LET n$=n$+", "
60 LET n$=n$+s$(i)
70 LET c$=s$(i)
80 NEXT i
90 PRINT n$
Output:
g, HHH, 5, YY, ++, ///, \