Реализация метода, который избегает одинарных кавычек в строке

Предыстория: в tcsh мы можем использовать одинарные кавычки внутри одинарных кавычек вот так (как сказано здесь):

echo 'It'''s Shell Programming'

Я хочу создать метод, который избегает одинарных кавычек и печатает его в tcsh-скрипте, который я создаю на лету с помощью Java. Это должно делать:

  1. Если есть ' (два символа) в строке, она будет экранирована так: '''.
  2. Если есть ' (один символ без перед ним) в строке, он ускользнет от него, поэтому '''.

Я написал для этого следующий метод:

private static String escapeStr(final String str) {
    String result = "";
    for (int index = 0; index < str.length(); ++index) {
        if (str.charAt(index) == '\') {
            if (index + 1 < str.length() && str.charAt(index + 1) == ''') {
                result += "\'\''";
                index++;
            } else {
                result += str.charAt(index);
            }
        } else if (str.charAt(index) == ''') {
            result += "'\''";
        } else {
            result += str.charAt(index);
        }
    }
    return result;
}

Похоже, он выполняет свою работу, но мне действительно не нравится, как я его реализовал. Трудно читать, так как в нем довольно много символы. Также я делаю index++ внутри цикла, который выглядит как плохой дизайн. Есть ли здесь лучший подход?

Добавлю, что хочу вызвать метод так:

fileWriter.write("echo '" + escapeStr(cmd) + "'n");

Вся суть этой части в том, что при побеге будет напечатан cmd как есть. Я завершаю команду ' поэтому он не будет оценивать такие вещи, как переменные среды. Поэтому, если cmd уже содержит одинарную кавычку, он потерпит неудачу. Мне нужно будет избегать одинарных кавычек и того, что я пытался сделать.

2 ответа
2

Я бы предложил решение, основанное на регулярные выражения так:

import static org.junit.Assert.assertThat;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hamcrest.CoreMatchers;
import org.junit.Test;

public class EscapeQuotesTest {

    Pattern escapedQuotePattern = Pattern.compile("(\\)?(')(')");

    private String escapeString(String input) {
        Matcher escapedQuote = escapedQuotePattern.matcher(input);
        escapedQuote.find();
        Optional<String> escapeChar = Optional.ofNullable(escapedQuote.group(1));
        String result = String.format("%s%s\'%s", escapeChar.orElse(""), escapedQuote.group(2), escapedQuote.group(3));
        return result;
    }

    @Test
    public void preseveLeadingBackslash() {
        String input = "\''";
        String result = escapeString(input);
        assertThat(" The escape char survived", result, CoreMatchers.containsString("\'\''"));
    }

    @Test
    public void noLeadingBackslash() {
        String input = "''";
        String result = escapeString(input);
        assertThat("no escape char at beginning", result, CoreMatchers.containsString("'\''"));
    }
}

  • Его все еще трудно читать, и вам нужно будет добавить try-catch no?

    – vesii

  • @vesii «Все еще трудно читать» да, вы не можете обойти комбинированное экранирование для специальных символов Java Strings и Regular Expression. «вам нужно будет добавить пробную ловушку, нет?» Я написал SSCCE, так что нет, try/catch блок не нужен.

    — Тимоти Тракл

Когда вы найдете ' последовательность, вы справляетесь со всем за один раз. Похоже, это источник ваших index++ проблема. Это также означает, что у вас есть повторяющиеся else пункт там. Небольшим улучшением будет обработка только когда вы встречаетесь ', затем обработайте последующие ' на следующем проходе по петле. Насколько я могу судить, обработка ' на самом деле не зависит от того, какой персонаж был до этого. Итак, вы получите:

private static String escapeStr(final String str) {
    String result = "";
    for (int index = 0; index < str.length(); ++index) {
        if (str.charAt(index) == '\' &&
                (index + 1 < str.length() && str.charAt(index + 1) == ''')) {
            result += "\";
        } else if (str.charAt(index) == ''') {
            result += "'\''";
        } else {
            result += str.charAt(index);
        }
    }
    return result;
}

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

private static String escapeStr(final String str) {
    StringBuilder result = new StringBuilder();
    for (int index = 0; index < str.length(); ++index) {
        if (str.charAt(index) == '\' &&
                (index + 1 < str.length() && str.charAt(index + 1) == ''')) {
            result.append("\");
        } else if (str.charAt(index) == ''') {
            result.append("'\''");
        } else {
            result.append(str.charAt(index));
        }
    }
    return result.toString();
}

  • Здесь рекомендуется использовать StringBuilder? Если да, то как бы вы это сделали?

    – vesii

  • @vesii Я добавил версию StringBuilder. Как правило, StringBuilder работает быстрее, когда вы создаете большие строки, бит за битом, потому что он оптимизирует множество перераспределений, которые требуются неизменяемым Strings. Когда вы выполняете небольшое число, нет большой разницы, и иногда использование строк будет быстрее, но в целом, если вы зацикливаете, я бы ожидал StringBuilder (но вы лучше представляете, насколько велики сценарии и действительно ли это будет иметь значение). StringBuilderНекоторым людям может быть труднее читать, чем обычно String конкатенация.

    — оборона

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *