在JavaScript中查找特定位置的单词

14
对于输入字符串 'this is a sentence',当位置为6或7时,它必须返回 'is'。当位置为0、1、2、3或4时,结果必须为'this'。最简单的方法是什么?

2
如果位置是5,应该返回什么? - Marc B
6个回答

25
function getWordAt (str, pos) {
    // check ranges
    if ((pos < 0) || (pos > str.length)) {
        return '';
    }
    // Perform type conversions.
    str = String(str);
    pos = Number(pos) >>> 0;
    
    // Search for the word's beginning and end.
    var left = str.slice(0, pos + 1).search(/\S+$/), // use /\S+\s*$/ to return the preceding word 
        right = str.slice(pos).search(/\s/);
    
    // The last word in the string is a special case.
    if (right < 0) {
        return str.slice(left);
    }
    
    // Return the word, using the located bounds to extract it from the string.
    return str.slice(left, right + pos);
    
}

这个函数接受任何空白字符作为单词分隔符,包括空格、制表符和换行符。基本上,它的工作原理如下:
  • 匹配单词的开头,使用/\S+$/
  • 匹配单词的结束位置,使用/\s/
如果给定的索引是一个空白字符,函数将返回"";因为空格本身不是单词的一部分。如果你希望函数返回前面的单词,可以将/\S+$/改为/\S+\s*$/
这是一个例子输出:"这是一个句子。"
0: This
1: This
2: This
3: This
4:
5: is
6: is
7:
8: a
9:
10: sentence.
// ...
18: sentence.

修改后返回前一个单词,输出结果如下:
0: This
1: This
2: This
3: This
4: This
5: is
6: is
7: is
8: a
9: a
10: sentence.
// ...
18: sentence.

3
我对修改后的正则表达式 /\S+\s*/ 的结果不一致。请参考这个jsfiddle链接 - Mogsdad
这个程序出现了问题,使用 getWordAt("s ss",1) 会返回空字符串。 - SuperUberDuper
1
正如@Mogsdad所指出的那样,前置单词正则表达式无法正确工作。它缺少了行尾$标记。我相信/\S+\s*$/应该能按预期工作。 - Tanner Stern

17

我在撰写时最受欢迎的答案中遇到了一些奇怪的行为,如果位置位于不是最后一个单词的最后一个字符,则无法获取该单词。

这是我的版本:

  • 我返回起始和结束索引以提高灵活性,而不是返回单词。
  • 我使用正则表达式来检测空格。这提供了在不同语言环境下的多样性,并且仍然相对高效,因为只检查了1个字符。
  • 注意:当位置两侧有空格(或字符串的开头/结尾)时,函数返回[position,position]。实际上这是有意义的,因为该位置没有单词:它是一个长度为0的单词。
function getWordBoundsAtPosition(str, position) {
  const isSpace = (c) => /\s/.exec(c);
  let start = position - 1;
  let end = position;

  while (start >= 0 && !isSpace(str[start])) {
    start -= 1;
  }
  start = Math.max(0, start + 1);

  while (end < str.length && !isSpace(str[end])) {
    end += 1;
  }
  end = Math.max(start, end);
  
  return [start, end];
}

要插入到子字符串中,只需拆分返回的边界。

const myString = 'This is a sentence.';
const position = 7;

const [start, end] = getWordBoundsAtPosition(myString, position);
const wordAtPosition = myString.substring(start, end); // => 'is'

酷炫的可视化

function getWordBoundsAtPosition(str, position) {
  const isSpace = (c) => /\s/.exec(c);
  let start = position - 1;
  let end = position;

  while (start >= 0 && !isSpace(str[start])) {
    start -= 1;
  }
  start = Math.max(0, start + 1);

  while (end < str.length && !isSpace(str[end])) {
    end += 1;
  }
  end = Math.max(start, end);
  
  return [start, end];
}

