Runtime verified Test scripts included

Syntax

Substring(sourceString, startPosition[, substringLength])  →  string
2–3 arguments

Parameters

Name Type Required Description
sourceString string | number Yes String to take a portion of
startPosition string | number Yes 1-based position to start at, as a whole number
substringLength string | number No Number of characters to take, as a whole number

Example

%%[
  VAR @part
  SET @part = Substring("Hello World", 1, 5)
]%%
Part: %%=v(@part)=%%

Renders Part: Hello.

The usual pattern pairs it with IndexOf to cut a value at a separator — note the guard, because a missing separator would otherwise produce a negative length:

%%[
  VAR @fullName, @space, @first
  SET @fullName = "Dale Cameron"
  SET @space = IndexOf(@fullName, " ")
  IF @space > 1 THEN
    SET @first = Substring(@fullName, 1, Subtract(@space, 1))
  ELSE
    SET @first = @fullName
  ENDIF
]%%

That guard is not optional — a negative length aborts the page, see below.

Return value

string — the requested portion of the source.

An empty string is the only sentinel: it comes back when the start position is past the end of the source, when the requested length is 0, and when the source itself is empty. The returned text is otherwise an open domain, so there is no closed set of values to test for.

Behaviour

Positions are 1-based and the extraction runs forward. Substring("Hello World", 1, 5) gives Hello, starting at 7 gives Wor for a length of 3, and position 11 is the trailing d.

Omitting the length returns the remainder. Substring("Hello World", 7) gives World, and starting at the final position gives just d.

A start past the end returns an empty string, and an over-long length is capped. Starting at 20 in an eleven-character source returns nothing at all, while asking for 99 characters from position 7 returns the five that remain rather than failing.

Numbers are accepted for all three parameters and numeric strings for the two numeric ones. Taking two characters from position 3 of the numeric literal 9876543 gives 76, and quoting the start, the length, or both gives the same result as the bare numbers. A boolean source renders an empty string — it never becomes extractable text — and a boolean in either numeric position aborts the page, as do a decimal and a lettered string.

Counting is in UTF-16 code units, the same unit Length counts. In a source built as caf + Char(233) + " time", which measures 9, taking one character at position 4 yields a value whose own Length is 1, and starting at 6 lands exactly on the following word.

The two numeric arguments disagree about negatives

Call Renders
Substring("Hello World", 1, 5) Hello
Substring("Hello World", 0, 5) Hello
Substring("Hello World", -3, 5) Hello
Substring("Hello World", 3, 0) (empty)
Substring("Hello World", 3, -2) page aborts

A start position below 1 is silently clamped to the first character, so an off-by-one in the position is invisible. A negative length is not tolerated: it aborts the page with HTTP 422 and discards everything already rendered. Since lengths are usually computed by subtracting two positions, guard the arithmetic before the call rather than expecting the same forgiveness the start position gets.

Catalogued on Differs from official docs. The docs are silent on both bounds rather than wrong about them, so the entry is not flagged as contradicting them.

Show test script
%%[
  VAR @b
  SET @b = RequestParameter("b")

  /* documented forms plus the accepted argument types */
  IF @b == "safe" THEN
    OutputLine(Concat("HEAD=[", Substring("Hello World", 1, 5), "]"))
    OutputLine(Concat("MID=[", Substring("Hello World", 7, 3), "]"))
    OutputLine(Concat("ONE=[", Substring("Hello World", 11, 1), "]"))
    OutputLine(Concat("REST=[", Substring("Hello World", 7), "]"))
    OutputLine(Concat("STARTATEND=[", Substring("Hello World", 11), "]"))
    OutputLine(Concat("STARTPASTEND=[", Substring("Hello World", 20, 3), "]"))
    OutputLine(Concat("STARTPASTEND2=[", Substring("Hello World", 12), "]"))
    OutputLine(Concat("LEN0=[", Substring("Hello World", 3, 0), "]"))
    OutputLine(Concat("EMPTYSRC=[", Substring("", 1, 3), "]"))
    OutputLine(Concat("NUMSRC=[", Substring(9876543, 3, 2), "]"))
    OutputLine(Concat("STARTSTR=[", Substring("Hello World", "7", 3), "]"))
    OutputLine(Concat("LENSTR=[", Substring("Hello World", 7, "3"), "]"))
    OutputLine(Concat("BOTHSTR=[", Substring("Hello World", "1", "5"), "]"))
    OutputLine(Concat("SRCBOOL=[", Substring(true, 1, 2), "]"))
  ENDIF

  /* a start below 1 is clamped, an over-long length is capped */
  IF @b == "clamped" THEN
    OutputLine(Concat("START0=[", Substring("Hello World", 0, 5), "]"))
    OutputLine(Concat("STARTNEG=[", Substring("Hello World", -3, 5), "]"))
    OutputLine(Concat("LENOVER=[", Substring("Hello World", 7, 99), "]"))
  ENDIF

  /* positions count UTF-16 code units, matching Length */
  IF @b == "unicode" THEN
    VAR @u
    SET @u = Concat("caf", Char(233), " time")
    OutputLine(Concat("ULEN=[", Length(@u), "]"))
    OutputLine(Concat("UACCENTLEN=[", Length(Substring(@u, 4, 1)), "]"))
    OutputLine(Concat("UAFTER=[", Substring(@u, 6), "]"))
  ENDIF

  /* every branch below aborts the page: the start marker never renders */
  IF @b == "lenneg" THEN
    OutputLine(Concat("--- lenneg start ---"))
    OutputLine(Concat("LENNEG=[", Substring("Hello World", 3, -2), "]"))
  ENDIF

  IF @b == "startdec" THEN
    OutputLine(Concat("--- startdec start ---"))
    OutputLine(Concat("STARTDEC=[", Substring("Hello World", 2.5, 3), "]"))
  ENDIF

  IF @b == "lendec" THEN
    OutputLine(Concat("--- lendec start ---"))
    OutputLine(Concat("LENDEC=[", Substring("Hello World", 1, 3.7), "]"))
  ENDIF

  IF @b == "badstr" THEN
    OutputLine(Concat("--- badstr start ---"))
    OutputLine(Concat("BADSTR=[", Substring("Hello World", "abc", 3), "]"))
  ENDIF

  IF @b == "startbool" THEN
    OutputLine(Concat("--- startbool start ---"))
    OutputLine(Concat("STARTBOOL=[", Substring("Hello World", true, 5), "]"))
  ENDIF

  IF @b == "lenbool" THEN
    OutputLine(Concat("--- lenbool start ---"))
    OutputLine(Concat("LENBOOL=[", Substring("Hello World", 1, true), "]"))
  ENDIF

  IF @b == "a0" THEN
    OutputLine(Concat("--- a0 start ---"))
    OutputLine(Concat("A0=[", Substring(), "]"))
  ENDIF

  IF @b == "a1" THEN
    OutputLine(Concat("--- a1 start ---"))
    OutputLine(Concat("A1=[", Substring("Hello World"), "]"))
  ENDIF

  IF @b == "a4" THEN
    OutputLine(Concat("--- a4 start ---"))
    OutputLine(Concat("A4=[", Substring("Hello World", 1, 5, 2), "]"))
  ENDIF
]%%

Availability

Platform Available
Marketing Cloud Engagement Yes
Marketing Cloud Next Yes, from API 67.0

See also