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


272

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

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);в ответе ac # stackoverflow, но он не работает. Я попробовал это в jsFiddle ...

Ответы:


408

Используйте захватывающие скобки :

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

Они определены как

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

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

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


21
Вам даже не нужно (?), Просто используйте /_(.+)/ для захвата еще 1 символа после первого _
Марк

3
Очень элегантно. Работает как шарм. Спасибо.
Ofeargall

12
Просто чтобы прояснить, причина, по которой это решение работает, состоит в том, что все после первого _сопоставляется внутри группы захвата и по этой причине добавляется в список токенов.
Алан Мур

28
Кто-нибудь знает, почему я получаю дополнительный пустой строковый элемент с этим: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee

4
@katylavallee - это может помочь: stackoverflow.com/questions/12836062/… Поскольку разделитель есть ": 16:9", после разделителя ничего нет, поэтому в конце создается пустая строка.
Дерек 朕 會 功夫

232

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

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 )
muratgozel

9
Ответ выводит вторую часть строки. Что, если вам тоже нужна первая часть? С var str = "good_luck_buddy", res = str.split(/_(.+)/);вами вы получите все запчасти:console.log(res[0]); console.log(res[1]);
Вс

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Вот и все. Также с поддержкой переменной иглы
Steffan

Это гений!
stuckedoverflow

36

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

"good_luck_buddy".split('_').slice(1).join('_')

18
Тот, кто боится RegExp, никогда не сможет сказать, насколько велик RegExp. Вы должны найти дверь самостоятельно. Оказавшись там, ты никогда не оглянешься назад. Спросите меня снова через несколько лет, и вы скажете мне, как это здорово.
Кристиан Вестербик

3
@yonas Возьми красную таблетку!
frnhr

2
@yonas Да, возьми красную таблетку! Это сделает вашу жизнь быстрее, даже для коротких строк: jsperf.com/split-by-first-colon
Джулиан Ф. Вейнерт,

15
Ха! Я написал этот комментарий 4+ лет назад. Я определенно нахожусь на борту с RegExp сейчас! :)
Йонас

3
@yonas тебе лучше этого не делать. RegExp потрясающий, когда вам это нужно . Здесь не тот случай. Проверьте обновленный тест: jsperf.com/split-by-first-colon/2
metalim

11

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

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

["good","luck_buddy"]

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


3
Это накладывает ненужные ограничения на строку.
Ян Фото

Этот ответ работал для меня, когда все вышеперечисленные ответы не.
GuitarViking

1
@YanFoto вы имеете в виду, используя '&'? Это может быть что угодно.
sebjwallace

2
@sebjwallace Что бы вы ни выбрали, это означает, что у вас не может быть этого символа в строке. Например, "fish & chips_are_great" дает [fish, chips, are_great], я думаю.
Джо

@Joe Вы можете использовать что угодно вместо '&' - это был просто пример. Вы можете заменить первое вхождение _ на ¬, если хотите. Таким образом, «fish & chips_are_great» заменит первое вхождение _ на ¬, чтобы дать «fish & chips¬are_great», а затем разделит на ¬, чтобы получить [«fish & chips», «are_great»]
sebjwallace

8

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

var arr = element.split(/_(.*)/)
Вы можете использовать второй параметр, который определяет предел разделения. то есть: var field = element.split ('_', 1) [1];

6
Это только указывает, сколько из разделенных элементов возвращается, а не сколько раз оно разделяется. 'good_luck_buddy'.split('_', 1);возвращается только['good']
Алекс Видаль

Спасибо сделал предположение об этом. Обновлен пост, чтобы использовать регулярное выражение.
Chandu

Был (:?.*)должен быть не захватив группу? Если так, то так и должно быть (?:.*), но если вы исправите это, вы обнаружите, что оно больше не работает. (:?.*)соответствует необязательному, :за которым следует ноль или более любого символа. Это решение в конечном итоге работает по той же причине, что и @ MarkF: все, что после первого _, добавляется в список токенов, потому что оно было найдено в группе захвата. (Кроме того, gмодификатор не имеет эффекта при использовании в регулярном выражении с разделением.)
Алан Мур

