SSIS - приведение телефонов в порядок #2

В предыдущем посте на эту тему я написал о том, как "кривые" телефоны привести к формату. При этом, если одна строка на входе (в соответствующем поле) содержала список телефонов через запятую, то и на выходе ей соответствовала одна строка со списком телефонов в поле, так же через запятую. Исправлялся только формат.

Разберем более сложный случай - из каждой строки на входе нужно получить на выходе столько строк, сколько там есть телефонов. Например, на входе строка со значением в поле "8 963 1112233, 556 5554; 667 67 67", на выходе должна дать три строки: "+7(963)111-22-33", "+7(495)556-55-54" и "+7(495)667-67-67". Разделитель может быть как запятая, так и точка с запятой.

Для этого вставляем в поток Script Component, где создаем Output для которого свойство SyncronousInputID выставляем в None:

Код пишем по такому образцу:

...
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text;
...
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
    if (!Row.PhonesIn_IsNull)
    {
        if (!Row.PhonesIn_IsNull)
        {
            var countryPref = "7";      // we assume, that all phones are domestic and harcode country code
            var defaultCityPref = "495";      // default city or operator prefix
            var phones = Row.PhonesIn.Replace(';', ',').Split(',');   // phones in string should be separated with commas or semicolons
            var e = phones.GetEnumerator();
            while (e.MoveNext())
            {
                var phone = e.Current.ToString().Trim();
                var r1 = new Regex(@"(.*)(\d).*(\d).*(\d).*(\d).*(\d).*(\d).*(\d)\D*$", RegexOptions.IgnoreCase);  // expression to match phone number
                var r2 = new Regex(@".*(\d{3})\D*$", RegexOptions.IgnoreCase); // expression to match city or operator prefix
                var m = r1.Match(phone);
                if (m.Success)
                {
                    var pref = r2.Match(m.Groups[1].Value).Groups[1].ToString();    // get prefix out
                    pref = (pref.Equals("095")) ? "495" : pref;         // replace old Moscow prefix with new
                    if (pref.Length == 0) pref = defaultCityPref;
                    Output0Buffer.AddRow();
                    Output0Buffer.Phone=
                        (
                        "+" + countryPref +         // phones prefixed with '+' and country code
                        "(" + pref + ")" +          // city or operator prefix taken in curves
                        m.Groups[2].Value +
                        m.Groups[3].Value +
                        m.Groups[4].Value + "-" +
                        m.Groups[5].Value +
                        m.Groups[6].Value + "-" +
                        m.Groups[7].Value +
                        m.Groups[8].Value);
                }
            }
        }
    }
}

Для более подробной информации можно почитать "Creating an Asynchronous Transformation with the Script Component".

О чем ещё следует упомянуть - асинхронные компоненты "вбирают" все данные потока перед тем как их выпустить (а не порциями) - это может быть важно, если у вас много данных.