Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Разбить строку на подстроки определенной длины, не разбивая слова  [new]
DimaU
Member

Откуда: Москва
Сообщений: 548
Наверное, классика) Подскажите красивое решение, плиз.
Как разбить произвольную длинную строку (текст) на подстроки определенной длины (например, максимум 70 символов), не при этом разбивая слова (в конце подстрок). То есть подстроки могут быть длиной до(!) 70 символов. Исходный текст - это слова, разделенные пробелами, запятыми...
30 окт 19, 11:54    [22005857]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 7391
DimaU,

поищите код на C#, создайте CLR функцию.
30 окт 19, 12:03    [22005868]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
DimaU
Member

Откуда: Москва
Сообщений: 548
Владислав Колосов
DimaU,

поищите код на C#, создайте CLR функцию.

Спасибо. Теоретически можно и SQL-функцию написать, возвращающую table с подстроками...
30 окт 19, 12:11    [22005879]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
msLex
Member

Откуда:
Сообщений: 7727
DimaU
Владислав Колосов
DimaU,

поищите код на C#, создайте CLR функцию.

Спасибо. Теоретически можно и SQL-функцию написать, возвращающую table с подстроками...

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


Но префоманс будет не ахти. Работа со строками не самая сильная сторона TSQL

Сообщение было отредактировано: 30 окт 19, 12:15
30 окт 19, 12:15    [22005881]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 7391
DimaU,

можете поэкспериментировать, но читаемость такого кода у меня под сомнением.
30 окт 19, 12:17    [22005885]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
court
Member

Откуда:
Сообщений: 1956
не претендуя на "красоту" :)

declare @str varchar(max)='Наверное, классика) Подскажите красивое решение, плиз. Как разбить произвольную длинную строку (текст) на подстроки определенной длины (например, максимум 70 символов), не при этом разбивая слова (в конце подстрок). То есть подстроки могут быть длиной до(!) 70 символов. Исходный текст - это слова, разделенные пробелами, запятыми...'
declare @len int = 70
--
;with cte as (
	select 
		rn				=1 
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left(@str,@len)))
		,res			=substring(@str, 1, @len-patindex('%[ ,.-]%',reverse(left(@str,@len)))+1)
		,[str]			=right(@str, case when len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1>0 then len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1 else 0 end)

	union all

	select
		rn				=rn+1
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left([str],@len))) 
		,res			=substring([str], 1, @len-patindex('%[ ,.-]%',reverse(left([str],@len)))+1)
		,[str]			=right([str], case when len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1>0 then len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1 else 0 end)
	from cte 
	where len([str]) > 0 
)
select * from cte 

rnres
1Наверное, классика) Подскажите красивое решение, плиз. Как разбить
2произвольную длинную строку (текст) на подстроки определенной длины
3(например, максимум 70 символов), не при этом разбивая слова (в конце
4подстрок). То есть подстроки могут быть длиной до(!) 70 символов.
5Исходный текст - это слова, разделенные пробелами, запятыми...
30 окт 19, 12:27    [22005898]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
andy st
Member

Откуда:
Сообщений: 796
court,
(c) не я
мы рассмотрели предложение по разработке информационной системы и приняли решение купить небольшую партию той травы, которую Вы курите
30 окт 19, 13:27    [22005995]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
defragmentator
Member

Откуда:
Сообщений: 20504
Разбить на слова, слова сложить в строки Картинка с другого сайта.
30 окт 19, 13:45    [22006024]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 7391
Любой боксовый контрол делает это автоматически.
30 окт 19, 13:48    [22006031]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
DimaU
Member

Откуда: Москва
Сообщений: 548
court, спасибо!
рекурсия - сила!)
Единственное, для вырожденного случая плохо работает) если исходная строка меньше 70 символов...
30 окт 19, 13:52    [22006037]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
court
Member

Откуда:
Сообщений: 1956
DimaU
Единственное, для вырожденного случая плохо работает) если исходная строка меньше 70 символов...
в смысле ?

+
declare @str varchar(max)='Наверное, классика) Подскажите красивое решение, плиз.'
declare @len int = 70
--
;with cte as (
	select 
		rn				=1 
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left(@str,@len)))
		,res			=substring(@str, 1, @len-patindex('%[ ,.-]%',reverse(left(@str,@len)))+1)
		,[str]			=right(@str, case when len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1>0 then len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1 else 0 end)

	union all

	select
		rn				=rn+1
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left([str],@len))) 
		,res			=substring([str], 1, @len-patindex('%[ ,.-]%',reverse(left([str],@len)))+1)
		,[str]			=right([str], case when len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1>0 then len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1 else 0 end)
	from cte 
	where len([str]) > 0 
)
select rn, res from cte order by 1

rnres
1Наверное, классика) Подскажите красивое решение, плиз.
30 окт 19, 14:02    [22006046]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
DimaU
Member

Откуда: Москва
Сообщений: 548
Владислав Колосов
Любой боксовый контрол делает это автоматически.

Это да)
Просто есть потребность выдать длинный текст в отчет (word-xml) с разрисованным подчеркиваниваем (независимо от текста, то есть простое подчеркивание текста строки-результата не подойдет... из-за переноса слов - концы линий подчеркивания плывут - не выровнены по правому краю). Думаю как реализовать...
30 окт 19, 14:03    [22006048]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
invm
Member

Откуда: Москва
Сообщений: 9115
declare @str varchar(max) = 'Наверное, классика) Подскажите красивое решение, плиз. Как разбить произвольную длинную строку (текст) на подстроки определенной длины (например, максимум 70 символов), не при этом разбивая слова (в конце подстрок). То есть подстроки могут быть длиной до(!) 70 символов. Исходный текст - это слова, разделенные пробелами, запятыми...';
declare @len int = 70;

with t as
(
 select
  d.s, d.f
 from
  (select @str) a(s) cross apply
  (select left(a.s, @len)) b(s1) cross apply
  (select patindex('% %', reverse(b.s1))) c(p) cross apply
  (select rtrim(left(a.s, @len - c.p)), substring(a.s, isnull(@len - nullif(c.p, 0) + 2, @len), cast(0x7fffffff as int))) d(f, s)

 union all

 select
  d.s, d.f
 from
  t a cross apply
  (select left(a.s, @len)) b(s1) cross apply
  (select patindex('% %', reverse(b.s1))) c(p) cross apply
  (select rtrim(left(a.s, @len - c.p)), substring(a.s, isnull(@len - nullif(c.p, 0) + 2, @len), cast(0x7fffffff as int))) d(f, s)
 where
  a.s > ''
)
select f, len(f) from t;
30 окт 19, 14:17    [22006065]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
DimaU
Member

Откуда: Москва
Сообщений: 548
court
DimaU
Единственное, для вырожденного случая плохо работает) если исходная строка меньше 70 символов...
в смысле ?

+
declare @str varchar(max)='Наверное, классика) Подскажите красивое решение, плиз.'
declare @len int = 70
--
;with cte as (
	select 
		rn				=1 
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left(@str,@len)))
		,res			=substring(@str, 1, @len-patindex('%[ ,.-]%',reverse(left(@str,@len)))+1)
		,[str]			=right(@str, case when len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1>0 then len(@str)-@len+patindex('%[ ,.-]%',reverse(left(@str,@len)))-1 else 0 end)

	union all

	select
		rn				=rn+1
		,pos_delim		=@len - patindex('%[ ,.-]%',reverse(left([str],@len))) 
		,res			=substring([str], 1, @len-patindex('%[ ,.-]%',reverse(left([str],@len)))+1)
		,[str]			=right([str], case when len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1>0 then len([str])-@len+patindex('%[ ,.-]%',reverse(left([str],@len)))-1 else 0 end)
	from cte 
	where len([str]) > 0 
)
select rn, res from cte order by 1

rntres
1tНаверное, классика) Подскажите красивое решение, плиз.


сорри, это pos_delim странно выходит, если длина строки меньше 70 символов
30 окт 19, 14:52    [22006106]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Minamoto
Member

Откуда: Москва
Сообщений: 1162
DimaU
Владислав Колосов
Любой боксовый контрол делает это автоматически.

Это да)
Просто есть потребность выдать длинный текст в отчет (word-xml) с разрисованным подчеркиваниваем (независимо от текста, то есть простое подчеркивание текста строки-результата не подойдет... из-за переноса слов - концы линий подчеркивания плывут - не выровнены по правому краю). Думаю как реализовать...

Сделать выравнивание по ширине...
Не надо плиз заниматься форматированием на уровне SQL-кода - это дикий костыль.


А сама задача имеет право на жизнь, например, чтобы преодолеть ограничение студии в 65535 символов в результирующих данных, при этом обрезать не посередине слов, а по переносам строки или пробелам. Для отладки делал такое, когда правил хранимки с мега-длинным динамически собранным SQL.
30 окт 19, 14:53    [22006110]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
invm
Member

Откуда: Москва
Сообщений: 9115
Поправочка
declare @str varchar(max) = 'Наверное, классика) Подскажите красивое решение, плиз. Как разбить произвольную длинную строку (текст) на подстроки определенной длины (например, максимум 70 символов), не при этом разбивая слова (в конце подстрок). То есть подстроки могут быть длиной до(!) 70 символов. Исходный текст - это слова, разделенные пробелами, запятыми...';
declare @len int = 70;

