Есть ли способ сопоставить это с Regex вместо цикла? - PullRequest
1 голос
/ 28 июня 2019

У меня есть эта функция, которая подсчитывает фигурные скобки вне кавычек, игнорируя их внутри: (передать строку и '{' или '}' в зависимости от моего использования)

public static int countCurlyBraces(String s, char c) {
    int count = 0;
    for (char cr : s.toCharArray()) {
        if (cr == '"')
            if (stack.isEmpty())
                stack.push(cr);
            else
                stack.pop();

            if (stack.size() == 1 && cr == c)
                count++;
    }
    return StringUtil.countMatches(s, c) - count;
}

Я пытаюсь заменить это регулярным выражением, но у меня есть небольшая проблема, возможно ли это вообще?

public static int countCurlyBraces(String s, char c) {
    Matcher a = Pattern.compile("\"(.*?)[" + c + "](.*?)\"").matcher(s);
    int count = 0;

    while (a.find()) 
        count++;

    return StringUtil.countMatches(s, c) - count;
}

Пример строки, которую я использую для тестирования:

sdfg "srfg {rmjy #" rmyrmy {rymundh "ecfvr {cerv #" fes {dc "cf2234TC @ $ # ct234" etw243T @ # $ c "nhg

Это должно вернуть счет 2,игнорируя две фигурные скобки, заключенные в кавычки. Выражение Regex видит все фигурные скобки, содержащиеся в кавычках, и выдает 0.

документ выглядит следующим образом:

LOCALE
user="XXXXXXX" time=1561234682/* "26-Jun-2019 23:00:03" */
{
  LOCALE="XXXXXXX"
}
SITE NAME="XxxXXxxx"
 user="XXXXXX" time=1568532503/* "26-Jun-2019 23:00:03" */
{
  SYSTEM_NAME="XXX-NNNNN"
  SYSTEM_IDENTIFIER="{XXXX-XXXX-XXX_XXX-XX}"
  SYSTEM_ID=NNNNN
  SYSTEM_ZONE_NAME="XXXXXX"
  DEFAULT_COMMUNICATION_TYPE=REDUNDANT
  IP_ADDR_AUTO_GEN=T
  PP_LAD="aGx{4"
  PVQ_LIMIT=0.5
  BCK_LIMIT=0.3
  MNN_LIMIT=0.1
  COMPANY_NAME=""
  DISPLAY_VERSION_CONTROL_ENABLED=F
}

Ответы [ 2 ]

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

Ваш метод является очень окольным способом достижения того, что вы хотите, и он довольно неэффективен.

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

Во-вторых, используя s.toCharArray(), вы по существу держите дубликаты данных и удваиваете объем памяти вашей строки;Вместо этого просто получите доступ к своим данным через charAt.

В-третьих, использование стека для отслеживания того, находитесь ли вы внутри кавычек, не нужно;Вместо этого просто переверните логическое значение.

Вот мои заметки о вашем методе:

public static int countCurlyBraces(String s, char c) {
    Deque<Character> stack = ...; // I'm assuming 'stack' is some kind of Deque
    int count = 0;
    // doubling memory usage of the string by copying the chars into another array with 's.toCharArray()'
    // for each character in that string...
    for (char cr : s.toCharArray()) {
        // using a stack to keep track if you are inside quotes? just flip a boolean instead
        if (cr == '"')
            if (stack.isEmpty())
                stack.push(cr);
            else
                stack.pop();

        // if inside quotes and the character matches the target, then count it..
        // I thought you wanted to count the characters outside the quotes?
        if (stack.size() == 1 && cr == c)
            count++;
    }

    // iterate through the whole string again and count ALL the characters
    // then subtract the number inside the strings from the total to get the number outside strings
    return StringUtil.countMatches(s, c) - count;
}

Вместо этого вы можете просто сделать что-то подобное, что гораздо эффективнее:

public static int countCharacterOutsideQuotes(CharSequence chars, char targetChar) {
    int count = 0;
    boolean isQuoted = false;
    // using `charAt` avoids doubling memory usage of copying all the chars into another array
    for (int i = 0; i < chars.length(); i++) {
        char c = chars.charAt(i);
        if (c == '"') {
            // found a quote, flip from not quoted to quoted or vice versa.
            isQuoted = !isQuoted;
        } else if (c == targetChar && !isQuoted) {
            // found the target character, if it's not inside quotes then count it
            count++;
        }
    }
    return count;
}

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

public static int countCharacterOutsideQuotes(Reader reader, char targetChar) throws IOException {
    Objects.requireNonNull(reader);
    int count = 0;
    boolean isQuoted = false;
    // using `charAt` avoids doubling memory usage of copying all the chars into another array
    for (int c = reader.read(); c != -1; c = reader.read()) {
        if (c == '"') {
            // found a quote, flip from not quoted to quoted or vice versa.
            isQuoted = !isQuoted;
        } else if (c == targetChar && !isQuoted) {
            // found the target character, if it's not inside quotes then count it
            count++;
        }
    }
    return count;
}

public static void main(String[] args) {
    // try (Reader reader = new InputStreamReader(new StringReader("your-test-string-goes-here"));) {
    try (Reader reader = new InputStreamReader(new FileInputStream("/path/to/file.txt"));) {
        System.out.println(countCharacterOutsideQuotes(reader, '{'));
    } catch (IOException e) {
        e.printStackTrace();
    }
}
0 голосов
/ 28 июня 2019

Цикл может быть более эффективным ЦП. Но здесь я бы пошел на 2 стадии регулярного выражения:

String input="sdfg\"srfg{rmjy#\"rmyrmy{rymundh\"ecfvr{cerv#\"fes{dc\"cf2234TC@$#ct234\"etw243T@#$c\"nhg";


input=input.replaceAll("\"[^\"]*\"", ""); // becomes sdfgrmyrmy{rymundhfes{dcetw243T@#$c"nhg

input=input.replaceAll("[^{]", ""); //becomes {{

return input.length();//2

второе регулярное выражение может использовать фактический переданный символ (если вы ограничите его {и}, оно должно работать.

input=input.replaceAll("[^"+c+"]", "");

и если мы объединим оба регулярных выражения, оно станет менее читабельным, но одна строка

input=input.replaceAll("\"[^\"]*\"|[^"+c+"]", "");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...