Scan String — Control Systems/Strings
Control_Systems/Strings/Scan_String · 1 input / 2 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++
Description#
The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.
Scan String
Control Systems / Strings
Reads values out of text by a format, one output per conversion, as
Simulink's Scan String does. With the Format "%d %f" (the default)
and the text 42 3.5, output 1 is 42 and output 2 is 3.5. It is
not C's sscanf: every rule below was measured on Simulink,
and several of them differ from C.
Ports
- Input (
str,ICoreString) – the text, one value. - Output 1 (
i32,ICoreInt32, for the default Format's%d) – one value [1,1]. - Output 2 (
f32,ICoreSingle, for the default Format's%f) – one value [1,1].
There is one output per conversion, in order; the number of outputs is yours
to set and must equal the number of conversions in the Format. What each output
carries is decided by its conversion, exactly as in Simulink:
%d and %ld a 32-bit integer (i32),
%hd a 16-bit one (i16), %u and
%lu an unsigned 32-bit integer (u32),
%hu an unsigned 16-bit one (u16), %f,
%e and %g a single (f32),
%lf, %le and %lg a double
(f64), %s and %c text (str,
ICoreString).
Parameters
- Format – the format, written with its quotes as in
Simulink:
"%d %f"(the default). A doubled quote inside is one quote. A conversion is%, an optional width, an optionalhorl, and one ofd u f e g s c; any other character must be matched exactly by the text;%%matches a percent sign; the escapes\n,\t,\r,\a,\b,\f,\vand\\match the control characters they name. Between 1 and 128 conversions, Simulink's own limits. - Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
How the text is read
- Each character of the Format that is not a conversion – a space
included – must match exactly one identical character of the text:
"%d %d"does not read1<tab>2, and" %d"does not read5. The first character that does not match STOPS the scan, and every later output is 0 or empty text. - A number skips white space first (space, tab, newline, vertical tab, form
feed, return), then reads an optional
+, an optional-, and thenInf(a capital I, then n and f in either case),NaN(any case), or decimal digits – with a fraction for%f,%eand%gand without one for%dand%u, whose exponent is still read:%dof1e3is 1000 and of3.9is 3. An exponent is read only when digits follow it. Hexadecimal is not read. - A number that is not there is not a stop: the output is 0 and the
scan goes on from the same place (
"%d%s"ofabcis 0 andabc). - An integer conversion cuts the number toward zero into a 64-bit integer
that saturates (NaN is 0) and keeps its low 32 or 16 bits:
%dof1e10is 1410065408, ofInfis -1;%uof-5is 4294967291. Digits beyond what a double holds are kept exactly. - A single is the double nearest the text, rounded again to single.
%sskips white space and takes the characters up to the next white space;%cskips nothing and takes one character, or as many as its width. A width limits what a conversion may take after the white space it skipped, signs included – except a leading+, which is skipped with the white space and costs nothing:"%2d"of-123is -1 and of+123is 12.
What is not offered
Each of these stops the run with a message naming it: %i,
%x, %o, %X, %E,
%G, %n, a *, a flag such as
%-d or %0d, a precision (%5.2f),
hh, ll and L – all refused by
Simulink too – and scansets such as %[abc], which Simulink
accepts and this block does not.
Code export
C, C++, Python, MATLAB, Java and Rust all read the same values: each carries the same small scanner, written out in the language, and turns digits into numbers with the language's own correctly rounded parser. The Format is read when the code is generated, not when it runs.
VHDL, Verilog and SystemVerilog do not carry text, and PLC Structured Text is not supported either: the language has a string type, but the check that runs an exported core cannot read one back. An export to any of these four stops and names this block and the reason.
Simulink bridge
Import and export, mapped to simulink/String/Scan String. "Format"
crosses unchanged, quotes and all; on import the number of outputs and what each
one carries are read off it, since Simulink derives both from the Format as
well. simulink/String/String to Double and String to Single are
this same block with the Format preset to "%lf" and
"%f" (measured: the same block type, the same one-parameter
dialog), and they import as this block with that Format; they export back as
Scan String. The Simulink block has no sampling-time setting of its own, so a
positive Sampling Time (s) stays on this side and is reported rather
than written.
Notes
- Algebraic, with no state: the outputs depend only on the current input.
- Widths,
%sand%ccount bytes of UTF-8 here – the same count as Simulink's for plain ASCII text. - One measured corner is not reproduced: Simulink saturated an in-range value written as an 18-digit mantissa with an exponent – the value eight below 263, so still inside the 64-bit range – where this block keeps it. Integer text of 18 digits or fewer is not affected.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Strings/Scan_String |
| family | Control_Systems/Strings |
| solver environment class | ICoreBlock_0_Control_Systems_1_Strings_2_Scan_String |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/Scan_String/ICoreBlock_0_Control_Systems_1_Strings_2_Scan_String.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/Scan_String/ICoreBlock_0_Control_Systems_1_Strings_2_Scan_String.h |
| default size on canvas | 100 × 70 px |
| ports at insert | 1 in, 2 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++ |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreString | — |
| 2 | out | ICoreInt32 | — |
| 3 | out | ICoreSingle | — |
Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.
Configuration variables#
| Config variable | Default | Simulink parameter |
|---|---|---|
Format | "%d %f" | Format |
Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.
Simulink bridge#
| support | Support::Both |
| Simulink path | simulink/String/Scan String |
| port-count rule | PortsParam::ScanStringFormat |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Format | Format | passes through |
Caveat (shown to the user): "Format crosses verbatim, quotes included
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.
The verdict above is
tools/docs/check_block_descriptions.py(P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.
File banner (developer view)#
The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).
Scan String -- read values out of text by a Format, one output per conversion The Format is parsed ONCE per use into literal runs and conversions (parseFormat below), and everything else is driven by that parse: the output count verify checks, each output's type, the live answer, and every generator -- which walks the items at EXPORT time and emits straight-line calls into a small scanner written out in the target language, so no target interprets a format at run time.
The scanner is Simulink's, measured on R2026a, and the rules are in the header. Two of them shape every implementation below and are worth restating here:
- A NUMBER is read as text first -- signs, digits, a fraction for the float conversions,
an exponent only when digits follow it -- and turned into a value from that text: a float by a correctly rounded parse (a single is that double, rounded again: measured, "1.0000000596046448" reads as 1, not as the single above it); an integer by the same parse, cut toward zero, when that double is below 2^53 in magnitude, and EXACTLY from the digits above it ("9007199254740993" reads as ...993, which no double holds).
- The integer is a SATURATING 64-bit value whose low 16 or 32 bits are the output, so a
"%d" of 1e10 is 1410065408 and a "%u" of -5 is 4294967291.
Sample results#
This block carries a ICoreString signal, whose value is text rather than a number and is not something a plot has an axis for. The samples are in the table below, exactly as the run recorded them.
| t | in ICoreString-Out-0 | out ICoreInt32-Out-0 | out ICoreSingle-Out-1 |
|---|---|---|---|
| 0 | u0 | 0 | 0 |
| 0.4 | u0 | 0 | 0 |
| 0.8 | u0 | 0 | 0 |
| 1.2 | u0 | 0 | 0 |
| 1.6 | u0 | 0 | 0 |
| 2 | u0 | 0 | 0 |
| 2.4 | u0 | 0 | 0 |
| 2.8 | u0 | 0 | 0 |
| 3.2 | u0 | 0 | 0 |
| 3.6 | u0 | 0 | 0 |
| 4 | u0 | 0 | 0 |
| 4.4 | u0 | 0 | 0 |
| 4.8 | u0 | 0 | 0 |
| 5.2 | u0 | 0 | 0 |
Every 4th of 60 samples, from the table stimulus.
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | 0 … 0 |
ramp | Ramp: slope 1 from t = 0 | 0 … 0 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0 … 0 |
step | Step: 0 -> 1 at t = 1 s | 0 … 0 |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit 93133d604 · produced by docsSample --out <folder> --blocks Gain_Scheduled_Lead_Lag Controller_1D Controller_Blend_1D Controller_2D Controller_3D Observer_Form_1D Self_Conditioned_1D Line_Of_Sight_Access Orbit_Propagator_Kepler Attitude_Dynamics Attitude_Profile_Nadir_Pointing Attitude_Profile_Geographic_Pointing Multitaper_PSD Cross_Power_Spectral_Density Transfer_Function_Estimate Envelope_Spectrum Compose_String Scan_String --steps 60 · data docs/generated/samples/Control_Systems__Strings__Scan_String.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).