Спасибо, не поняла это. Обновил Regex и попробовал его на пару сценариев ...
Chandu

1
Это не работает в ie8, и я переключаюсь обратно на indexOf и подстроку
Игорь Алексеев

6

Это решение сработало для меня

var str = "good_luck_buddy";
var index = str.indexOf('_');
var arr = [str.slice(0, index), str.slice(index + 1)];

//arr[0] = "good"
//arr[1] = "luck_buddy"

ИЛИ

var str = "good_luck_buddy";
var index = str.indexOf('_');
var [first, second] = [str.slice(0, index), str.slice(index + 1)];

//first = "good"
//second = "luck_buddy"

1
Однако это не работает, если разделитель имеет более 1 символа.
Хайкам

5

В настоящее время String.prototype.splitдействительно позволяет ограничить количество расколов.

str.split([separator[, limit]])

...

лимит необязательно

Неотрицательное целое число, ограничивающее количество разбиений. Если предусмотрено, разбивает строку при каждом появлении указанного разделителя, но останавливается, когда в массив помещаются предельные записи. Любой оставшийся текст вообще не включается в массив.

Массив может содержать меньше записей, чем ограничение, если конец строки достигнут до достижения предела. Если предел равен 0, расщепление не выполняется.

предостережение

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

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C"]

Я надеялся на:

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B_C_D_E"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C_D_E"]

Тоже самое. Похоже, что PHP разделяется на «первое» и «остальное».
BananaAcid

4

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

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

parts.shift(); // removes the first item from the array
var field = parts.join('_');

Я вижу, что функция split не помогает, но использование регулярных выражений, похоже, позволяет этого достичь. Следует указать, что вы имеете в виду саму функцию Split, изначально.
Дэн Ханли

1
Интересно, что это решение сводит проблему к более читаемому / управляемому решению. В моем случае преобразования полного имени в имя и фамилию (да, наши требования требовали такой логики), это решение работало лучше и было более читабельным, чем другие. Спасибо
Sukima

Это больше не так :)
Kraken


3

С помощью задания деструктуризации это может быть более читабельным:

let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')

2

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

Я провел несколько тестов , и это решение выиграло: 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вместо slice.

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

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

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

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

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

Вот решение, которое будет нарезано вплоть до n-го экземпляра. Это не так быстро, но, по мнению ОП, 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.

Да ладно ... Сейчас 2019 год. Есть ли на самом деле люди, которые до сих пор используют микробенчмаркинг?
Виктор Шредер,

Я согласен. Хотя микробенчмаркинг немного интересен, вы должны полагаться на компилятор или транслятор для оптимизации. Кто знает. Мб кто-то читает это, строит компилятор или использует ejs / embedded и не может использовать регулярные выражения. Тем не менее, это выглядит лучше для моего конкретного случая, чем регулярное выражение. (Я бы убрал «самое быстрое решение»)
TamusJRoyce

1

Решение Марка Ф. является замечательным, но оно не поддерживается старыми браузерами. Решение 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]);


1

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

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

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


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

1

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

var result = "good_luck_buddy".replace(/.*?_/, "");
console.log(result);

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


document.body.innerHTMLЧасть здесь совершенно бесполезно.
Виктор Шредер

@ VictorSchröder, как вы ожидаете увидеть вывод фрагмента без document.body.innerHTML?
Джеймс Т

2
document.bodyзависит от наличия DOM и не будет работать в чистой среде JavaScript. console.logдля этого достаточно или просто оставьте результат в переменной для проверки.
Виктор Шредер

@ VictorSchröder Не думаю, что это вызвало бы много путаницы, но я все же отредактировал.
Джеймс Т

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's no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)

0

Вот один RegExp, который делает трюк.

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

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

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.