Replace numbers with delimiter AND the number - tsql

I have text that I would like to parse into individual sections, along with the number heading that section. To see what I mean, I currently have something similar to the following script that parses out lines of text between numbers such as '1. ' or '10. '.
DECLARE #EXAMPLE TABLE (
ID INT
, COMMENT VARCHAR(8000)
)
DECLARE
#olddelim1 VARCHAR(MAX) = '1. '
,#olddelim2 VARCHAR(MAX) = '2. '
,#olddelim3 VARCHAR(MAX) = '3. '
,#olddelim4 VARCHAR(MAX) = '4. '
,#olddelim5 VARCHAR(MAX) = '5. '
,#olddelim6 VARCHAR(MAX) = '6. '
,#olddelim7 VARCHAR(MAX) = '7. '
,#olddelim8 VARCHAR(MAX) = '8. '
,#olddelim9 VARCHAR(MAX) = '9. '
,#olddelim10 VARCHAR(MAX) = '0. '
,#newdelim VARCHAR(MAX) = '¦'
,#olddelim0 VARCHAR(MAX) = '¦¦'
INSERT INTO #EXAMPLE (ID, COMMENT)
VALUES
(1, '1. Sed ut perspiciatis 1.3 unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. 2. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. 3. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem 4. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur 5. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?')
, (2, 'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia 8.3 deserunt mollitia animi, id est laborum et dolorum fuga')
, (3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua 1. A cras semper auctor neque 2. Morbi tincidunt augue interdum velit euismod in')
, (4, '1. Amet luctus venenatis lectus magna 2. Magna ac placerat vestibulum lectus mauris ultrices eros in cursus 3. Et ligula ullamcorper malesuada proin libero nunc consequat interdum varius 4. Enim facilisis gravida neque convallis a 5. Sed lectus vestibulum mattis ullamcorper velit sed 6. Vel turpis nunc eget lorem dolor 7. Convallis convallis tellus id interdum velit laoreet id donec 8. Nibh tortor id aliquet lectus proin nibh nisl 9. Eu ultrices vitae auctor eu augue ut 10. Mus mauris vitae ultricies leo integer malesuada nunc 11. Viverra justo nec 10.4 ultrices dui sapien eget mi 12. Tristique senectus et netus et malesuada fames ac turpis')
UPDATE #EXAMPLE
SET COMMENT = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COMMENT, #olddelim1,#newdelim),#olddelim2,#newdelim),#olddelim3,#newdelim),#olddelim4,#newdelim),#olddelim5,#newdelim),#olddelim6,#newdelim),#olddelim7,#newdelim),#olddelim8,#newdelim),#olddelim9,#newdelim),#olddelim10,#newdelim),#olddelim0,#newdelim)
;WITH SPLIT_TABLE(ID, STARTS, POS, FOUND_SECTIONS) AS (
SELECT
ID
, 1
, CHARINDEX('¦', COMMENT)
, COMMENT
FROM #EXAMPLE
UNION ALL
SELECT
ID
, POS + 1
, CHARINDEX('¦', FOUND_SECTIONS, POS + 1)
, FOUND_SECTIONS
FROM SPLIT_TABLE
WHERE POS > 0)
, FOUND_SECTIONS_TBL(ID, FOUND_SECTIONS) AS (
SELECT
ID
, SUBSTRING(FOUND_SECTIONS, STARTS, CASE WHEN POS > 0 THEN POS - STARTS ELSE LEN(FOUND_SECTIONS) END)
FROM SPLIT_TABLE)
SELECT * FROM FOUND_SECTIONS_TBL
WHERE FOUND_SECTIONS <> ''
ORDER BY ID
However, what I would like to see is that the number I'm extracting stay associated to that section of text. However, I cannot simply do this with something like a ROW_NUMBER() over the STARTS/POS fields, as there are strings that do not initially start with a number (e.g. ID = 3). So the end result would look something like this:
CREATE TABLE #RESULTS (ID INT, SECTION_NUMBER INT, SECTION VARCHAR(8000))
INSERT INTO #RESULTS
VALUES
(1,1,'Sed ut perspiciatis 1.3 unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.')
,(1,2,'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.')
,(1,3,'Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem')
,(1,4,'Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur')
,(1,5,'Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?')
,(2,0,'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia 8.3 deserunt mollitia animi, id est laborum et dolorum fuga')
,(3,0,'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua')
,(3,1,'A cras semper auctor neque')
,(3,2,'Morbi tincidunt augue interdum velit euismod in')
,(4,1,'Amet luctus venenatis lectus magna')
,(4,2,'Magna ac placerat vestibulum lectus mauris ultrices eros in cursus')
,(4,3,'Et ligula ullamcorper malesuada proin libero nunc consequat interdum varius')
,(4,4,'Enim facilisis gravida neque convallis a ')
,(4,5,'Sed lectus vestibulum mattis ullamcorper velit sed')
,(4,6,'Vel turpis nunc eget lorem dolor')
,(4,7,'Convallis convallis tellus id interdum velit laoreet id donec')
,(4,8,'Nibh tortor id aliquet lectus proin nibh nisl')
,(4,9,'Eu ultrices vitae auctor eu augue ut')
,(4,10,'Mus mauris vitae ultricies leo integer malesuada nunc')
,(4,11,'Viverra justo nec')
,(4,12,'Tristique senectus et netus et malesuada fames ac turpis')
SELECT * FROM #RESULTS DROP TABLE #RESULTS
I've tried replacing my nested REPLACE statement with variations of the STUFF function, and even combinations of the STUFF/REPLACE functions, but I can't quite get it to work the way I want it to. Does anyone have ideas about how to do this? Thanks!

Note! Assuming that I understood your request, then The solution is simple using STRING_SPLIT function, but this is not something that you should do in production probably s the performance are really poor. This type of task are better do in the client side.
DDL+DML
CREATE TABLE EXAMPLE (
ID INT
, COMMENT VARCHAR(8000)
)
INSERT INTO EXAMPLE (ID, COMMENT)
VALUES
(1, '1. Sed ut perspiciatis 1.3 unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. 2. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. 3. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem 4. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur 5. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?')
, (2, 'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia 8.3 deserunt mollitia animi, id est laborum et dolorum fuga')
, (3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua 1. A cras semper auctor neque 2. Morbi tincidunt augue interdum velit euismod in')
, (4, '1. Amet luctus venenatis lectus magna 2. Magna ac placerat vestibulum lectus mauris ultrices eros in cursus 3. Et ligula ullamcorper malesuada proin libero nunc consequat interdum varius 4. Enim facilisis gravida neque convallis a 5. Sed lectus vestibulum mattis ullamcorper velit sed 6. Vel turpis nunc eget lorem dolor 7. Convallis convallis tellus id interdum velit laoreet id donec 8. Nibh tortor id aliquet lectus proin nibh nisl 9. Eu ultrices vitae auctor eu augue ut 10. Mus mauris vitae ultricies leo integer malesuada nunc 11. Viverra justo nec 10.4 ultrices dui sapien eget mi 12. Tristique senectus et netus et malesuada fames ac turpis')
GO
SELECT * FROM EXAMPLE
GO
Optional solution
Please check if this solve your needs:
;With MyCTE as (
select ID, c =
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(COMMENT, '11. ', '#11# ')
, '12. ', '#12# '
)
, '13. ', '#13# '
)
, '4. ', '#4# '
)
, '5. ', '#5# '
)
, '6. ', '#6# '
)
, '7. ', '#7# '
)
, '8. ', '#8# '
)
, '9. ', '#9# '
)
, '10. ', '#10# '
)
, '1. ', '#1# '
)
, '2. ', '#2# '
)
, '3. ', '#3# '
)
FROM EXAMPLE
)
select ID,REPLACE(ISNULL(V.n,0),'# ','') as SECTION_NUMBER ,RIGHT([value], LEN([value]) - CHARINDEX('# ',[value],1)) as SECTION
from MyCTE
CROSS APPLY STRING_SPLIT(c,'#')
Left Join (values ('1# '),('2# '),('3# '),('4# '),('5# '),('6# '),('7# '),('8# '),('9# '),('10# '),('11# '),('12# '),('13# ')) V(n) ON [value] like V.n + '%'
where NOT ISNULL([value],'') = ''
GO
Comments
(1) For the sake of the demo, I manually used the numbers 1-6 but in your production you might have many more numbers. You can use dynamic query in order to build the query dynamically. With that being said, for most cases, It is HIGHLY recommended not to be lazy (at least if the amount of numbers is too high) and use this approach of manually write nested REPLACE since dynamic query might reduce performance.
(2) SQL Server does not deal parsing text well and using multiple string functions is usually not recommended. Using SQL CLR you can gain much better result probably by using regular expression.
(3) In general you should avoid such task in the server side. SQL Server is a database management application and as such it is designed for best performance in managing the data and not parsing the data.

