String.prototype.charAt()

Сводка

Метод charAt() возвращает указанный символ из строки.

Синтаксис

str.charAt(index)

Параметры

index

Целое число от 0 до длины строки минус 1.

Описание

Символы в строке идут слева направо. Индекс первого символа равен 0, а последнего символа в строке stringName равен stringName.length - 1. Если предоставленный вами параметр index выходит за пределы этого диапазона, JavaScript вернёт пустую строку.

Примеры

Пример: отображение символов из различных позиций строки

Следующий пример показывает символы в различных позициях в строке "Дивный новый мир":

js
var anyString = "Дивный новый мир";

console.log("Символ по индексу 0   равен '" + anyString.charAt(0) + "'");
console.log("Символ по индексу 1   равен '" + anyString.charAt(1) + "'");
console.log("Символ по индексу 2   равен '" + anyString.charAt(2) + "'");
console.log("Символ по индексу 3   равен '" + anyString.charAt(3) + "'");
console.log("Символ по индексу 4   равен '" + anyString.charAt(4) + "'");
console.log("Символ по индексу 5   равен '" + anyString.charAt(5) + "'");
console.log("Символ по индексу 999 равен '" + anyString.charAt(999) + "'");

Этот код отобразит следующее:

js
Символ по индексу 0   равен 'Д'
Символ по индексу 1   равен 'и'
Символ по индексу 2   равен 'в'
Символ по индексу 3   равен 'н'
Символ по индексу 4   равен 'ы'
Символ по индексу 5   равен 'й'
Символ по индексу 999 равен ''

Пример: получение целых символов

Следующий пример показывает, как обойти строку в цикле, каждый раз гарантированно получая целый символ, даже если строка содержит символы, не помещающиеся на Базовую многоязыковую плоскость (БМП).

js
var str = "A \uD87E\uDC04 Z"; // Также можно использовать не-БМП символы напрямую
for (var i = 0, chr; i < str.length; i++) {
  if ((chr = getWholeChar(str, i)) === false) {
    continue;
  }
  // Поместите эти строки в самое начало каждого цикла, передавая в функцию строку
  // и текущую итерацию; возвращаемая переменная будут представлять
  // отдельный символ

  console.log(chr);
}

function getWholeChar(str, i) {
  var code = str.charCodeAt(i);

  if (isNaN(code)) {
    return ""; // Позиция не найдена
  }
  if (code < 0xd800 || code > 0xdfff) {
    return str.charAt(i);
  }

  // Старшая часть суррогатной пары (последнее число можно изменить на 0xDB7F,
  // чтобы трактовать старшую часть суррогатной пары в частной плоскости как
  // одиночный символ)
  if (0xd800 <= code && code <= 0xdbff) {
    if (str.length <= i + 1) {
      throw "Старшая часть суррогатной пары без следующей младшей";
    }
    var next = str.charCodeAt(i + 1);
    if (0xdc00 > next || next > 0xdfff) {
      throw "Старшая часть суррогатной пары без следующей младшей";
    }
    return str.charAt(i) + str.charAt(i + 1);
  }
  // Младшая часть суррогатной пары (0xDC00 <= code && code <= 0xDFFF)
  if (i === 0) {
    throw "Младшая часть суррогатной пары без предшествующей старшей";
  }
  var prev = str.charCodeAt(i - 1);

  // (последнее число можно изменить на 0xDB7F, чтобы трактовать старшую
  // часть суррогатной пары в частной плоскости как одиночный символ)
  if (0xd800 > prev || prev > 0xdbff) {
    throw "Младшая часть суррогатной пары без предшествующей старшей";
  }
  // Теперь мы можем пропустить младшую часть суррогатной пары,
  // которую мы уже обработали
  return false;
}

В среде, поддерживающей JavaScript 1.7+ (например, в Firefox), который позволяет деструктурирующее присваивание, можно использовать более лаконичную и более гибкую альтернативу в том смысле, что она автоматически увеличивает счётчик (если символ гарантированно является суррогатной парой).

js
var str = "A\uD87E\uDC04Z"; // Также можно использовать не-БМП символы напрямую
for (var i = 0, chr; i < str.length; i++) {
  [chr, i] = getWholeCharAndI(str, i);
  // Поместите эту строку в самое начало каждого цикла, передавая в функцию строку
  // и текущую итерацию; возвращаемый массив будет содержать отдельный символ и
  // новое значение счётчика цикла 'i' (изменится только при встрече суррогатной пары)

  console.log(chr);
}

function getWholeCharAndI(str, i) {
  var code = str.charCodeAt(i);

  if (isNaN(code)) {
    return ""; // Позиция не найдена
  }
  if (code < 0xd800 || code > 0xdfff) {
    return [str.charAt(i), i]; // Обычный символ, оставляем переменную 'i' неизменной
  }

  // Старшая часть суррогатной пары (последнее число можно изменить на 0xDB7F,
  // чтобы трактовать старшую часть суррогатной пары в частной плоскости как
  // одиночный символ)
  if (0xd800 <= code && code <= 0xdbff) {
    if (str.length <= i + 1) {
      throw "Старшая часть суррогатной пары без следующей младшей";
    }
    var next = str.charCodeAt(i + 1);
    if (0xdc00 > next || next > 0xdfff) {
      throw "Старшая часть суррогатной пары без следующей младшей";
    }
    return [str.charAt(i) + str.charAt(i + 1), i + 1];
  }
  // Младшая часть суррогатной пары (0xDC00 <= code && code <= 0xDFFF)
  if (i === 0) {
    throw "Младшая часть суррогатной пары без предшествующей старшей";
  }
  var prev = str.charCodeAt(i - 1);

  // (последнее число можно изменить на 0xDB7F, чтобы трактовать старшую
  // часть суррогатной пары в частной плоскости как одиночный символ)
  if (0xd800 > prev || prev > 0xdbff) {
    throw "Младшая часть суррогатной пары без предшествующей старшей";
  }
  // Возвращаем следующий символ (и увеличиваем счётчик)
  return [str.charAt(i + 1), i + 1];
}

Пример: добавление к методу charAt() поддержки символов не в Базовой многоязыковой плоскости (БМП)

В то время как пример выше может быть более полезен тем, кто хочет поддерживать символы не в плоскости БМП (поскольку он не требует от вызывающей стороны знания о том, где может встретиться символ из не-БМП), в случае, если кто-то желает выбирать символы по индексу и трактовать суррогатную пару внутри строки как один символ, он может использовать следующий код:

js
function fixedCharAt(str, idx) {
  var ret = "";
  str += "";
  var end = str.length;

  var surrogatePairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
  while (surrogatePairs.exec(str) != null) {
    var li = surrogatePairs.lastIndex;
    if (li - 2 < idx) {
      idx++;
    } else {
      break;
    }
  }

  if (idx >= end || idx < 0) {
    return "";
  }

  ret += str.charAt(idx);

  if (
    /[\uD800-\uDBFF]/.test(ret) &&
    /[\uDC00-\uDFFF]/.test(str.charAt(idx + 1))
  ) {
    // Перешагиваем через один, поскольку один «символ» является частью суррогатной пары
    ret += str.charAt(idx + 1);
  }
  return ret;
}

Спецификации

Specification
ECMAScript Language Specification
# sec-string.prototype.charat

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также