Чистка адресных данных линии послал курьеров к API


Я разработал клиентская библиотека для интеграции с API и доставка курьерами. Курьер принимает только ограниченный набор символов для адреса, поэтому я создал вспомогательный класс, чтобы вырезать недопустимые символы и при необходимости заменить их (с например ?).

public static class StringCleaner
{
    private static readonly char[] ValidCharacters =
    {
        ' ', '#', '&', '\'', '(', ')', '+', ',', '-', '.', '/',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        ':', '?', '@',
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        '[', ']', '_', '`',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        '{', '|', '}', '~',
    };

    public static string CleanString(string stringToClean, string replacement = "")
    {
        int length = stringToClean.Length;
        var newString = new StringBuilder();
        for (var i = 0; i < length; i++)
        {
            if (ValidCharacters.Contains(stringToClean[i]))
            {
                newString.Append(stringToClean[i]);
            }
            else
            {
                newString.Append(replacement);
            }
        }

        return newString.ToString();
    }
}


931
9
задан 29 марта 2018 в 03:03 Источник Поделиться
Комментарии
4 ответа

Сначала я думал об именах. Не Clean() достаточно для метод внутри класса по имени StringCleaner? Вам нужно повторить String?

Еще в прототипе, вы должны учесть, что ты переборе char но replacement это string. Это может быть (или нет) что-то рассматривать, это зависит от ваших требований.

Подробнее о char и stringты переборе char (который на UTF-16 код блока). UTF-16 не фиксированный размер, тогда вы нарушаете для кодовых точек , которые не кодируются как один код блока. Подробнее о Юникоде: не каждый персонаж - это один код точки: ты разбиваешь на суррогаты. Строка может быть (или нет) нормализуется, вы должны заботиться о нем? Одна графема может быть представлена с помощью двух или более кодовых точек: подумайте, например, вьетнамский характер. Вы можете прочитать , Как я могу выполнить Юникод посимвольное сравнение?

Теперь давайте предположим, что для перебора char это прекрасно. Если нет, то читать почти готов к использованию пример разбиения строки на куски одинаковой длины.

В .Чистые строки являются неизменными, то значение String.Length в цикле-это прекрасно, и компилятор может оптимизировать его. Вы уже знаете, максимальная длина строки, тогда вы можете выделить на StringBuilder буфера максимальной вместимости:

var newString = new StringBuilder(stringToClean.Length);

foreach поможет сделать код короче и легче следовать:

foreach (var c in stringToClean)
{
if (ValidCharacters.Contains(c))
{
newString.Append(c);
}
else
{
newString.Append(replacement);
}
}

Если replacement может быть char это даже проще:

foreach (var c in stringToClean)
newString.Append(ValidCharacters.Contains(c) ? c : replacement);

Возможно, вы захотите увидеть в LINQ версии (в случае, если вы найдете его более читабельным):

new String(stringToClean.Select(c => ValidCharacters.Contains(c) ? c : replacement));

Производительности

В общем, вы должны быть осторожны с параметрами со значением по умолчанию, в этом случае также из-за производительности. Если добавить перегрузку для общего дела, то вы можете написать:

public static string CleanString(string stringToClean)
=> new String(stringToClean.Where(ValidCharacters.Contains(c)));

Поиск каждого символа внутри массива-это что-то ужасно медленно. Первый подход: HashSet<char>. Построить список и поиск в хэш-коллекции. Это намного быстрей.

Мы говорим об истинных/ложных ценностей, и все допустимые символы внутри 7 бит ASCII диапазон...если производительность действительно имеет значение, вы можете использовать BitVector. К сожалению, по умолчанию реализации бросков ArgumentOutOfRangeException если вы задаете значение за пределами назначенного диапазона; если вы не хотите, чтобы создать вектор 2^16/8 байт, то вы можете написать собственную реализацию (см., например, считать предметы, существующие в 2 списков для быстрого и грязные сравнения).

9
ответ дан 29 марта 2018 в 03:03 Источник Поделиться

Использовать для поиска HashSet за O(1)
Строки будут перечислять персонажей

private static readonly HashSet<char> ValidCharacters = new HashSet<char> 
{ ' ', '#', '&', '\'', '(', ')', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
':', '?', '@',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'[', ']', '_', '`',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'{', '|', '}', '~',
};

public static string CleanString(string stringToClean, string replacement = "")
{
var newString = new StringBuilder();
foreach (char c in stringToClean)
{
if (ValidCharacters.Contains(c))
{
newString.Append(c);
}
else
{
newString.Append(replacement);
}
}
return newString.ToString();
}

6
ответ дан 29 марта 2018 в 03:03 Источник Поделиться

Я не могу научить вас что-нибудь о кодирование, как @Адриано Репетти делал в своей великой ответов, но, возможно, я смогу спасти вас от более печатать...

Вам не придется создавать char[] себя, stringс массивами так пусть компилятор объединить их и инициализировать коллекцию string:

new HashSet<char>(
" #&" +
"01234"
);

6
ответ дан 29 марта 2018 в 04:03 Источник Поделиться

Я бы использовал Regex для этого.

public static class StringCleaner
{
private const Pattern = @"[^a-zA-Z0-9 \#\\\'\(\)\+\,\-\.\/\[\]_`\{\|\}\~\:\?@]";

public static string Clean(string address, string replacement = "") =>
Regex.Replace(address, Pattern, replacement);
}

3
ответ дан 29 марта 2018 в 07:03 Источник Поделиться