разделить строку только в первом экземпляре указанного символа

192

В моем коде я разделил строку на основе _ и захватил второй элемент в массиве.

var element = $(this).attr('class');
var field = element.split('_')[1];

Принимает good_luck и предоставляет мне luck. Отлично работает!

Но теперь у меня есть класс, который выглядит как good_luck_buddy. Как мне заставить javascript игнорировать второй _ и дать мне luck_buddy?

Я нашел этот var field = element.split(new char [] {'_'}, 2); в ответе stackoverflow С#, но он не работает. Я попробовал это в jsFiddle...

Теги:

13 ответов

279
Лучший ответ

Используйте скобки для скобок:

"good_luck_buddy".split(/_(.+)/)[1]
"luck_buddy"

Они определяются как

Если separator содержит скользящие круглые скобки, согласованные результаты возвращается в массив.

Итак, в этом случае мы хотим разбить на _.+ (т.е. разделительный разделитель является подстрокой, начиная с _) , но также пусть результат содержит некоторую часть нашего разделителя (т.е. все после _).

В этом примере наш разделитель (совпадение _(.+)) равен _luck_buddy, а захваченная группа (внутри разделителя) равна lucky_buddy. Без скользящей скобки luck_buddy (совпадение .+) не включалось бы в массив результатов, как в случае с простым split, чтобы разделители не были включены в результат.

  • 21
    Вам даже не нужно (?), Просто используйте /_(.+)/, чтобы захватить еще 1 символов после первого _
  • 3
    Очень элегантно. Работает как шарм. Спасибо.
Показать ещё 18 комментариев
196

Для чего вам нужны регулярные выражения и массивы?

myString = myString.substring(myString.indexOf('_')+1)

var myString= "hello_there_how_are_you"
myString = myString.substring(myString.indexOf('_')+1)
console.log(myString)
  • 5
    строка! == строка JavaScript чувствителен к регистру.
  • 3
    Я думаю, что это лучший ответ. также можно получить строку после второго _ , написав: myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
Показать ещё 3 комментария
22

Я избегаю RegExp любой ценой. Вот еще одна вещь, которую вы можете сделать:

"good_luck_buddy".split('_').slice(1).join('_')
  • 0
    Это действительно неэффективно. Вы разбиваете всю строку на части, удаляете один элемент, затем соединяете их все снова. Это нормально для маленьких строк, но не делайте этого для больших.
  • 1
    @bfontaine - да, я согласен. Для действительно длинных строк мне придется просто смириться с этим и использовать RegExp.
Показать ещё 7 комментариев
10

Замените первый экземпляр уникальным заполнителем, а затем отделите его.

"good_luck_buddy".replace(/\_/,'&').split('&')

["good","luck_buddy"]

Это более полезно, когда нужны обе стороны разделения.

  • 1
    Это накладывает ненужные ограничения на строку.
  • 0
    Этот ответ работал для меня, когда все вышеперечисленные ответы не.
Показать ещё 3 комментария
5

Вы можете использовать регулярное выражение, например:

var arr = element.split(/_(.*)/)
Вы можете использовать второй параметр, который определяет предел разделения. то есть:   var field = element.split('_', 1) [1]; Дел >
  • 5
    Это только указывает, сколько из разделенных элементов возвращается, а не сколько раз оно разделяется. 'good_luck_buddy'.split('_', 1); возвращается просто ['good']
  • 0
    Спасибо сделал предположение об этом. Обновлен пост, чтобы использовать регулярное выражение.
Показать ещё 4 комментария
3

Javascript String.split, к сожалению, не имеет возможности ограничить фактическое количество разделов. Он имеет второй аргумент, указывающий, сколько из фактических разделенных элементов возвращается, что не полезно в вашем случае. Решение состояло бы в том, чтобы разбить строку, перенести первый элемент и затем снова присоединиться к остальным элементам::

var element = $(this).attr('class');
var parts = element.split('_');

parts.shift(); // removes the first item from the array
var field = parts.join('_');
  • 0
    Я вижу, что функция split не помогает, но использование регулярных выражений, похоже, позволяет добиться этого. Следует указать, что вы имеете в виду саму функцию Split, изначально.
  • 1
    Интересно, что это решение сводит проблему к более читаемому / управляемому решению. В моем случае преобразования полного имени в первое и последнее (да, наши требования заставили эту логику) это решение работало лучше и было более читабельным, чем другие. Спасибо
0

Самое быстрое решение?

Я провел несколько тестов, и это решение выиграло: 1

str.slice(str.indexOf(delim) + delim.length)

// as function
function gobbleStart(str, delim) {
    return str.slice(str.indexOf(delim) + delim.length);
}

// as polyfill
String.prototype.gobbleStart = function(delim) {
    return this.slice(this.indexOf(delim) + delim.length);
};

Сравнение производительности с другими решениями

Единственным близким соперником была та же строка кода, за исключением использования substr вместо split.

Другие решения, которые я пробовал с использованием split или RegExp сильно пострадали и были примерно на 2 порядка медленнее. Использование join по результатам split, конечно, добавляет дополнительное снижение производительности.

Почему они медленнее? Каждый раз, когда нужно создать новый объект или массив, JS должен запросить кусок памяти у ОС. Этот процесс очень медленный.

Вот некоторые общие рекомендации, если вы гоняетесь за тестами:

  • Новые динамические выделения памяти для объектов {} или массивов [] (наподобие того, который создает split) будут стоить очень дорого.
  • Поиск RegExp более сложен и поэтому медленнее, чем поиск строк.
  • Если у вас уже есть массив, деструктурирование массивов происходит примерно так же быстро, как и их явная индексация, и выглядит потрясающе.

