String Count — Control Systems/Strings
Control_Systems/Strings/String_Count · 2 input / 1 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.
String Count
Control Systems / Strings
Counts how many times one piece of text occurs inside another: y = the number of occurrences of u2 in u1.
Ports
- u1 (
str,ICoreString) – the text being searched. - u2 (
str,ICoreString) – the text being counted. - Output (
u32,ICoreUInt32) – the count, one value. A count is never negative, so it is an unsigned integer.
Parameters
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Occurrences do not overlap
Counting restarts after each occurrence found, so "aa" occurs twice in "aaaa", not three times. This is the same answer Simulink gives. The two readings only differ when the text being counted can start inside itself – "aa", "abab", "---" – which is why it is worth stating: for a search text like "cat" or ", " the two rules give the same number and the difference never shows.
Counting nothing
An empty u2 answers the length of u1 plus one: "abc" gives 4. There is an empty piece of text before each byte of u1 and one more after the last. This too is what Simulink answers, and every generated target answers it.
Upper and lower case are different
"Cat" is not counted in "concatenate". There is no option to ignore case, for the reason set out on String Compare: the seven languages this block exports to do not agree on what ignoring case means for anything outside A–Z, so the family does one thing exactly rather than seven things approximately.
Code export
C, C++, Python, MATLAB, Java and Rust all produce the same count. Most of them have a built-in that counts occurrences, and most of those disagree with this block about the two cases above, so the scan is written out explicitly instead.
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. Since this block's inputs are always text, an export to any of these four stops and names this block and the reason.
Simulink bridge
Import and export, mapped to simulink/String/String Count, whose
output is set to uint32 to match. Simulink's case-sensitivity
switch is always written as on, matching this block. 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.
Simulink counts occurrences in characters where this block counts bytes; the two agree for plain ASCII text and differ by design on anything else, exactly as String Length records.
Notes
- Algebraic, with no state: the output depends only on the current inputs.
- An unconnected text input is empty text.
- A search text longer than the text being searched gives 0.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Strings/String_Count |
| family | Control_Systems/Strings |
| solver environment class | ICoreBlock_0_Control_Systems_1_Strings_2_String_Count |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/String_Count/ICoreBlock_0_Control_Systems_1_Strings_2_String_Count.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/String_Count/ICoreBlock_0_Control_Systems_1_Strings_2_String_Count.h |
| default size on canvas | 90 × 70 px |
| ports at insert | 2 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++ |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreString | u1 |
| 2 | in | ICoreString | u2 |
| 3 | out | ICoreUInt32 | — |
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#
No config variable beyond the Sampling Time (s) every block carries.
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/String Count |
| port-count rule | PortsParam:: |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
Caveat (shown to the user): Occurrences do NOT overlap on either side -- measured on R2026a, 'aa' in 'aaaa' answers 2 -- and an empty search text answers LEN + 1 on both sides, also measured. Simulink counts CHARACTERS where this block counts BYTES of UTF-8, so the two agree for ASCII and differ by design on anything else, the same trade String Length records. OutDataTypeStr is pinned to uint32 rather than left at Simulink's inherit rule, so both
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B0no sample under docs/generated/samples/ — nothing to cross-check (P8.1)
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).
String Count -- how many times one piece of text occurs in another y = the number of NON-OVERLAPPING occurrences of u2 in u1, counted in bytes of UTF-8. Two measured facts carry this block, and each is a silent wrong answer taken the other way: the occurrences do not overlap ("aa" in "aaaa" is 2, not 3) and an empty needle answers LEN(u1) + 1 rather than 0 or 1. The header has both measurements.
The scan is written out in six of the seven targets rather than delegated to a built-in, because the built-ins disagree about precisely those two cases.
Sample results#
No sample run is committed for this block. Samples come from the headless harness (DOCS_PLAN.md P8.1) into docs/generated/samples/; until one exists this block's behaviour is witnessed by the parity and export-verification suites, not by a plot here.