{
  "category": "Text functions",
  "display_name": "FIND",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.FIND",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 47 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 47 recorded answers",
    "workbook_corpus": {
      "cells": 47,
      "matched": 47
    }
  },
  "implementations": [
    {
      "commit": "9e1884369a678158dd82d3d74188daf986995e87",
      "flavour": "excel-compatibility-candidate",
      "language": "Rust",
      "name": "CalcFunc",
      "own_code": [
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/text_functions.rs#L301-L301"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/text_functions::count_surrogate_pairs",
        "catalogue/text_functions::start",
        "catalogue/text_functions::find",
        "ported/text_kernels::text",
        "ported/text_kernels::unit_width",
        "ported/text_kernels::character_boundary",
        "ported/text_kernels::slice_character_count",
        "ported/text_kernels::character_scan_end",
        "ported/text_kernels::len_character_count",
        "ported/text_kernels::truncated",
        "ported/text_kernels::positive",
        "ported/text_kernels::find"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/text_functions.rs#L301-L301",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 47 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.FIND.json",
  "name": "FIND",
  "notes": [],
  "schema": "efh.function-reference/v1",
  "section_order": [
    "what-it-does",
    "try-it",
    "quirks",
    "how-to-implement",
    "version-differences",
    "alternatives"
  ],
  "sections": {
    "alternatives": "\n### Relationships\n\n\n- **`SEARCH`** is the permissive sibling: case-insensitive and wildcard-aware. The pair\n  `FIND`/`SEARCH` is the same distinction as [`EXACT`](FUNC.EXACT.md) versus `=`. Choosing\n  wrongly between them is the most common defect in text-parsing formulas, because both return\n  plausible numbers.\n- **[`FINDB`](FUNC.FINDB.md)** is the byte-counting variant, documented in the same article;\n  its positions are byte positions rather than character positions.\n- **`SEARCHB`** completes the square.\n- **`MID`, [`LEFT`](FUNC.LEFT.md), `RIGHT`** are what the returned position is almost always\n  fed into; the `-1` and `+1` adjustments in those compositions are where `FIND`'s 1-based,\n  absolute result matters.\n- **`TEXTBEFORE`, `TEXTAFTER`, `TEXTSPLIT`** are the modern replacements for the whole\n  `LEFT`/`MID`+`FIND` idiom. They are not deprecating `FIND`, but a formula that only needs the\n  text around a delimiter should not be computing a position at all.\n- **`SUBSTITUTE`** and **`REPLACE`** share this entry's implementing module in the reference\n  engine and share the same position conventions.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n1. **1-based, absolute, first-match.** All three are separately observable and all three are\n   easy to get wrong in a port from a 0-based language.\n2. **The empty-needle case needs an explicit rule**, and it should be tested against Excel\n   rather than reasoned about: both \"matches immediately at `start_num`\" and \"errors\" are\n   defensible and the documentation retrieved here settles neither.\n3. **Do not fold, do not normalise, do not treat `*` or `?` as special.** `FIND`'s value is\n   that it is the function which does none of those things.\n4. **Comparison unit must match the slicing family's unit**, or positions returned by `FIND`\n   will not be valid inputs to `MID` on astral text.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n\nThe return kind is `Number`, a positive integer when the search succeeds.\n\n- **The result is absolute, not relative to `start_num`.** `FIND(\"a\",\"banana\",4)` reports the\n  position within the whole string, not the offset from position 4. Every off-by-one bug in\n  occurrence-walking formulas comes from assuming otherwise.\n- **The search is for the *first* occurrence at or after `start_num`.** There is no direction\n  argument and no last-occurrence form; finding the last occurrence is the classic\n  `SUBSTITUTE`-with-a-sentinel idiom.\n- **Case sensitivity is absolute**, including for characters whose case difference is not an\n  ASCII letter pair. Whether Excel's comparison is code-unit equality or something normalising\n  is not stated in the article and has not been checked here — the same open question that\n  [`EXACT`](FUNC.EXACT.md) raises.\n- **Empty `find_text`.** What `FIND(\"\", t)` returns is not stated in the text of the article\n  retrieved for this page. It is one of the two or three most commonly relied-on edge cases in\n  the function, and the Handbook does not assert a value for it.\n- Numbers and logicals convert to text before searching, so `FIND(1, 2140)` searches the\n  rendering `2140` for the rendering `1`.\n\n\n\n### Errors\n\n\n- **`#VALUE!` when `find_text` does not appear in `within_text`** — stated in the article. This\n  is the design decision that shapes every formula built on `FIND`: absence is an error, not a\n  zero and not `#N/A`. Wrapping in `IFERROR`, or switching to `TEXTBEFORE`/`TEXTAFTER` which\n  take an if-not-found argument, is the standard response.\n- Microsoft publishes a dedicated article on correcting `#VALUE!` in `FIND`/`FINDB` and\n  `SEARCH`/`SEARCHB`, which is a fair indication of how often the not-found error surprises\n  people.\n- The conventional expectation is that `start_num` values less than 1, or greater than the\n  length of `within_text`, also produce `#VALUE!`. Those conditions are **not** confirmed in\n  the article text retrieved for this page, and the Handbook does not assert them.\n- Error inputs propagate, and a value kind with no text conversion surfaces `#VALUE!` under the\n  shared coercion rules ([chapter 02](../model/02-coercion-and-lifting.md)).\n\n\n",
    "sources": "- Microsoft, \"FIND, FINDB functions\" —\n  <https://support.microsoft.com/en-us/office/find-function-c7912941-af2a-4bdf-a553-d0d89b0a0628>\n  (the starting-position definition; the SBCS/DBCS split and the \"always counts each character\n  … as 1\" guarantee; `find_text`, `within_text`, `start_num` and the assumed 1; case\n  sensitivity and the absence of wildcards; the `#VALUE!` on not-found). Retrieved for this\n  page through a search result for that article; a direct request for the same URL did not\n  serve it.\n- Microsoft, \"How to correct a #VALUE! error in FIND/FINDB and SEARCH/SEARCHB functions\" —\n  <https://support.microsoft.com/en-us/excel/how-to-correct-a-value-error-in-find-findb-and-search-searchb-functions>\n- Handbook `content/model/02-coercion-and-lifting.md` (to-text conversion, error propagation).\n\n\n",
    "version-differences": "No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.",
    "what-it-does": "\n`FIND` locates one text string inside another and returns the **1-based position** at which the\nfirst occurrence starts, counted from the first character of the containing string. Microsoft's\narticle states it that way: the number of the starting position of `find_text`, counted from\nthe first character of `within_text`.\n\nTwo properties in the article's own words make `FIND` the strict member of the pair: `FIND` is\n**case sensitive** and does **not allow wildcard characters**. It is therefore a literal search\n— no folding, no pattern language, no locale-aware matching. That is the whole of its\nsemantics, and it is exactly why it is the right tool for parsing machine-generated text and\nthe wrong tool for matching what a human typed.\n\nStated as an operation: return the smallest position `p ≥ start_num` such that the characters\nof `within_text` beginning at `p` equal `find_text`, comparing character by character with no\ntransformation of either side. Positions are counted in characters, and the article is explicit\nthat `FIND` \"always counts each character, whether single-byte or double-byte, as 1, no matter\nwhat the default language setting is\" — a guarantee `FINDB` deliberately does not make.\n\n\n\n### Arguments\n\n\n`find_text` — required, the text you want to find.\n\n`within_text` — required, the text containing the text you want to find.\n\n`start_num` — optional, specifying the character at which to start the search. The first\ncharacter in `within_text` is character number 1, and omitting `start_num` is documented as\nassuming 1.\n\nThe argument order is the position readers get wrong most often, and it is worth stating\nplainly: the *needle comes first*. `FIND(within, needle)` is a silent bug, not an error,\nwhenever the two strings happen to contain each other.\n\n`start_num` has two ordinary uses that are worth separating: skipping a known prefix, and\nfinding the *second* occurrence by feeding it the position of the first plus one. The second\nuse is the reason the argument exists.\n\n\n"
  },
  "signature": {
    "display": "FIND(find_text, within_text, [start_num])",
    "parameters": [
      {
        "description": null,
        "name": "find_text",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "within_text",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "start_num",
        "optional": true,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 3,
        "min": 2
      },
      "category": "Text functions",
      "classification": {
        "arg_preparation_profile": "ValuesOnlyPreAdapter",
        "coercion_lift_profile": "None",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "None",
        "fec_dependency_profile": "None",
        "host_interaction_class": "None",
        "kernel_signature_class": "Custom",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": "Position of one text inside another, case-sensitive",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Finds one text value within another (case-sensitive)",
        "microsoft_url": "https://support.microsoft.com/en-us/office/find-function-c7912941-af2a-4bdf-a553-d0d89b0a0628"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "FIND"
        },
        {
          "locale": "bg-BG",
          "name": "FIND"
        },
        {
          "locale": "cs-CZ",
          "name": "NAJÍT"
        },
        {
          "locale": "da-DK",
          "name": "FIND"
        },
        {
          "locale": "de-DE",
          "name": "FINDEN"
        },
        {
          "locale": "el-GR",
          "name": "FIND"
        },
        {
          "locale": "en-AU",
          "name": "FIND"
        },
        {
          "locale": "en-GB",
          "name": "FIND"
        },
        {
          "locale": "en-US",
          "name": "FIND"
        },
        {
          "locale": "es-ES",
          "name": "ENCONTRAR"
        },
        {
          "locale": "et-EE",
          "name": "FIND"
        },
        {
          "locale": "fi-FI",
          "name": "ETSI"
        },
        {
          "locale": "fr-FR",
          "name": "TROUVE"
        },
        {
          "locale": "he-IL",
          "name": "FIND‏"
        },
        {
          "locale": "hr-HR",
          "name": "FIND"
        },
        {
          "locale": "hu-HU",
          "name": "SZÖVEG.TALÁL"
        },
        {
          "locale": "id-ID",
          "name": "FIND"
        },
        {
          "locale": "it-IT",
          "name": "TROVA"
        },
        {
          "locale": "ja-JP",
          "name": "FIND"
        },
        {
          "locale": "ko-KR",
          "name": "FIND"
        },
        {
          "locale": "lt-LT",
          "name": "FIND"
        },
        {
          "locale": "lv-LV",
          "name": "FIND"
        },
        {
          "locale": "nb-NO",
          "name": "FINN"
        },
        {
          "locale": "nl-NL",
          "name": "VIND.ALLES"
        },
        {
          "locale": "pl-PL",
          "name": "ZNAJDŹ"
        },
        {
          "locale": "pt-BR",
          "name": "PROCURAR"
        },
        {
          "locale": "pt-PT",
          "name": "LOCALIZAR"
        },
        {
          "locale": "ro-RO",
          "name": "FIND"
        },
        {
          "locale": "ru-RU",
          "name": "НАЙТИ"
        },
        {
          "locale": "sk-SK",
          "name": "FIND"
        },
        {
          "locale": "sl-SI",
          "name": "FIND"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "FIND"
        },
        {
          "locale": "sv-SE",
          "name": "HITTA"
        },
        {
          "locale": "th-TH",
          "name": "FIND"
        },
        {
          "locale": "tr-TR",
          "name": "BUL"
        },
        {
          "locale": "uk-UA",
          "name": "FIND"
        },
        {
          "locale": "vi-VN",
          "name": "Các hàm FIND"
        },
        {
          "locale": "zh-CN",
          "name": "FIND"
        },
        {
          "locale": "zh-HK",
          "name": "FIND"
        },
        {
          "locale": "zh-TW",
          "name": "FIND"
        }
      ],
      "signature": {
        "display": "FIND(find_text, within_text, [start_num])",
        "parameters": [
          {
            "description": null,
            "name": "find_text",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "within_text",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "start_num",
            "optional": true,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "FIND",
      "version_marker": null,
      "xlcall": null
    },
    "microsoft": "https://support.microsoft.com/en-us/office/find-function-c7912941-af2a-4bdf-a553-d0d89b0a0628"
  },
  "try_it": {
    "formula": "=FIND(\"book\",\"handbook\")"
  },
  "url": "https://handbook.dnacalc.com/functions/FIND.html",
  "variants": [
    {
      "code": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/text_functions.rs#L301-L301",
      "difference": "A surrogate pair counts as two characters.",
      "error_bound": null,
      "evidence": [
        {
          "commit": "",
          "locator": "",
          "note": "",
          "path": "https://support.microsoft.com/en-gb/excel/compatibility-versions",
          "repository": ""
        }
      ],
      "key": {
        "cpu": "Any",
        "excel_build_range": "Compatibility Version 1, all Excel versions"
      },
      "kind": "environment",
      "name": "FIND at Compatibility Version 1",
      "onecalc_udf": null,
      "state": "selected",
      "state_summary": "Implemented; selected by the workbook's Compatibility Version."
    },
    {
      "code": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/text_functions.rs#L301-L301",
      "difference": "A surrogate pair counts as one character. Characters combined from several code points still count separately.",
      "error_bound": null,
      "evidence": [
        {
          "commit": "",
          "locator": "",
          "note": "",
          "path": "https://support.microsoft.com/en-gb/excel/compatibility-versions",
          "repository": ""
        }
      ],
      "key": {
        "cpu": "Any",
        "excel_build_range": "Compatibility Version 2, Excel for Microsoft 365"
      },
      "kind": "environment",
      "name": "FIND at Compatibility Version 2",
      "onecalc_udf": null,
      "state": "selected",
      "state_summary": "Implemented; selected by the workbook's Compatibility Version."
    }
  ],
  "versions": []
}