A scan string rarely says only what your program needs. The reader delivers the whole label - family tag, part number, lot, date - as one line, and the job is to take characters 5 to 9 and ignore the rest. Every environment that can receive a scan string also has the equivalents of the spreadsheet LEFT, RIGHT and MID functions, so the trim itself is a one-line call once the character positions are known. What follows is the recipe: map the code layout, take the piece you need in structured text, robot script and slice-style languages, convert the result to a number when the downstream logic wants a number, and verify the trim before it reaches production. The two traps that cost the most time - counting from the wrong base and letting a terminator character corrupt the length check - each get their own section.
What you need
- The decoded string exactly as your program receives it. Print it with its length. A 16-character layout can arrive as 17 or 18 characters once the reader appends a terminator.
- The layout map. Which positions hold which field, taken from the label specification or from ten sample scans laid side by side.
- The string function list for your environment. IEC 61131-3 structured text ships LEFT, RIGHT and MID as standard functions; robot script manuals document string functions beginning with str_ in the internals section; scripting languages expose substring and slice methods.
The trim is identical no matter which device delivers the string. A handheld code reader on USB hands your program the same characters as a fixed code reader on Ethernet: the LS-HCR-0001 is a 1.0 MP handheld reader with USB 2.0 output, and the MV-ID3013PM-12M-WBN is a 1.3 MP compact fixed reader with a 12 mm focal length and Fast Ethernet output.
- Resolution
- Up to 3 MP
- Shutter
- Global
- Interface
- Fast Ethernet
- Resolution
- Up to 3 MP
A third source hides the reader entirely. A smart camera decodes the label on the device and returns only the result string to the program - same trim, same positions. The MV-SC3050XC-16M-ATN-H is a 5 MP color vision sensor with a global shutter.
- Interface
- Fast Ethernet
- Resolution
- 5 MP
- Sensor format
- 1/1.45"
- Shutter
- Global
Map the code layout before you slice
Fixed-position trimming assumes every field keeps the same width on every label. Write the layout down as position ranges before you write any code:
| Positions (1-based) | Field | Example value |
|---|---|---|
| 1-4 | Literal "PART" | PART |
| 5-9 | Part number | 12345 |
| 10-12 | Literal "LOT", ignored | LOT |
| 13-16 | Lot number | 0099 |
The fastest way to build that map is to print scans from several lots in a text editor with a column ruler and confirm that every field starts and ends in the same place. Then record the counting base: spreadsheet functions and the IEC 61131-3 string functions count from 1, so position 5 is the fifth character, while Python-style slices count from 0, so the same character sits at index 4. Most wrong-character bugs are that conversion, not the trim.
If any field changes length between labels, every position after it shifts and the trim cuts into the wrong field without raising an error. Move to delimiter splitting instead; the split barcode string data into fields guide covers that path field by field.
LEFT, RIGHT and MID: the three cuts
In words: LEFT takes the first n characters, RIGHT takes the last n, and MID takes L characters starting at position P.
Where each family lives:
| Environment | Functions | Counts positions from |
|---|---|---|
| IEC 61131-3 structured text | LEFT, RIGHT, MID standard functions | 1 |
| Spreadsheet-style | LEFT, MID, RIGHT | 1 |
| Robot controller script | str_ functions, internals section of the script manual | stated per function |
| C# and Java-style languages | Substring / substring methods | 0 |
| Python-style | slice notation s[start:end] | 0 |
Read two details once, from the manual, and write them next to your map: whether the second argument is a length or an end index, and which base the robot script functions use.
Worked example: part number out of a 16-character scan
Inputs. The scan string is PART12345LOT0099, 16 characters in the layout from the table above - an illustrative fixed-format label of the kind where one code carries several fields; your real layout comes from your label specification. The program needs the part number as a number for a lookup. MID takes L characters from 1-based position P.
Rule. Check the length first, take MID for the part number, RIGHT for the lot, and convert only the part number.
Arithmetic.
- LEN("PART12345LOT0099") = 4 + 5 + 3 + 4 = 16 characters, so the scan matches the layout and is worth slicing.
- Part number = MID(scan, 5, 5): positions 5, 6, 7, 8 and 9 hold "1", "2", "3", "4", "5", so the result is "12345".
- 0-based equivalent: start index = 5 - 1 = 4, end index = 4 + 5 = 9, so scan[4:9] returns "12345".
- Lot = RIGHT(scan, 4): positions 16 - 4 + 1 = 13 through 16, so the result is "0099".
- Conversion: "12345" becomes the integer 12345. "0099" becomes 99 and loses two leading zeros, so the lot stays text.
Decision. Reject any scan whose length is not 16 characters; that one threshold catches misreads, wrong labels and terminator surprises before slicing turns them into plausible garbage. Convert the part number, because the lookup compares numbers. Keep the lot as the string "0099", because a lot code is an identifier, not a quantity.
Take the trim in your program, step by step
- Dump the string. Display or log the scan exactly as received, with its length. Strip any terminator once, at the input, before any position math.
- Write the position map from the label specification or ten sample scans, and mark which fields are fixed width.
- Confirm the widths across lots. If any field varies, stop and split on the separator instead.
- Write the MID call in 1-based positions - MID(scan, 5, 5) for characters 5 to 9 - then convert if your language counts from 0: start index = position - 1, end index = start index + length.
- Convert numeric fields only, and handle the failed conversion: environments either return zero or raise an error, and the length check from step 1 should make both unreachable.
- Test two strings: a known-good label that must yield "12345" and 12345, and a deliberately short scan that the length check must reject.
Convert the trimmed text to a number, only when you need one
Every environment above has a string-to-number conversion: structured text converts string to integer as a standard conversion, the robot script conversion sits among the str_ functions, and scripting languages cast. Three rules keep it predictable:
- Convert after the trim, never before. "12345" converts cleanly; "PART12345..." does not convert to anything useful.
- Quantities convert; identifiers stay text. A part number used only for matching should remain a string even when it looks numeric.
- Where a comparison is all you need, compare the strings directly; the PLC ladder string compare pattern matches the trimmed string against an expected value without any conversion.
What happens to the number afterwards - a PLC memory tag or a database lookup - is its own decision; the part number parameter lookup guide weighs both.
Verify the trim before you trust it
- Known-good label. The program must produce "12345" and 12345, not "1234" or "2345"; an off-by-one shows up on the first printed expectation.
- Short scan. The length check must refuse it. Scan any other code or a half-presented label; the program must reject, not slice.
- Buffer state. Fixed-length buffers and display fields keep the previous scan's characters when the new string is shorter; clear the destination before each copy. How leftover bytes behave in PLC memory is in the ASCII string storage guide, and why a string display keeps old characters after a shorter scan is the display-side version of the same bug.
- One scan, one clear. Empty the input buffer or string control after each scan instead of appending; clearing a string control after a barcode scan shows the pattern end to end.
Common mistakes that shift every character
- Counting from the wrong base. Label positions count from 1; slices and most substring methods count from 0. Write the conversion - index = position - 1 - next to the layout map.
- Terminators in the length. An appended carriage return or line feed makes LEN read 17 or 18 on a 16-character layout, and every RIGHT cut takes the wrong end. The CR and LF characters guide shows what those characters do in an HMI field.
- Second-argument confusion. One language's substring takes start and length, another takes start and end; a length of 5 passed where an end index is expected returns one character instead of five.
- Junk bytes from the link. Noise on a serial line inserts characters, every position shifts, and the trim still returns something plausible. The garbage characters guide traces those bytes back to the link settings.
When fixed positions are the wrong tool
If you specify the label yourself, put a separator between the fields - a dash, a comma or a dedicated separator character - and split on it; variable widths stop being a risk. Codes that carry defined field markers behave the same way: parse the markers, not the positions. Fixed-position trimming earns its keep on closed layouts where every width is fixed by the label specification, and it fails quietly anywhere else.
The string side of a scan integration costs nothing to get right: map the layout, check the length, take MID. When you are still choosing the hardware that produces the string, the code reader finder filters fixed readers, handhelds and smart cameras by interface and resolution, and the code-reading topic page collects the rest of the string-handling guides.
Questions
Do MID positions count from 0 or from 1?
Spreadsheet functions and the IEC 61131-3 string functions count from 1, so MID(s, 5, 5) starts at the fifth character. Python-style slices and most substring methods count from 0, so the same five characters are s[4:9]. Convert with index = position - 1 and exclusive end = index + length, and write the counting base next to your position map so the next person does not have to guess it.
How do I keep leading zeros in a lot code after trimming?
Keep it as text. Converting "0099" to a number gives 99 and the zeros never come back. Store the lot in a string tag or buffer, compare lot codes as strings, and zero-pad only at the moment of display or printing if a fixed width is required. A quantity such as a piece count is the kind of field that converts safely.
Why does a shorter scan leave old characters on the display or in the tag?
Fixed-length buffers and fixed-width display fields keep whatever the previous scan wrote when the new string is shorter. Clear the destination before each copy, write the string together with its length, and pad to a fixed width when the field expects one. The same habit applies to string tags in PLC memory and to text fields on the HMI.
When should I split on a separator instead of trimming fixed positions?
When any field can change length. A layout with variable fields shifts every position after the first variable one, and the trim cuts the wrong characters without raising any error. Splitting finds the separator and takes what sits between separators, so it survives variable field lengths. Reserve fixed positions for codes where every field is a guaranteed width.
Does the terminator count as a character in my length check?
It can. Readers and scanners often append a carriage return, a line feed or an Enter to the string, and serial links may add terminators that Ethernet outputs leave out. If LEN returns one or two characters more than the layout, strip the terminator once at the input or configure the reader output to drop it, before any position math.