Удаление за пределы первой инстанции

Здесь решение, которое будет нарезать вплоть до n-го экземпляра. Это не так быстро, но по вопросу OP gobble(element, '_', 1) по-прежнему> в 2 раза быстрее, чем решение RegExp или split и может сделать больше:

/*
'gobble', given a positive, non-zero 'limit', deletes
characters from the beginning of 'haystack' until 'needle' has
been encountered and deleted 'limit' times or no more instances
of 'needle' exist; then it returns what remains. If 'limit' is
zero or negative, delete from the beginning only until '-(limit)'
occurrences or less of 'needle' remain.
*/
function gobble(haystack, needle, limit = 0) {
  let remain = limit;
  if (limit <= 0) { // set remain to count of delim - num to leave
    let i = 0;
    while (i < haystack.length) {
      const found = haystack.indexOf(needle, i);
      if (found === -1) {
        break;
      }
      remain++;
      i = found + needle.length;
    }
  }

  let i = 0;
  while (remain > 0) {
    const found = haystack.indexOf(needle, i);
    if (found === -1) {
      break;
    }
    remain--;
    i = found + needle.length;
  }
  return haystack.slice(i);
}

Согласно приведенному выше определению, gobble('path/to/file.txt', '/') даст имя файла, а gobble('prefix_category_item', '_', 1) удалит префикс, как в первом решении. в этом ответе.


  1. Тесты были выполнены в Chrome 70.0.3538.110 на macOSX 10.14.
  • 0
    Да ладно ... Сейчас 2019 год. Есть ли люди, которые до сих пор используют микробенчмаркинг?
  • 0
    Согласен. Хотя микробенчмаркинг немного интересен, вы должны полагаться на компилятор или транслятор для оптимизации. Кто знает. Мб кто-то читает это, строит компилятор или использует ejs / embedded и не может использовать регулярные выражения. Тем не менее, это выглядит лучше для моего конкретного случая, чем регулярное выражение. (Я бы убрал "самое быстрое решение")
0

Здесь один RegExp, который делает свое дело.

'good_luck_buddy' . split(/^.*?_/)[1] 

Сначала это заставляет матч начинаться с начала с '^'. Затем он соответствует любому количеству символов, которые не являются "_", другими словами, все символы перед первым "_".

'?' означает, что минимальное количество символов, которые соответствуют целому шаблону, совпадает с '. *?' потому что за ним следует '_', который затем включается в совпадение в качестве последнего символа.

Поэтому этот метод split() использует такую подходящую часть, как "разделитель", и удаляет ее из результатов. Таким образом, он удаляет все, вплоть до первого "_", и дает вам остальное как 2-й элемент результата. Первый элемент - это "", представляющий часть перед соответствующей частью. Это "", потому что матч начинается с начала.

Есть и другие RegExps, которые работают так же, как /_(.*)/, данные Чанду в предыдущем ответе.

Преимущество/^.*?_/заключается в том, что вы можете понять, что он делает, не зная особой роли групп захвата, выполняемых с replace().

0

Используйте метод string replace() с регулярным выражением:

document.body.innerHTML = "good_luck_buddy".replace(/.*?_/, "");

Это регулярное выражение соответствует 0 или более символам перед первым _ и самим _. Затем совпадение заменяется пустой строкой.

  • 0
    Часть document.body.innerHTML здесь абсолютно бесполезна.
  • 0
    @ VictorSchröder, как вы ожидаете увидеть вывод фрагмента без document.body.innerHTML ?
Показать ещё 2 комментария
0

Мне нужны две части строки, так что, regex lookbehind поможет мне в этом.

const full_name = 'Maria do Bairro';
const [first_name, last_name] = full_name.split(/(?<=^[^ ]+) /);
console.log(first_name);
console.log(last_name);
0

Это работало для меня в Chrome + FF:

"foo=bar=beer".split(/^[^=]+=/)[1] // "bar=beer"
"foo==".split(/^[^=]+=/)[1] // "="
"foo=".split(/^[^=]+=/)[1] // ""
"foo".split(/^[^=]+=/)[1] // undefined

Если вам также нужен ключ, попробуйте это:

"foo=bar=beer".split(/^([^=]+)=/) // Array [ "", "foo", "bar=beer" ]
"foo==".split(/^([^=]+)=/) // [ "", "foo", "=" ]
"foo=".split(/^([^=]+)=/) // [ "", "foo", "" ]
"foo".split(/^([^=]+)=/) // [ "foo" ]

//[0] = ignored (holds the string when there no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)
0

Для начинающих, подобных мне, которые не привыкли к регулярному выражению, это решение обходного решения работало:

   var field = "Good_Luck_Buddy";
   var newString = field.slice( field.indexOf("_")+1 );

slice() метод извлекает часть строки и возвращает новую строку, а метод indexOf() возвращает позицию первого найденного вхождения указанного значения в строку.

  • 0
    Это не обходной путь, а правильный способ сделать это;)
0

Решение Mark F является удивительным, но оно не поддерживается старыми браузерами. Решение Kennebec является удивительным и поддерживается старыми браузерами, но не поддерживает регулярное выражение.

Итак, если вы ищете решение, которое разбивает вашу строку только один раз, что поддерживается старыми браузерами и поддерживает регулярное выражение, вот мое решение:

String.prototype.splitOnce = function(regex)
{
    var match = this.match(regex);
    if(match)
    {
        var match_i = this.indexOf(match[0]);
        
        return [this.substring(0, match_i),
        this.substring(match_i + match[0].length)];
    }
    else
    { return [this, ""]; }
}

var str = "something/////another thing///again";

alert(str.splitOnce(/\/+/)[1]);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню