字符串长度截断,但不允许截断单词

Posted

技术标签:

【中文标题】字符串长度截断,但不允许截断单词【英文标题】:String truncate on length, but no chopping up of words allowed 【发布时间】:2011-10-02 19:08:13 【问题描述】:

我想将 mysql 中的字符串字段长度限制在一定长度,但我不希望发生任何切词。

当我这样做时:

SELECT SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 28)

我得到这个作为输出:

Business Analist met focus o

但我愿意

Business Analist met focus

如何执行 28 个字符的限制,但又能防止切词?当然,[在此处插入选择的编程语言]很容易;-),但我想知道在 MYSQL 中是否可以通过简单的语句来实现。

【问题讨论】:

您正在 MySQL 中寻找“自动换行”。有点。 这里需要逻辑。换句话说,您需要一个 if 语句。如果 MySQL 支持 if,则使用 that,否则,没办法 我认为这对 mysql 来说是相当昂贵的操作。只需在您使用的任何编程语言中砍掉这个词并在以后摆脱它。 @keymone 并非如此,请参阅sitepoint.com/mysql-mistakes-php-developers 的第 5 点(5. 优先使用 PHP 而不是 SQL) @bicycle 在你的 PHP 中实现 AVG 和做子字符串是有区别的。很有可能,如果您正在执行子字符串,则无论如何您都需要在 php 中执行其他更复杂的操作。 【参考方案1】:

如何在空格上分割:

SELECT SUBSTRING_INDEX('Business Analist met focus op wet- en regelgeving',' ',4)

会回来

Business Analist met focus

【讨论】:

【参考方案2】:

@str 成为您的字符串,@len 是要剪切的初始位置。那么必要的步骤可能是:

    @str最左边的@len字符。

    反转子串。

    找到反转子串中第一个空格的位置。

    从该位置减去1。但是如果没有找到空格,就让位置保持0

    @len 中减去找到的位置,并将其命名为cutpos

    @str的第一个(最左边)cutpos字符作为str1,将所有其他字符(从cutpos+1开始)作为str2

SELECT
  LEFT(str, cutpos) AS str1,
  SUBSTRING(str, cutpos + 1) AS str2
FROM (
  SELECT
    @str AS str,
    @len - IFNULL(NULLIF(LOCATE(' ', REVERSE(LEFT(@str, @len))), 0) - 1, 0) AS cutpos
) s

【讨论】:

【参考方案3】:

非常有趣的问题。我是这样做的:

//gets initial string - use 29 instead of 28 to see if the 29th  character is a space
SELECT SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29) 

//inverts the string, so we can get the first 
SELECT REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29))

// find the charindex of the first space (last space in the string not reversed)
SELECT CHARINDEX(' ', REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29)))

// get the substring from the first (last) space
SELECT  SUBSTRING(REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29)), CHARINDEX(' ', REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29))), 29)

// reverse the string again to unfold it.
SELECT REVERSE(SUBSTRING(REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29)), CHARINDEX(' ', REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 29))), 29))


// to try different lengths...
DECLARE  @size  int
select @size = 24
SELECT REVERSE(SUBSTRING(REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, @size)), 
CHARINDEX(' ', REVERSE( SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, @size))), @size))

【讨论】:

charindex 不是有效的 mysql 函数。它应该是 LOCATE 。顺便说一句,这应该是最好的答案。 这对我有用,但我必须将 CHARINDEX 更改为 LOCATE【参考方案4】:

在 SQL 中它会是...

select Substring('Business Analist met focus op wet- en regelgeving', 0 , 28 + 2 - CharIndex(' ',  REVERSE(SUBSTRING('Business Analist met focus op wet- en regelgeving', 0, 28 + 1 )),0))

我不知道这些功能在MYSQL中是否都可用

编辑:我认为 MYSQL 用“Locate”替换“CharIndex”

【讨论】:

【参考方案5】:

以纳尼安的回答为基础,这是一个适用于两个字段 (a.product,a.descr) 的回答,其中在字符串被截断时添加了“...”。 a.descr 也可以为空。

  IF (
CHARACTER_LENGTH(
  IF(
    a.descr = '',
    a.product,
    CONCAT_WS(' - ',a.product,a.descr)
  )
)>35,
IF(
  a.descr = '',
  CONCAT(
    REVERSE(SUBSTRING(REVERSE( SUBSTRING(a.product, 1, 35)), locate(' ', REVERSE( SUBSTRING(a.product, 1, 35))), 35)),
    '...'
  ),
  CONCAT(
    REVERSE(SUBSTRING(REVERSE( SUBSTRING(CONCAT_WS(' - ',a.product,a.descr), 1, 35)), locate(' ', REVERSE( SUBSTRING(CONCAT_WS(' - ',a.product,a.descr), 1, 35))), 35)),
    '...'
  )
),
CONCAT_WS(' - ',a.product,a.descr)
)