function analyzeStringWithCursor(str, bounds, cursorIdx) {
  const [start, end] = bounds;
  document.getElementById("cursor-position").textContent = String(cursorIdx);
  document.getElementById("fn-call").textContent = `getWordBoundsAtPosition("${str}", ${cursorIdx})`;
  document.getElementById("fn-result").textContent = `[${start}, ${end}]`; 
  document.getElementById("substring-call").textContent = `"${str}".substring(${start}, ${end})`; 
  document.getElementById("substring-result").textContent = `"${str.substring(start, end)}"`; 
  document.getElementById("viz").textContent = ` ${"0123456789".repeat(Math.floor((str.length - 1) / 10) + 1).substring(0, str.length)}
"${str}"
${" ".repeat(start) + "↗" + " ".repeat(end - start) + "↖"}`;
}

analyzeStringWithCursor(
  "",
  [0, 0],
  0
);

const update = (e) => {
  analyzeStringWithCursor(
    e.target.value,
    getWordBoundsAtPosition(e.target.value, e.target.selectionStart),
    e.target.selectionStart
  );
};


document.getElementById("input").addEventListener('keyup', update);
document.getElementById("input").addEventListener('click', update);
<p>Type some words below. Your cursor position will be used as the position argument to the function.</p>
<input id="input" placeholder="Start typing some words..." />
<pre id="viz" style="font-size: 2rem; margin: 0.25rem"></pre>
<ul>
  <li>Cursor Position: <code id="cursor-position"></code></li>
  <li><code id="fn-call"></code>: <code id="fn-result"></code></li>
  <li><code id="substring-call"></code>: <code id="substring-result"></code></li>
</ul>


谢谢,这个可行,另一个不行,而且那个 >>> 在最流行的答案中是无意义的,令人困惑。 - SuperUberDuper

3
var str = "this is a sentence";

function GetWordByPos(str, pos) {
    var left = str.substr(0, pos);
    var right = str.substr(pos);

    left = left.replace(/^.+ /g, "");
    right = right.replace(/ .+$/g, "");

    return left + right;
}

alert(GetWordByPos(str, 6));

注:尚未经过彻底测试,也没有错误处理。


1
function getWordAt(str, pos) {

   // Sanitise input
   str = str + "";
   pos = parseInt(pos, 10);

   // Snap to a word on the left
   if (str[pos] == " ") {
      pos = pos - 1;
   }

   // Handle exceptional cases
   if (pos < 0 || pos >= str.length-1 || str[pos] == " ") {
      return "";
   }

   // Build word
   var acc = "";
   for ( ; pos > 0 && str[pos-1] != " "; pos--) {}
   for ( ; pos < str.length && str[pos] != " "; pos++) {
      acc += str[pos];
   }

   return acc;
}

alert(getWordAt("this is a sentence", 6));

像这样的东西。一定要彻底测试循环逻辑;我没有。


最佳实践是使用parseInt(pos,10)Math.floor(pos) - 永远不要在未指定基数的情况下使用parseInt。 - PleaseStand
嗯,Chris的/Amit的更好。 - Lightness Races in Orbit

1
function getWordAt(s, pos) {
  // make pos point to a character of the word
  while (s[pos] == " ") pos--;
  // find the space before that word
  // (add 1 to be at the begining of that word)
  // (note that it works even if there is no space before that word)
  pos = s.lastIndexOf(" ", pos) + 1;
  // find the end of the word
  var end = s.indexOf(" ", pos);
  if (end == -1) end = s.length; // set to length if it was the last word
  // return the result
  return s.substring(pos, end);
}
getWordAt("this is a sentence", 4);

0

最终我采用了自己的解决方案。

注1:这些正则表达式并未经过彻底测试。

注2:该解决方案速度很快(即使对于大字符串),而且即使在单词中间也可以工作(即使位置(pos参数)在单词中间)。

function getWordByPosition(str, pos) {
  let leftSideString = str.substr(0, pos);
  let rightSideString = str.substr(pos);

  let leftMatch = leftSideString.match(/[^.,\s]*$/);
  let rightMatch = rightSideString.match(/^[^.,\s]*/);

  let resultStr = '';

  if (leftMatch) {
      resultStr += leftMatch[0];
  }

  if (rightMatch) {
      resultStr += rightMatch[0];
  }

  return {
      index: leftMatch.index,
      word: resultStr
  };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接