Инициалы из ФИО средствами встроенного языка 1С
Содержание
Чтобы из Иванов Иван Иванович получить Иванов И. И., не нужен ни один внешний компонент — всё решается встроенным языком. Если фамилия, имя и отчество лежат в отдельных реквизитах, достаточно взять по первой букве от имени и отчества через функцию Лев. Если всё ФИО хранится одной строкой, её сначала режут на части функцией СтрРазделить, а потом собирают сокращённое представление.
Главная ошибка новичка — считать, что имя и отчество всегда отделены ровно одним пробелом, а регистр всегда правильный. На практике приходят иванов иван, ИВАНОВ ИВАН, двойные фамилии через дефис и ФИО вообще без отчества. Рабочий код обязан это переживать.
Ниже — готовые фрагменты под оба способа хранения, обработка кривого ввода, получение инициалов прямо в запросе и то, что важно при пересчёте по тысячам физлиц.
Два способа хранения ФИО решают всё
Прежде чем писать код, посмотрите, как ФИО лежит в базе. Отсюда зависит весь алгоритм.
- 📇 Отдельные реквизиты. В типовых ЗУП 3.1 и БП 3.0 у справочника физических лиц есть отдельные поля
Фамилия,Имя,Отчество. Разбирать строку не нужно — буквы берутся напрямую. - 📄 Одна строка. В самодельных справочниках, при импорте из внешних систем и в поле
НаименованиеФИО часто лежит целиком:Иванов Иван Иванович. Такую строку сначала надо разделить на слова.
Определите свой случай — и переходите к соответствующему разделу. Смешанный вариант (есть и строка, и части) сводится к любому из двух.
В типовых конфигурациях представление физлица в списках и печатных формах уже формируется как Фамилия И. О. автоматически. Прежде чем писать код, проверьте — возможно, нужное представление платформа отдаёт сама, и достаточно вывести реквизит-наименование.
Инициалы из отдельных полей Фамилия, Имя, Отчество
Это самый надёжный путь: не нужно угадывать, где кончается фамилия и начинается имя.
Фамилия = СокрЛП(ФизЛицо.Фамилия);
Имя = СокрЛП(ФизЛицо.Имя);
Отчество = СокрЛП(ФизЛицо.Отчество);
Инициалы = Фамилия;
Если ЗначениеЗаполнено(Имя) Тогда
Инициалы = Инициалы + " " + ВРег(Лев(Имя, 1)) + ".";
КонецЕсли;
Если ЗначениеЗаполнено(Отчество) Тогда
Инициалы = Инициалы + " " + ВРег(Лев(Отчество, 1)) + ".";
КонецЕсли;
Разберём по шагам:
СокрЛПубирает пробелы в начале и в конце — частый мусор при импорте.Лев(Имя, 1)возвращает первую букву. Если имя пустое, блок пропускается, и лишней точки.не появится.ВРегподнимает инициал в верхний регистр, даже если имя пришло строчными буквами.ЗначениеЗаполненострахует от сотрудников без отчества — для них вернётсяИванов И.
Результат: Иванов И. И. Если нужен формат без пробела внутри инициалов (Иванов И.И.), уберите " " + перед отчеством.
Нужен обратный порядок — И. И. Иванов? Соберите строку в другой последовательности: сначала инициалы, затем фамилия.
Разбор строки «Фамилия Имя Отчество» через СтрРазделить
Когда ФИО лежит одной строкой, её надо распилить на слова. Для этого есть функция СтрРазделить — она доступна в платформе не ниже 8.3.6.
ПолноеФИО = СокрЛП("Иванов Иван Иванович");
// Третий параметр Ложь выбрасывает пустые элементы:
// подряд идущие пробелы не дадут "пустых слов".
Части = СтрРазделить(ПолноеФИО, " ", Ложь);
Результат = "";
Если Части.Количество() >= 1 Тогда
Результат = Части[0];
КонецЕсли;
Если Части.Количество() >= 2 Тогда
Результат = Результат + " " + ВРег(Лев(Части[1], 1)) + ".";
КонецЕсли;
Если Части.Количество() >= 3 Тогда
Результат = Результат + " " + ВРег(Лев(Части[2], 1)) + ".";
КонецЕсли;
Ключевой момент — третий параметр Ложь у СтрРазделить. Без него строка Иванов Иван (три пробела) даст элементы ["Иванов", "", "", "Иван"], и инициал возьмётся от пустого слова. С Ложь пустые элементы отбрасываются, и Части всегда содержит только реальные слова.
Индексы массива в 1С начинаются с нуля: Части[0] — фамилия, Части[1] — имя, Части[2] — отчество. Проверки Количество() >= N защищают от выхода за границу массива, когда отчества или имени в строке нет.
Регистр, пробелы и аккуратное форматирование
Данные редко приходят идеальными. Вот что чаще всего ломает «наивный» код и как это закрыть.
| Проблема в данных | Что добавить в код |
|---|---|
иванов иван строчными | ВРег(Лев(...)) для инициала; ТРег(Фамилия) — первая буква фамилии заглавной |
| Пробелы по краям и внутри | СокрЛП + СтрРазделить(..., " ", Ложь) |
| Неразрывный пробел из Word | заменить его: СтрЗаменить(ФИО, Символ(160), " ") |
| Разделитель — не пробел, а запятая | СтрРазделить(ФИО, ",", Ложь) |
Функция ТРег (титульный регистр) делает первую букву каждого слова заглавной, остальные — строчными. Для фамилии это удобно: ИВАНОВ → Иванов. Но с двойными фамилиями и приставками (де, оглы) ТРег работает не всегда корректно, поэтому для фамилии его применяют осторожно и проверяют на своих данных.
Саму итоговую строку удобно собирать не плюсами, а функцией СтрШаблон — так виден формат целиком и легче переключаться между вариантами вывода:
// Фамилия И. О.
Результат = СтрШаблон("%1 %2. %3.", Фамилия, ВРег(Лев(Имя, 1)), ВРег(Лев(Отчество, 1)));
// И. О. Фамилия
Результат = СтрШаблон("%1. %2. %3", ВРег(Лев(Имя, 1)), ВРег(Лев(Отчество, 1)), Фамилия);
СтрШаблон подставляет аргументы на места %1, %2, %3. Минус у такого шаблона один: он не умеет сам пропускать пустое отчество, поэтому СтрШаблон берут, когда отчество точно заполнено, а для данных с пропусками оставляют сборку через Если из примеров выше.
Символ неразрывного пробела (Символ(160)) визуально неотличим от обычного, но СтрРазделить по обычному пробелу его не поймает. Если инициалы «съезжают» на вид правильных данных — ищите именно его.
Получение инициалов в запросе и в СКД
Иногда инициалы нужны не в коде, а прямо в запросе — например, для колонки отчёта на СКД. В языке запросов нет СтрРазделить, но есть функция ПОДСТРОКА, которая работает только с отдельными полями ФИО.
ВЫБРАТЬ
ФизическиеЛица.Фамилия
+ " "
+ ПОДСТРОКА(ФизическиеЛица.Имя, 1, 1) + "."
+ " "
+ ПОДСТРОКА(ФизическиеЛица.Отчество, 1, 1) + "." КАК ФИОКратко
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ПОДСТРОКА(Поле, 1, 1) берёт один символ начиная с первого — это аналог Лев(Поле, 1) в коде.
Важное ограничение: в запросе нельзя разобрать ФИО, лежащее одной строкой — функции деления строки на слова там нет. Если ФИО в базе хранится цельной строкой, а инициалы нужны в отчёте, разбирайте их в коде (например, в вычисляемом поле СКД или в обработчике) и передавайте в запрос готовыми, либо храните Фамилию/Имя/Отчество отдельными полями.
Для имени или отчества, которых может не быть, добавьте в запрос ВЫБОР КОГДА ... ЕСТЬ NULL или проверку на пустую строку, иначе получите Иванов . . с «висячими» точками.
Нестандартные ФИО и защита от ошибок
Код, который падает или выдаёт мусор на первом же нетипичном ФИО, в продакшене не живёт. Разберём случаи, которые реально встречаются.
- 🔸 Нет отчества. У иностранцев и во многих анкетах отчества нет. Проверка
ЗначениеЗаполнено/Количество() >= 3обязательна, иначе получите лишнюю точку. - 🔸 Двойная фамилия через дефис.
Мамин-Сибиряк— это одно слово (дефис не пробел),СтрРазделитьпо пробелу его не разорвёт. Инициал имени возьмётся правильно. - 🔸 Двойное имя через дефис.
Анна-Мария→ инициалА.Это корректно: берём первую букву всего имени. - 🔸 Пустая строка.
СтрРазделить("", " ", Ложь)вернёт пустой массив,Количество()= 0, результат — пустая строка. Падения не будет. - 🔸 Четыре и более слов. Длинные составные ФИО или мусор вроде должности в поле: лишние элементы массива просто игнорируются — мы берём только
[0],[1],[2]. - 🔸 Фамилия из двух слов через пробел.
ван дер Берг,де ла Круз: разбор по пробелу «разорвёт» такую фамилию и примет вторую часть за имя. Автоматически это не лечится — для подобных записей надёжнее хранить Фамилию/Имя/Отчество отдельными полями, а не одной строкой.
Чтобы не держать все проверки в голове, оформите разбор отдельной функцией и прогоняйте через неё все ФИО. Так логика живёт в одном месте, а не копируется по модулям. Готовая функция под отдельные поля:
Функция СформироватьИнициалы(Фамилия, Имя, Отчество) Экспорт
Ф = СокрЛП(Фамилия);
И = СокрЛП(Имя);
О = СокрЛП(Отчество);
Результат = Ф;
Если ЗначениеЗаполнено(И) Тогда
Результат = Результат + " " + ВРег(Лев(И, 1)) + ".";
КонецЕсли;
Если ЗначениеЗаполнено(О) Тогда
Результат = Результат + " " + ВРег(Лев(О, 1)) + ".";
КонецЕсли;
Возврат СокрЛП(Результат);
КонецФункции
Эта же функция принимает на вход и результат разбора цельной строки: сначала получаете Части через СтрРазделить, затем передаёте Части[0], Части[1], Части[2] (с проверкой количества) в СформироватьИнициалы. Весь код разбора сходится в одну точку.
Если платформа старше 8.3.6 и СтрРазделить недоступна, слова из строки вынимают вручную: СтрНайти ищет позицию пробела, Лев и Сред отрезают фамилию и остаток. Это длиннее и ошибочнее, поэтому на актуальных релизах используют СтрРазделить.
Массовый пересчёт инициалов без тормозов
Разобрать одно ФИО — миллисекунды. Проблемы начинаются, когда нужно проставить инициалы по десяткам тысяч физлиц или сотрудников.
Главное правило — не делайте запрос в цикле. Сначала одним запросом выберите все нужные поля, затем обходите выборку в памяти:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизЛица.Ссылка КАК Ссылка,
| ФизЛица.Фамилия КАК Фамилия,
| ФизЛица.Имя КАК Имя,
| ФизЛица.Отчество КАК Отчество
|ИЗ
| Справочник.ФизическиеЛица КАК ФизЛица";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Инициалы = СформироватьИнициалы(Выборка.Фамилия, Выборка.Имя, Выборка.Отчество);
// ... использование результата
КонецЦикла;
Если инициалы нужно именно записать в реквизит, помните:
- Не вызывайте
Записать()на каждом объекте в одной длинной транзакции без разбивки — на больших объёмах это раздувает журнал и держит блокировки. Пишите порциями и фиксируйте транзакцию по частям. - Тяжёлый пересчёт запускайте фоновым заданием, а не в клиентском сеансе, чтобы не упереться в таймаут.
- Запись объекта проводит обработчики
ПередЗаписью/ПриЗаписи— на массовой правке это может быть дороже самого разбора строки. Оцените, нужна ли запись вообще.
Во многих задачах хранить инициалы отдельно не нужно: дешевле считать их на лету при выводе (в печатной форме, в отчёте), чем держать ещё один реквизит и следить за его актуальностью при смене фамилии.
Частые вопросы
Есть ли в платформе готовая функция для инициалов?
Фамилия И. О. автоматически, и часто достаточно вывести это представление, не изобретая код.Чем `СтрРазделить` лучше старого `Найти` и `Сред`?
СтрРазделить сразу отдаёт массив слов и с параметром Ложь сам отбрасывает пустые элементы от лишних пробелов. Ручной разбор через поиск позиции пробела и Сред работает, но длиннее и легче ошибиться на краевых случаях.Как получить инициалы в формате `И. О. Фамилия`?
Почему в результате появляется лишняя точка, например `Иванов И. .`?
ЗначениеЗаполнено (для полей) или Количество() >= 3 (для разобранной строки).Можно ли разобрать ФИО-строку прямо в запросе?
ПОДСТРОКА работает только с уже раздельными полями Фамилия/Имя/Отчество.