наиболее эффективный способ удалить символы из строки целых и десятичных чисел - PullRequest
1 голос
/ 21 июня 2019

Я обрабатываю необработанные данные переписи населения США в базе данных SQL Server. Разархивированный файл tar дает чуть более 14 000 CSV-файлов, которые необходимо обработать в 266 различных таблицах базы данных. Мне нужно перебрать каждый CSV-файл и добавить к нему заголовок, чтобы SSIS мог вытянуть необработанные данные в целевую таблицу SQL Server.

Первые 6 столбцов каждого файла CSV абсолютно одинаковы. Остальные столбцы для файла отличаются. Данные в остальных столбцах в основном представляют собой числовые значения (целые и десятичные числа). Тем не менее, Бюро переписей добавляет символы, называемые значениями «jam», показывающие, почему нет значения. Мне нужно заменить эти значения jam на пустую или пустую строку, потому что столбцы таблицы целевой базы данных - DECIMALS, а значения jam приводят к сбою вставки в SSIS.

Итак, у меня есть библиотека классов C # (DotNet Core), зацикливающаяся на 14K-файлы. Для каждого файла я должен сделать следующее:

  1. создать переменную StringBuilder
  2. добавить заголовок строки в StringBuilder, чтобы SSIS работал
  3. цикл над каждой строкой в ​​файле
  4. для каждой строки, я должен разделить первые 6 столбцов, потому что мне нужны эти строки в целевой таблице. Затем я разделяю оставшиеся столбцы, потому что мне нужно удалить значения пробок, оставляя числовые данные
  5. объединить первые 6 столбцов и убрать данные обратно в строку
  6. добавить недавно очищенную строку в StringBuilder
  7. после завершения цикла по всем строкам запишите StringBuilder в целевую папку, где SSIS будет загружаться в базу данных.

У меня есть 3 вложенных цикла:

  1. цикл более 14000 файлов
  2. для каждого файла, цикл по каждой строке
  3. для каждой строки, цикл по столбцам, удаляя символы

Вот мой код для зацикливания каждого файла:

    private static Boolean BuildCensusDataFileWithHeader(String censusDataFilePath, String rowHeader, String censusDataDestinationFilePath)
    {
        try
        {
            // BUILD NEW FILE WITH HEADER
            StringBuilder currentContent = new StringBuilder();
            currentContent.Append(rowHeader + Environment.NewLine);

            //RETRIEVE ALL LINES IN TARGET FILE
            List<String> rawList = File.ReadAllLines(censusDataFilePath).ToList();

            // LOOP THROUGH EACH LINE AND REMOVE ANY STRINGS IN COLUMNS AFTER COLUMN 6
            // NOTE: COLUMNS 1-6 CONTAINS STRINGS NEEDED IN DATABASE
            foreach (var row in rawList)
            {
                //TURN COMMA DELIMITED ROW OF DATA INTO ARRAY
                String[] rowArray = row.Split(",");

                // PEEL OFF FIRST 6 COLUMNS TO BE KEPT AS IS
                IList<String> goodStrings = rowArray.Take(6).ToList();

                // RETRIEVE REMAINING COLUMNS TO BE CLEANED OF STRINGS
                IList<String> stringsToNullList = rowArray.Skip(6).ToList();

                // REMOVE ALL STRINGS
                stringsToNullList.OnlyDecimalValues();

                // PUT GOOD COLUMNS AND CLEANED COLUMNS BACK TOGETHER AS A ROW
                var cleanedRow = $"{String.Join(",", goodStrings)},{String.Join(",", stringsToNullList)}";

                // APPEND ROW TO NEW DOCUMENT TO BE WRITTEN TO TARGET DIRECTORRY CONTAINING CLEANED DATA
                currentContent.Append(cleanedRow + Environment.NewLine);
            }

            File.WriteAllText(censusDataDestinationFilePath, currentContent.ToString());

            return true;
        }
        catch (Exception ee)
        {
            string temp = ee.Message;
            return false;
        }
    }

Вот мои методы расширения, заменяющие символы пустым пространством:

    public static void OnlyDecimalValues(this IList<String> stringToClean)
    {
        for (int i = 0; i < stringToClean.Count; ++i)
        {
            stringToClean[i] = (stringToClean[i].IsDecimal()) ? stringToClean[i] : "";
        }
    }

    public static bool IsDecimal(this string text)
    {
        decimal test;
        return decimal.TryParse(text, out test);
    }

Это все работает через программирование методом грубой силы. Есть ли более эффективный способ сделать это?

Спасибо за ваше время.

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Рекомендую пересмотреть процесс проектирования.Используйте силу sql и ssis в правильном балансе.Используйте ssis, чтобы перебрать все файлы в папке и загрузить строки необработанного текста во вновь созданную необработанную таблицу.Затем используйте SQL-код, чтобы выполнить остальную часть обработки.Вы можете использовать функции charindex или patIndex для разделения необработанных строк, и одно преимущество SQL будет значительным сокращением времени выполнения, поскольку вы будете обрабатывать весь пакет за одну транзакцию для данного файла.

Еще один вероятный потенциал ростабыло бы, что вам, возможно, просто нужно создать одну необработанную таблицу для всех разных файлов с тремя столбцами - id, fileName, rawText.Таким образом, дизайн будет выглядеть примерно так:

Шаги, выполняемые в SSIS

  • Создание переменной StringBuilder.Добавьте заголовок строки в StringBuilder, чтобы SSIS работал в цикле над каждой строкой в ​​файле.

Шаги, выполненные в SQL

  • Разделить первые 6 столбцов, чтобы получить строки в целевой таблице, и разделить оставшиеся столбцы, чтобы удалить значения пробок, оставляячисловые данные с использованием одного оператора выбора с использованием функций patindex или charindex в сочетании с функцией replace для обнуления значений застревания.
0 голосов
/ 21 июня 2019

У меня есть два предложения, чтобы ускорить его. Во-первых, поскольку вы ничего не делаете с результирующим разобранным десятичным значением, вы можете использовать регулярное выражение, чтобы проверить, содержит ли строка только числа. Это быстрее, чем при использовании TryParse. Я использовал секундомер для проверки скорости, и таким образом получаю немного лучшую производительность для «ложных» дел и значительно лучшую производительность для «истинных» дел. Итак, метод IsDecimal стал бы:

private static bool IsDecimal(string text)
{
    var regex = @"^-?(0|[1-9]\d*)(\.\d+)?$";
    return Regex.Match(text, regex).Success;
}

Второе предложение - преобразовать блок if-else в блок if. Итак, эта строка:

stringToClean[i] = (stringToClean[i].IsDecimal()) ? stringToClean[i] : "";

станет таким:

if (!stringToClean[i].IsDecimal())
{
    stringToClean[i] = "";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...