After some research and elbow grease, I've come up with the solution below. I am not sure how performant/optimized it is, and I'm certain there's probably better ways to do this. However, I wanted to avoid hard-coding the numbers... So here it is:
CREATE TABLE #EXAMPLE (
ID INT
, COMMENT VARCHAR(8000)
)
INSERT INTO #EXAMPLE (ID, COMMENT)
VALUES
(1, '1. Sed ut perspiciatis 1.3 unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. 2. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. 3. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem 4. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur 5. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?')
, (2, 'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia 8.3 deserunt mollitia animi, id est laborum et dolorum fuga')
, (3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua 1. A cras semper auctor neque 2. Morbi tincidunt augue interdum velit euismod in')
, (4, '1. Amet luctus venenatis lectus magna 2. Magna ac placerat vestibulum lectus mauris ultrices eros in cursus 3. Et ligula ullamcorper malesuada proin libero nunc consequat interdum varius 4. Enim facilisis gravida neque convallis a 5. Sed lectus vestibulum mattis ullamcorper velit sed 6. Vel turpis nunc eget lorem dolor 7. Convallis convallis tellus id interdum velit laoreet id donec 8. Nibh tortor id aliquet lectus proin nibh nisl 9. Eu ultrices vitae auctor eu augue ut 10. Mus mauris vitae ultricies leo integer malesuada nunc 11. Viverra justo nec 10.4 ultrices dui sapien eget mi 12. Tristique senectus et netus et malesuada fames ac turpis')
GO
WITH PATTERNS AS (
SELECT
*
FROM (VALUES('%[0-9]. %', 3)
, ('%[0-9][0-9]. %', 4)
, ('%[0-9][0-9][0-9]. %', 5)) AS PATTERNS (PATTERN, LENGTH))
, L0 AS (SELECT C FROM (VALUES(1),(1)) AS D(C))
, L1 AS (SELECT 1 AS C FROM L0 AS A CROSS JOIN L0 AS B)
, L2 AS (SELECT 1 AS C FROM L1 AS A CROSS JOIN L1 AS B)
, L3 AS (SELECT 1 AS C FROM L2 AS A CROSS JOIN L2 AS B)
, L4 AS (SELECT 1 AS C FROM L3 AS A CROSS JOIN L3 AS B)
, NUMS AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ROWNUM FROM L4)
, INDXS AS (
SELECT E.*, N.*, [GROUP] = DENSE_RANK() OVER(ORDER BY N.ROWNUM) - N.ROWNUM
FROM #EXAMPLE AS E
-- Outer apply so as to capture those without the pattern at all
OUTER APPLY (
SELECT *
FROM NUMS AS N
WHERE N.ROWNUM BETWEEN 1 AND LEN(E.COMMENT)
AND (EXISTS (
SELECT *
FROM PATTERNS AS P
WHERE PATINDEX(P.PATTERN, STUFF(E.COMMENT, 1, CASE WHEN N.ROWNUM > P.LENGTH THEN N.ROWNUM - P.LENGTH ELSE 0 END, '')) BETWEEN 1 AND CASE WHEN N.ROWNUM > P.LENGTH THEN P.LENGTH ELSE N.ROWNUM END)
-- For when there is no intial number at the beginning, but others at later indices
OR N.ROWNUM = 1 )) AS N
)
, PEN AS (
SELECT *, [RN] = ROW_NUMBER() OVER(PARTITION BY ID, [GROUP] ORDER BY ID, [GROUP], ROWNUM)
FROM INDXS
)
, FINAL AS (
SELECT F.ID, F.COMMENT, F.ROWNUM, [RN] = ROW_NUMBER() OVER(PARTITION BY F.ID ORDER BY F.ID, F.ROWNUM)
FROM PEN AS F
WHERE RN = 1
)
SELECT F.ID--, F.COMMENT, F.ROWNUM, N.ROWNUM
, [SECTION_NUMBER] = CASE WHEN N.ROWNUM IS NULL AND F.ROWNUM = 1 THEN 0 ELSE REPLACE(SUBSTRING((CASE WHEN N.ROWNUM IS NULL THEN SUBSTRING(F.COMMENT, F.ROWNUM, 8000) ELSE SUBSTRING(F.COMMENT, F.ROWNUM, N.ROWNUM - F.ROWNUM) END), 1, CHARINDEX('.', (CASE WHEN N.ROWNUM IS NULL THEN SUBSTRING(F.COMMENT, F.ROWNUM, 8000) ELSE SUBSTRING(F.COMMENT, F.ROWNUM, N.ROWNUM - F.ROWNUM) END), 1)), '.', '') END
, [SECTION] = (CASE WHEN N.ROWNUM IS NULL THEN SUBSTRING(F.COMMENT, F.ROWNUM, 8000) ELSE SUBSTRING(F.COMMENT, F.ROWNUM, N.ROWNUM - F.ROWNUM) END)
, F.ROWNUM
, N.ROWNUM
FROM FINAL AS F
LEFT OUTER JOIN FINAL AS N
ON F.ID = N.ID
AND F.RN = N.RN - 1
ORDER BY F.ID, F.ROWNUM
DROP TABLE #EXAMPLE
This solution was partially inspired by Ronen Ariely's answer as well as this post from SQLServerFast: https://sqlserverfast.com/blog/hugo/2019/04/removing-multiple-patterns-from-a-string/. It is able to hardcode the patterns I want to capture rather than all the individual instances of the pattern. I tried in particular to get this part to return only the starting index point of the number pattern (so if "2. " appears at points 233, 234, and 235, I only really want 233) but alas I could not figure that out. Interesting stuff, XML.
, INDXS AS (
SELECT E.*, N.*, [GROUP] = DENSE_RANK() OVER(ORDER BY N.ROWNUM) - N.ROWNUM
FROM #EXAMPLE AS E
-- Outer apply so as to capture those without the pattern at all
OUTER APPLY (
SELECT *
FROM NUMS AS N
WHERE N.ROWNUM BETWEEN 1 AND LEN(E.COMMENT)
AND (EXISTS (
SELECT *
FROM PATTERNS AS P
WHERE PATINDEX(P.PATTERN, STUFF(E.COMMENT, 1, CASE WHEN N.ROWNUM > P.LENGTH THEN N.ROWNUM - P.LENGTH ELSE 0 END, '')) BETWEEN 1 AND CASE WHEN N.ROWNUM > P.LENGTH THEN P.LENGTH ELSE N.ROWNUM END)
-- For when there is no intial number at the beginning, but others at later indices
OR N.ROWNUM = 1 )) AS N
)
In production, I had 335 rows I was applying this to, in which the "COMMENT" field was generally much longer than the sample data I used here. It took about 24 seconds, which works for me (this is for a one time report, but I may use it for similar requests).
Thanks to those who responded!

Related

Autohotkey Hotstring is too slow

If I insert the Hotstring into another program it is not fast enough and mixes sometimes keys on the right position or it lags. It happens when I am using a long text.
I am using:
:*:ex1::Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod t
empor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia
m, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commod
o consequat. Duis aute irure dolor in reprehenderit in voluptate velit es
se cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupida
tat non proident, sunt in culpa qui officia deserunt mollit anim id est l
aborum Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod t
empor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia
m, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commod
o consequat. Duis aute irure dolor in reprehenderit in voluptate velit es
se cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupida
tat non proident, sunt in culpa qui officia deserunt mollit anim id est l
aborum
And it will result in some errors or a longer waiting duration.
This works faster, but sometimes I get an error with double text if I press an Endchar like enter too fast, or I do some different things which I can't lay my finger on.
:*:ex2::
Clipboard = %text%
Send ^v
Exit
Also there is the Clipboard messed up (I think I can store it with an variable and refill it but I am using this Multi-clipboard from windows Windows+V)
Also I am updating the Autohotkey script with excel and it is much easier to use concatenate with the first example.
=IF(List1!B34<>"",(CONCATENATE(":",List1!C34,":",List1!A34,"::",List1!B34)),(CONCATENATE(";",List1!A34)))
So I can use in C something like *0
In A the shortcut and in B the actual text.
If there is nothing in the shortcut field, my headline is inserted in the code for a better overview.
Thanks
Try this
:*:ex1::
ClipSaved := ClipboardAll ; save the entire clipboard to the variable ClipSaved
clipboard := "" ; empty the clipboard (start off empty to allow ClipWait to detect when the text has arrived)
clipboard = ; copy this text:
(
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
)
ClipWait, 1 ; wait for the clipboard to contain data.
If (!ErrorLevel) ; If NOT ErrorLevel clipwait found data on the clipboard
Send, ^v ; paste the text
Sleep, 300 ; don't change clipboard while pasting! (Sleep > 0)
clipboard := ClipSaved ; restore original clipboard
VarSetCapacity(ClipSaved, 0) ; free the memory
return
If you often have to send such a complex or long text, you can create a function, for not repeating the whole code every time:
:*:ex1::
my_text =
(
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
)
Send(my_text)
return
Send(text){
ClipSaved := ClipboardAll
clipboard := ""
clipboard := text
ClipWait, 1
If (!ErrorLevel)
Send, ^v
Sleep, 300
clipboard := ClipSaved
VarSetCapacity(ClipSaved, 0)
}
See ClipboardAll and ClipWait

Multiline Regex replacement with Autohotkey

Can't seem to wrap my head around the proper regex!
MY GOAL
add 2 spaces to each line of a selected block of text
MY CONTEXT
some markdown tools I used need 2 spaces at the end of each line to properly manage lists, etc.
if a file is edited multiple times, I do not want to end up with lines ending with 4+ spaces
a block of text can be a line, a paragraph, the whole file content as shown in the editor
I have some kind of macro in Notepad++ that does the trick but I want to do the same with Autohotkey to be editor-independant
MY EXAMPLE
----
# 2020-03-17
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a,
MY SNIPPET SO FAR
; CTL+SHIFT+F12
^+F12::
Clipboard = ; Empty the clipboard so that ClipWait has something to detect
SendInput, ^c ; Copy selected text
ClipWait
OutputText := ""
Loop, parse, Clipboard, `n, `r
{
OutputText .= RegExReplace(A_LoopField,"m)^(.*) *$","$1 `r`n")
}
SendRaw % OutputText
return
MY PROBLEM
Between the character ignored when looping, what I am trying to match and what I try to replace the group with, I end up with far more lines and spaces than needed.
CURRENT OUTPUT
----
# 2020-03-17
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a,
DESIRED OUTPUT
----
# 2020-03-17
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a,
You're getting too many lines in the output because the send command is messing up due to the carriage returns, which aren't needed in there anyway. I don't really know why that is happening, and I can't be bothered to find out why since the approach isn't good anyway.
And also your indentation is getting messed up because your text editor automatically adds indentation based on the previous line.
But anyway, sending such (long) input is never a good idea.
Make use of the clipboard and just send a ctrl+v to instantly and reliably paste in the text.
Here's an example of that along another way to add the spaces at the end:
inp := "
(
----
# 2020-03-17
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a,
)"
Loop, Parse, inp, `n, `r
OutputText .= (A_LoopField = "" ? "" : RTrim(A_LoopField) " ") "`n"
Clipboard := OutputText
SendInput, ^v
The ternary A_LoopField = "" ? "" : RTrim(A_LoopField) " " returns true if the line was empty and then the two spaces aren't added at the end.
I think that's the behavior you were doing for.
And RTrim is used to trim any trailing spaces (or tabs) off the end, so we're sure to end up with just the two we want.
And, of course, at the end of any line we add one line feed `n.
Also, your Regex approach was just fine as well at first it just seemed off to me, but well, here's another way. And I guess this would be more efficient, though you'd have to work with seriously large inputs and/or slow hardware for that to make any meaningful difference haha.

Pasting text that triggers the scroll leaves cursor in visually the wrong location

See what I mean by going here: https://codemirror.net/demo/indentwrap.html
Paste in something large enough to trigger scrollbar, you may need to remove existing text first:
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
The cursor will appear not in the correct location until you type or move it. Has anyone seen this or know of a solution or workaround?
Edit:
If I put listeners for pasting and do a refresh() and scrollIntoView() it seems to work most of the time... but doesnt always seem effective if I'm posting between content for some reason.
Although it has the small side affect of always having a 17px padding on the right side... adding padding-right: 17px !important to .CodeMirror-sizer seems to fix the problem.
The problem seems to have been caused by when the scrollbar appears, it makes the scrollbar visible, and also adds the 17px right padding to the sizer (so the text doesn't run under the scrollbar). The cursor appears to be positioned based on if that padding was never added until the user types or does something that would move the cursor. By always having that right padding, we don't run into this problem.
The small side affect doesn't matter much to me so I'm happy with this workaround, however... if someone else finds a better solution I'd like to see it.

Kentico CMS: Text area input length calculation and determining max length - specifically when there are line breaks in the text

How does Kentico calculate the length of inputted content in a text area on a form and how much value does it give to a line break? A line break is 2 characters according to my JavaScript calculation but seems like Kentico calculates it as being more than 2 characters.
Summary of problem:
I have a maximum length of 2500 set on a text area input on a form on my Kentico site.
I have entered some text into a this text area and with my JavaScript calculations (used to show how many characters the user has left) the character length is exactly 2500 (including line breaks and spaces) and so should therefore validate and send. However Kentico is failing my input saying that my max length has been exceeded. See below:
If I remove the line break and type some extra characters to bring my character calculation back up to 2500, the form sends without failing.
Test used that fails:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae
augue ac enim molestie scelerisque a id metus. Suspendisse purus
justo, iaculis quis accumsan ut, congue vitae mauris. Nunc luctus
vulputate scelerisque. Nullam ullamcorper porta elit, sed ornare lorem
placerat dictum. Sed quis enim quis nibh convallis sagittis nec vitae
felis. Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus lectus.
Sed quis enim quis nibh convallis sagittis nec vitae felis. Sed
porttitor, nibh et volutpat posuere, neque dui sollicitudin sapien, at
scelerisque lacus elit quis enim. Donec at metus lectus. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Quisque vitae augue ac
enim molestie scelerisque a id metus. Suspendisse purus justo, iaculis
quis accumsan ut, congue vitae mauris. Nunc luctus vulputate
scelerisque. Nullam ullamcorper porta elit, sed ornare lorem placerat
dictum. Sed quis enim quis nibh convallis sagittis nec vitae felis.
Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus
lectus.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque vitae augue ac enim molestie scelerisque a id metus.
Suspendisse purus jus
to, iaculis quis accumsan ut, congue vitae mauris. Nunc luctus
vulputate scelerisque. Nullam ullamcorper porta elit, sed ornare lorem
placerat dictum. Sed quis enim quis nibh convallis sagittis nec vitae
felis. Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus lectus.
Sed quis enim quis nibh convallis sagittis nec vitae felis. Sed
porttitor, nibh et volutpat posuere, neque dui sollicitudin sapien, at
scelerisque lacus elit quis enim. Donec at metus lectus. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Quisque vitae augue ac
enim molestie scelerisque a id metus. Suspendisse purus justo, iaculis
quis accumsan ut, congue vitae mauris. Nunc luctus vulputate
scelerisque. Nullam ullamcorper porta elit, sed ornare lorem placerat
dictum. Sed quis enim quis nibh convallis sagittis nec vitae felis.
Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus
lectus.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque vitae augue ac enim molestie scelerisque a id metus.
Suspendisse purus justo, iaculis quis accumsan ut, congue vitae maur d
Test used that passes: Notice that the line break has been removed and 2 extra characters added to the end to bring it back up to 2500 characters
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae
augue ac enim molestie scelerisque a id metus. Suspendisse purus
justo, iaculis quis accumsan ut, congue vitae mauris. Nunc luctus
vulputate scelerisque. Nullam ullamcorper porta elit, sed ornare lorem
placerat dictum. Sed quis enim quis nibh convallis sagittis nec vitae
felis. Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus lectus.
Sed quis enim quis nibh convallis sagittis nec vitae felis. Sed
porttitor, nibh et volutpat posuere, neque dui sollicitudin sapien, at
scelerisque lacus elit quis enim. Donec at metus lectus. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Quisque vitae augue ac
enim molestie scelerisque a id metus. Suspendisse purus justo, iaculis
quis accumsan ut, congue vitae mauris. Nunc luctus vulputate
scelerisque. Nullam ullamcorper porta elit, sed ornare lorem placerat
dictum. Sed quis enim quis nibh convallis sagittis nec vitae felis.
Sed porttitor, nibh et volutpat posuere, neque dui sollicitudin
sapien, at scelerisque lacus elit quis enim. Donec at metus
lectus.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque vitae augue ac enim molestie scelerisque a id metus.
Suspendisse purus justo, iaculis quis accumsan ut, congue vitae
mauris. Nunc luctus vulputate scelerisque. Nullam ullamcorper porta
elit, sed ornare lorem placerat dictum. Sed quis enim quis nibh
convallis sagittis nec vitae felis. Sed porttitor, nibh et volutpat
posuere, neque dui sollicitudin sapien, at scelerisque lacus elit quis
enim. Donec at metus lectus. Sed quis enim quis nibh convallis
sagittis nec vitae felis. Sed porttitor, nibh et volutpat posuere,
neque dui sollicitudin sapien, at scelerisque lacus elit quis enim.
Donec at metus lectus. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Quisque vitae augue ac enim molestie scelerisque a id
metus. Suspendisse purus justo, iaculis quis accumsan ut, congue vitae
mauris. Nunc luctus vulputate scelerisque. Nullam ullamcorper porta
elit, sed ornare lorem placerat dictum. Sed quis enim quis nibh
convallis sagittis nec vitae felis. Sed porttitor, nibh et volutpat
posuere, neque dui sollicitudin sapien, at scelerisque lacus elit quis
enim. Donec at metus lectus.Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Quisque vitae augue ac enim molestie scelerisque a id
metus. Suspendisse purus justo, iaculis quis accumsan ut, congue vitae
maur dee
The problem lay in the fact that my calculation in my JavaScript gave a length of 1 to a line break whereby Kentico's calculation gives a length of 2 to a line break. So they weren't matching up. Hence my character counter said that the length of the entered text was ok but Kentico's check deemed it over the max length.
This is what I had previously:
enteredText = textareaVariableName.val();
characterCount = enteredText.length; //one line break entered returned 1
This is what I have changed it to:
enteredText = textareaVariableName.val();
enteredTextEncoded = escape(enteredText);
//next I match any line break characters - %0A - after encoding the text area text
linebreaks = enteredTextEncoded.match(/%0A/g);
(linebreaks != null) ? linebreaksLength = linebreaks.length : linebreaksLength = 0;
characterCount = enteredText.length + linebreaksLength; //one line break entered now returns 2
Is there a better way I could check for line breaks in the text, rather than to encode the text and then check for the substring %0A ?
EDIT/UPDATE: I believe the following is a better solution as opposed to what I was doing above.
var limit = 2500; //for example
enteredText = textareaVariableName.val();
numberOfLineBreaks = (enteredText.match(/\n/g)||[]).length;
left = limit - enteredText.length - numberOfLineBreaks;
if (left < 0) {
//character count over code here
} else {
//character count within limits code here
}
This is basically JavaScript problem related to browser. In Firefox or Chrome or any other WebKit based browser textareaVariableName.val().length will count only 1 character for new line (\n). Same for jQuery implementation. But in IE document.getElementById('textareaVariableName').value.length will count 2 for new line (\r\n)
In Kentico, the text is validated to the actual count of characters and therefore the validation is failing.
Quick fix for this is simple regular expression for counting the actual length:
function getTextLength(elementId){
if (elementId) {
var elem = document.getElementById(elementId);
if (elem) {
var str = elem.value;
if (str) {
str = str.replace(/(\r\n|\r|\n)/g, '\r\n');
return str.length;
}
}
}
return 0;
}
This should help you to count characters correctly independently of the browser used by the customer.

Can iFrames work on iPhone/iPad?

I have read so much and still no real answer. How can I get iFrames to work on the iPhone and iPad? Especially now you can't 2 finger scroll. I just want to have an iFrame scroll or something that functions like one. I feel like this should be so simple!
Here is a sample page:
http://olbrichdesigns.com/ffff/characters/test.html
Thanks!
jenny
Mobile safari supports them but generally they're not great.
Using a DIV with overflow-y: scroll would probably be better for you
<div style="overflow-y: scroll; height: 100px;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam at laoreet augue. Pellentesque felis arcu, convallis eu luctus vitae, imperdiet ut est. Nulla rhoncus varius odio, non facilisis felis imperdiet pellentesque. Aliquam in luctus orci. Ut convallis felis sodales diam ultricies in elementum ligula ultricies. Vivamus consequat convallis mauris, sed ullamcorper ante fermentum a. Quisque at velit eu velit venenatis faucibus id vel velit. Integer eget nisi elit. Ut blandit eros lorem, sed feugiat nunc. Sed lacus nisi, placerat sit amet blandit feugiat, accumsan ut urna. Proin risus sapien, porttitor sed euismod quis, adipiscing vitae nulla. Vestibulum at ipsum vel tortor fermentum tristique. Fusce laoreet magna vitae tellus accumsan eget adipiscing velit ullamcorper. Proin tempus volutpat fringilla. Etiam eu magna sit amet mi commodo volutpat sit amet eget lacus. Nullam euismod tellus id odio tempus id tincidunt felis mollis.
</div>
​