我需要这样的东西,所以我添加了它。可能会帮助别人。

【讨论】:

是不是你在 3 小时前否决了我的回答?如果是这样,如果您详细说明您的原因,我将不胜感激。顺便说一句,当您检查开头的长度时,您正在验证descr 是否为空,并根据此情况单独检查product 的长度或以- 分隔的两列的长度。但是稍后,当您真正找到要剪切的位置时,您不再检查descr 是否为空。我认为,有时这可能不会导致最精确的切割。 确实如此,好点。我刚刚调整了一下。是的,我对你投了反对票,因为另一个答案在没有太多定制的情况下更容易实现。不过应该调整它,因为 charindex 不是有效的 mysql 方法,而问题是关于 mysql。应该改为定位。 但不包括在指定长度内找不到空格的情况。事实上,你的也没有。不过,我意识到省略这种情况可能会使该解决方案看起来更容易。无论哪种方式,感谢您解释您的反对意见!【参考方案6】:

@Andriy M. 我非常喜欢你的回答 :) 无论如何,我在我的数据库上发现,如果您像这样更改第 2 行和第 3 行,效果会更好:

SELECT
  IF(LENGTH(str)<=@len,str,LEFT(str, cutpos)) AS str1,
  IF(LENGTH(str)<=@len,'',SUBSTRING(str, cutpos + 1)) AS str2
FROM (
  SELECT
    @str AS str,
    @len - IFNULL(NULLIF(LOCATE(' ', REVERSE(LEFT(@str, @len))), 0) - 1, 0) AS cutpos
  FROM @table
) s

不知道是我的错还是什么,但另一方面它有时会在第一个字母的长度为

我给你发布一个工作示例:

CREATE TABLE `test` (
  `sometext` varchar(65)
);

INSERT INTO `test` (`sometext`) VALUES
('Firs strin'),
('Alll right'),
('third string'),
('fourth string'),
('a longer example string'),
('Supercalifragilisticexpialidocious');

SELECT
  IF(LENGTH(str)<=12,str,LEFT(str, cutpos)) AS str1,
  IF(LENGTH(str)<=12,'',SUBSTRING(str, cutpos + 1)) AS str2
FROM (
  SELECT
    sometext AS str,
    12 - IFNULL(NULLIF(LOCATE(' ', REVERSE(LEFT(sometext, 12))), 0) - 1, 0) AS cutpos
  FROM test
) s

这是一个使用您的原始代码的不工作示例:

SELECT
  LEFT(str, cutpos) AS str1,
  SUBSTRING(str, cutpos + 1) AS str2
FROM (
  SELECT
    sometext AS str,
    12 - IFNULL(NULLIF(LOCATE(' ', REVERSE(LEFT(sometext,12))), 0) - 1, 0) AS cutpos
  FROM test
) s

我不确定这是不是 utf8 问题,或者我只是误解了您的代码,或者其他什么...

【讨论】:

【参考方案7】:

简单功能:

DROP FUNCTION IF EXISTS fn_maxlen;
delimiter //
CREATE FUNCTION fn_maxlen(s TEXT, maxlen INT) RETURNS VARCHAR(255)
BEGIN

 RETURN LEFT(s, maxlen - LOCATE(' ', REVERSE(LEFT(s, maxlen))));

END//
delimiter ;

用途:

SELECT fn_maxlen('Business Analist met focus op wet- en regelgeving', 28);

【讨论】:

【参考方案8】:

似乎人们不阅读mysql手册:

原文:SELECT SUBSTRING('Business Analist met focus op wet- en regelgeving', 1, 28) 给出断句。

修改:SELECT SUBSTRING_INDEX('Business Analist met focus op wet- en regelgeving', ' ' , 4) 给出完整的单词

SUBSTRING_INDEX(string, delimiter, number) 将根据找到分隔符的次数截断字符串。 让你的分隔符成为一个空格,你只会得到整个单词。所以:

SUBSTRING_INDEX( LEFT('Business Analist met focus op wet- en regelgeving',28), ' ' , 4) 应该这样做。

【讨论】:

似乎有些人不假设通用输入......答案应该适用于任意数量的单词,而不仅仅是 4 个!无论如何,SUBSTRING_INDEX 函数很有趣。

以上是关于字符串长度截断,但不允许截断单词的主要内容,如果未能解决你的问题,请参考以下文章

将字符串 c++ 截断为长度而不剪切单词

显示一定长度的字符串而不截断

(SqlServe)关于字符串长度被截断的问题

如何截断 DataFrame 列中字符串的长度?

sql 查询语句截断问题

怎么解决过程中提示:将截断字符串或二进制数据?