with t as
(
 select
  d.s, d.f
 from
  (select @str) a(s) cross apply
  (select left(a.s, @len)) b(s1) cross apply
  (select patindex('% %', reverse(b.s1))) c(p) cross apply
  (select rtrim(left(a.s, @len - c.p)), substring(a.s, isnull(@len - nullif(c.p, 0) + 2, @len), cast(0x7fffffff as int))) d(f, s)

 union all

 select
  d.s, d.f
 from
  t a cross apply
  (select left(a.s, @len)) b(s1) cross apply
  (select patindex('% %', reverse(b.s1))) c(p) cross apply
  (select rtrim(left(a.s, @len - c.p)), substring(a.s, isnull(@len - nullif(c.p, 0) + 2, @len), cast(0x7fffffff as int))) d(f, s)
 where
  len(a.s) > @len
)
select f, len(f) from t;
30 окт 19, 15:23    [22006141]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Alexander Us
Member

Откуда:
Сообщений: 1091
Владислав Колосов
DimaU,
поищите код на C#, создайте CLR функцию.

Поддерживаю.


DimaU, Вам придётся чуть повозиться, но оно (SQL.CLR) того стоит.

+

  public partial class Utils
    {
        

        [SqlFunction(Name = "fn_Split", DataAccess = DataAccessKind.None, FillRowMethodName = "GetRowData1", IsDeterministic = true)]
        public static IEnumerable fn_Split(String StrToSplit, String Splitter)
        {
            List<TableRow1> ret = new List<TableRow1>();

            if (StrToSplit == null) 
                return ret;         
            
            if (String.IsNullOrEmpty(Splitter)) 
	        {
		        Char[] arr;
                arr = StrToSplit.ToCharArray();
                for (int i = 0; i < arr.Length; i++)
                {
                    ret.Add(new TableRow1(i + 1, arr[i].ToString()));
                }                
	        }
            else 
            {
                String[] arr;
                arr = StrToSplit.Split(new String[] {Splitter}, StringSplitOptions.None);
                for (int i = 0; i < arr.Length; i++)
                {
                    String tmp = arr[i];                    
                    ret.Add(new TableRow1(i+1, tmp));
                }
            }
                  
            return ret;            
        }

        [SqlFunction(Name = "fn_SplitByLen", DataAccess = DataAccessKind.None, FillRowMethodName = "GetRowData1", IsDeterministic = true)]
        public static IEnumerable fn_SplitByLen(String StrToSplit, Int32 Len)
        {
            List<TableRow1> ret = new List<TableRow1>();
            if (StrToSplit == null)
                return ret;

            if (Len <= 0)
                throw new ArgumentException("param len <= 0");

            for (int i = 0; i * Len < StrToSplit.Length; i++)
            {
                String tmp = StrToSplit.Substring(i * Len, Math.Min(Len, StrToSplit.Length - i * Len));
                ret.Add(new TableRow1(i + 1, tmp));
            }

            return ret;                       
        }



       

        //######################
        public static void GetRowData1(object o, out SqlInt32 RowNr, out SqlChars Data)
        {
            TableRow1 row = (TableRow1)o;
            RowNr = new SqlInt32(row.RowNr);
            Data = new SqlChars(row.Value);
        }

        public struct TableRow1
        {
            public int RowNr;
            public string Value;

            public TableRow1(int RowNr, string Value)
            {
                this.RowNr = RowNr;
                this.Value = Value;
            }
        }


 
        }
30 окт 19, 16:15    [22006203]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Tigrist
Member

Откуда: Россия-Таиланд-Корея
Сообщений: 477
DimaU
Наверное, классика) Подскажите красивое решение, плиз.
Как разбить произвольную длинную строку (текст) на подстроки определенной длины (например, максимум 70 символов), не при этом разбивая слова (в конце подстрок). То есть подстроки могут быть длиной до(!) 70 символов. Исходный текст - это слова, разделенные пробелами, запятыми...


До 70 символов, Карл!

Это значит чуть по сложнее, искать пробел от 70го символа в сторону начала строки а не после
30 окт 19, 22:19    [22006607]     Ответить | Цитировать Сообщить модератору
 Re: Разбить строку на подстроки определенной длины, не разбивая слова  [new]
Сруль.
Member

Откуда:
Сообщений: 119
Как бы я такое делал?
Циклом от 70 позиции с шагом (-1) найти первый пробел,
его номер знаем, вырезаем подстроку с первой позиции до найденной, типа, этого пробела.
И повторяем, пока исходная строка не сойдёт в ноль.

Сообщение было отредактировано: 31 окт 19, 15:54
31 окт 19, 15:53    [22007319]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить