Regex/JavaScript: Dela upp sträng i separata rader efter max antal tecken per rad med att titta n tecken bakåt efter ett möjligt blanksteg?


Detta är en liknande fråga som Hur man delar en sträng vid varje n tecken eller till närmaste föregående mellanslag, men till skillnad från vad jag förväntade mig baserat på titeln, fungerar den lösningen inte om det bara finns ett långt ord utan något blanksteg.

Så jag behöver en Regex som delar en sträng till separata rader (flera gånger om det behövs) efter max antal tecken per rad, och ser bakåt n tecken för ett möjligt mellanrum (bryt där om det hittas, annars vid maxlängd)?

Redigera 1: Till exempel, med maximal radlängd 30 tecken och 15 tecken bakåt för mellanslagssökning:

Loremipsum är helt enkelt dummytext från tryck- och sättningsindustrin.

Den meningen första ord har en längd på 32 tecken. Så utskriften bör vara:

Loremipsumissimplydummytextoft  # Line has length of 30 char
he printing and typesetting     # Cut before the word at otherwise 30 char
industry.

Så det första ordet bör tvångsavbrytas efter den 30:e tecknet, eftersom det inte fanns något blanksteg.

Den återstående strängen har en längd på 28 (eller 29 med bindestreck) före ordet 'industry', så på platsen för den 30:e tecknet finns ett ord, så lösningen letar efter det tidigare blanksteget inom en räckvidd på 15 tecken. Den raden bryts före ordet 'industry'.

Redigera 2: Andra exemplet på text:

Loremipsum är helt enkelt dummytext från tryck- och sättningsindustrin. Loremipsum är helt enkelt dummytext från tryck- och sättningsindustrin. Loremipsum är helt enkelt dummytext från tryck- och sättningsindustrin. Loremipsum är helt enkelt dummytext från tryck- och sättningsindustrin.

Bör resultat bli:

Loremipsumissimplydummytextoft
he printing and typesetting
industry. Loremipsumis simply
dummytext ofthe printing and
typesetting industry.
Loremipsumissimplydummytextoft
he printing and typesetting
industry. Loremipsumis simply
dummytext ofthe printing and
typesetting industry.

Användningsfall för detta regex är att formatera en lång sträng till läsbar text med maxlängd på varje rad och rader som börjar med ett tecken och inte ett blanksteg.

Valfritt krav: När jag efter den första publiceringen lade till det exemplet i Redigering 1 lade jag också till ett valfritt krav på att lägga till ett bindestreck '-' i början av nästa rad, om ett ord delades vid maximal radlängd. Jag tar nu bort det från exemplet och lägger till det som ett separat valfritt krav här.

Så ett valfritt krav: Om en rad bryts mitt i ett ord vid maxlängd och inte vid ett blanksteg, ska ett bindestreck läggas till i slutet av den raden (och inte i början av nästa rad, som jag ursprungligen beskrev).

Loremipsumissimplydummytextoft-  # Line length 30+1 char with an appended a dash
he printing and typesetting     # Cut before the word at otherwise 30 char
industry.

Svar #1

Du kan använda

var s = "Loremipsumissimplydummytextofthe printing and typesetting industry. Loremipsumis simply dummytext ofthe printing and typesetting industry. Loremipsumissimplydummytextofthe printing and typesetting industry. Loremipsumis simply dummytext ofthe printing and typesetting industry.";
var regex = /\s*(?:(\S{30})|([\s\S]{1,30})(?!\S))/g;
console.log(
  s.replace(regex, function($0,$1,$2) { return $1 ? $1 + "-\n" : $2 + "\n"; } )
)

Detaljer

  • \s* - 0 eller fler mellanslagstecken.
  • (?: - början på den icke-fångande gruppen:

    • (\S{30}) - Grupp 1 (refererad med $1-variabeln i callback-metoden): trettio (n) icke-vita tecken
    • | - eller
    • ([\s\S]{1,30})(?!\S)) - Grupp 2 (refereras till med $2-variabeln i callback-metoden): vilken som helst av ett till trettio (n) tecken, så många som möjligt, men inte omedelbart följt av ett icke-vitt utrymmestecken.

Delen function($0,$1,$2) { return $1 ? $1 + "-\n" : $2 + "\n"; } betyder att om Grupp 1 matchar (det vill säga, vi matchade ett mycket långt ord som är uppdelat i två delar), ersätter vi matchningen med Grupp 1:s värde + bindestreck och en ny rad. Annars, om Grupp 2 matchar, ersätter vi med Grupp 2:s värde + en ny rad.

ES6+ kompatibel kodsnutt:

const text = "Loremipsumissimplydummytextofthe printing and typesetting industry. Loremipsumis simply dummytext ofthe printing and typesetting industry. Loremipsumissimplydummytextofthe printing and typesetting industry. Loremipsumis simply dummytext ofthe printing and typesetting industry.";
const lineMaxLen = 30;
const wsLookup = 15; // Look backwards n characters for a whitespace
const regex = new RegExp(String.raw`\s*(?:(\S{${lineMaxLen}})|([\s\S]{${lineMaxLen - wsLookup},${lineMaxLen}})(?!\S))`, 'g');
console.log(
  text.replace(regex, (_, x, y) => x ? `${x}-\n` : `${y}\n`)
);

Svar #2

Slutgiltigt svar:

(\S[\s\S]{1,30}$|\S[\s\S]{1,29}(?:\s+)|\S{30})

evolution :

  1. ([\s\S]{1,15}(?!\S)|\S{15,})

du behöver bara ändra svaret i länken med ett 'eller'-påstående som lägger till ditt ytterligare krav: |\S{15,}

  1. som svar på dina ändringar, här är mitt modifierade regex: ([\s\S]{1,15}(?!\S)|\S{15})

du kan ersätta 15s med 30 eller den teckenuppsättning du väljer

  1. justerar för dina ytterligare förtydliganden: (\S[\s\S]{1,14}(?:\s*)|\S{15})

Nu måste strängen börja med ett icke-vitt tecken och den matchar men fångar inte ytterligare mellanslag efter de första 15 tecknen. Återigen måste du ändra 15 och 14 till de längder du vill ha.

  1. (\S[\s\S]{1,30}$|\S[\s\S]{1,29}(?:\s+)|\S{30}) Lägger till ett annat villkor i det flera 'eller'-uttrycket i början som fångar slutet av strängen om det slutar med ett icke-mellanslagstecken. Om det slutade med ett mellanslagtecken fångar den andra delen av 'eller'-uttrycket det.