Denna vecka tänkte jag vi skulle leka lite med stränghantering jag är väl först ut då... Och hur svårt var det, då? *PHP rules* ;-) fast den bryter långa ord utan att bryta med "-" Hej! Mitt bidrag då. Nu fungerar den: Får väl lämna två bidrag då, med min favoritfunktion alla kategorier ; String.Split(). Mina funktioner är ganska lika Rogers, men jag har som sagt valt att använda Split() i stället för att parsa tecken för tecken. Överkurs. anledningen att jag valde att inte använda split var av prestandaskäl, Roger : Självklart, allt du skriver är jag med på, men jag är av naturen lat och väljer alltid den enklaste lösning som fungerar. Dessutom optimerar jag ALDRIG kod som inte behöver optimeras, speciellt inte som optimering ofta påverkar läsbarheten negativt. I det här fallet så var det inte tal om stora textmassor så den enda prestandan jag brydde mig om var hur snabbt jag kunde få fram en funktion som fungerar :-)Veckans nöt #3
<info>
Du tänker dig att du har en mening på 50 ord. Du har sedan bestämt dig att du inte får skriva mer än 30 tecken per rad och sedan måste radbryta. Givetvis kan du inte bryta mitt i ett ord utan får antingen bara bryta efter ordet och ta ny rad alternativt dela ett ord med "-" och därefter ny rad.
</info>
<info>
Mening="Det var en gång en liten gubbe som satt på en stubbe. Stubben var alldeles förskräckligt blöt av mossa som blivit indränkt av morgondaggen. Mannen reste sig upp och svor, borstade av sig och lunkade vidare för att gå hem till stugan och dricka en kopp varmt kaffe."
</info>
Hur skulle du göra detta med programkod?Sv: Veckans nöt #3
public static string WordWrap(string Text)
{
System.Text.StringBuilder sb=new System.Text.StringBuilder();
int columns=30; // antal kolumner per rad
int startIndex=0; //scanna från början av texten
while(true)
{
if (startIndex >= Text.Length - columns)
{
//hantera den sista lilla snutten som inte ska wrappas
sb.Append (Text.Substring (startIndex));
break;
}
else
{
//hantera rader som ska wrappas
for (int currentIndex=startIndex+columns;currentIndex>=startIndex;currentIndex--)
{
if (Text[currentIndex]==' ')
{
//hantera radbrytning på " "
sb.Append(Text.Substring (startIndex,currentIndex-startIndex));
sb.Append (Environment.NewLine);
startIndex = currentIndex+1;
break;
}
else if (currentIndex == startIndex)
{
//bryt rader som är för långa
currentIndex += columns -1; //räkna av 1 tecken för att ge plats åt "-"
sb.Append(Text.Substring (startIndex,currentIndex-startIndex));
sb.Append("-");
sb.Append (Environment.NewLine);
startIndex = currentIndex;
break;
}
}
}
}
//returnera texten
return sb.ToString ();
}
Kanske inte den kortaste rutien man kan göra men den bör vara skapligt snabb,,
på den angivna texten så utförs totalt 13 teckenjämförelser av totalt 263 tecken..
dvs den scannar i snitt 1 av 20 tecken.
//Roger
Sv: Veckans nöt #3
<?php
$mening = 'Det var en gång en liten gubbe som satt på en stubbe. Stubben var aldeles förskräckligt
blöt av mossa som blivit indränkt av morgondaggen. Mannen reste sig upp och svor, borstade av sig
och lunkade vidare för att gå hem till stugan och dricka en kopp varmt kaffe.';
echo wordwrap($mening, 30, '<br>');
?>
Sv: Veckans nöt #3
så den faller på "Givetvis kan du inte bryta mitt i ett ord ..." ;) :P
//RogerSv: Veckans nöt #3
Har man valt en utvecklingsmiljö som inte tillhandahåller en färdig funktion (som Per),
får man bygga den själv.
Har valt att bryta på mellanslag, om position 30 är i ett ord.
Backar då tillbaka till första påträffade mellanslaget.
Att infoga - kan ge konstiga avstavningar, då man inte vet var i ordet det infogas.
Finjusterad:
<code>
void WordWrapper( CString &strText, int iLineBreak )
{
CString strTemp;
int iWrapIndex = 0;
if( strText.IsEmpty() || strText.GetLength() <= iLineBreak )
{
return;
}
while( strText.GetLength() - iLineBreak > 0 )
{
if( strText.GetAt( iLineBreak ) == ' ' )
{
strText.Insert( iLineBreak + 1, "\r\n" );
iLineBreak += 30;
}
else
{
strTemp = strText.Left( iLineBreak );
iWrapIndex = strTemp.ReverseFind( ' ' );
strText.Insert( iWrapIndex + 1, "\r\n" );
iLineBreak = iWrapIndex + 30;
}
}
}//WordWrapper
</code>
//HåkanSv: Veckans nöt #3
Tror allt ska vara rätt :)
om ni hittar något som man kan förbättra säg till. :)
<code>
void WarpLine(string Mening,int Chars)
{
bool End=false;
int Line = Chars;
while(!End)
{
for(int i=Line;i<Line+10;i++)
{
if(Mening.substr(i,1)==" ")
{
Mening.insert(i+1,"\r\n");
break;
}
}
Line = Line + 30;
if(Line>Mening.length()-30)
{
End=true;
}
}
cout << Mening << "\n";
}
...
WarpLine(Mening,30);
</code>Sv: Veckans nöt #3
<?php
$text = 'Det var en gång en liten gubbe som satt på en stubbe. Stubben var aldeles förskräckligt blöt av
mossa som blivit indränkt av morgondaggen.
kdsjakjkjdjaksjdfjsiajdöijfisjaidjvkijasdjvkcashöfjdhjsahjfhdsahfjdhsajkfhjdshajf. Mannen reste sig upp och
svor, borstade av sig och lunkade vidare för att gå hem till stugan och dricka en kopp varmt kaffe.';
function my_wordwrap($text)
{
return nl2br(implode("\n",array_map(create_function('$word','return
wordwrap($word,30,"-\n",true);'),explode("\n", wordwrap($text, 30, "\n", false)))));
}
$text = my_wordwrap($text, 30, "\n", '-');
//echo '<pre>', $text, '</pre>';
echo $text;
?>
Sv: Veckans nöt #3
Den första funktionen splittar bara med mellanslag, medans den andra splittar med bindestreck om orden är längre 5 tecken och "överhänget" (eller "underhänget"?) är 3 tecken eller mer för att avstavningarna inte ska bli löjliga. Fortfarande är dom ju inte korrekta, de ska väl egentligen göras mellan stavelser i ordet, men det skiter jag i :-)
private string SimpleWordWrap(string message,int maxlength)
{
int sentence=0;
System.Text.StringBuilder builder=new System.Text.StringBuilder();
string[] words=message.Split(' ');
foreach (string word in words)
{
sentence+=word.Length;
if (sentence>maxlength)
{
builder.Append(Environment.NewLine);
sentence=word.Length;
}
else
builder.Append(' ');
builder.Append(word);
}
return builder.ToString().TrimStart(' ');
}
private string WordWrap(string message,int maxlength)
{
int sentence=0;
System.Text.StringBuilder builder=new System.Text.StringBuilder();
string[] words=message.Split(' ');
string cutword="";
foreach (string word in words)
{
cutword=word;
sentence+=word.Length;
if (sentence>maxlength)
{
if (Math.Abs(sentence-maxlength)>3 && word.Length>5)
{
builder.Append(' ');
builder.Append(word.Substring(0,sentence-maxlength-1));
builder.Append('-');
cutword=word.Substring(sentence-maxlength-1);
}
builder.Append(Environment.NewLine);
sentence=cutword.Length;
}
else
builder.Append(' ');
builder.Append(cutword);
}
return builder.ToString().TrimStart(' ');
}
Sv: Veckans nöt #3
Bryt långa ord med "-"
enligt grammatiska regler.
T.ex om ordet "hemsida" börjar på position 25
bör det brytas
hem-
sida
och inte
hems-
ida
Det blir lite svårare.. :)Sv: Veckans nöt #3
inte för att via har något krav på den här koden , men om man antar att man vill wordwrappa en väldigt stor text så kommer split bli långsammare än mitt exempel.
när man gör split , så går split rutinen tecken för tecken igenom hela texten och letar efter ditt splittecken och när ett splittecken hittas så läggs ordet till i en lista.
vilket i det här fallet skulle resultera i 263 teckenjämförelser (ointressant här , men har ganska stor betydelse om man ska skicka in en fil på 1mb eller så)
och div arrayallokeringar sker oxo.
i mitt exempel så snittade den på 1 teckenjämförelse på 20 tecken och inga arrayallokeringar.
så , rätt kompilerat skulle den koden bli ungefär 20 ggr snabbare än en variant som är baserad på split.
(eftersom klasslibbet i .net är .net så exekverar den koden inte snabbare än din egna kod)
skulle man däremot göra samma lösningar i tex vb6 eller i ett scriptsoråk så är det mycket möjligt att split varianten skulle bli betydligt snabbare.
eftersom scriptkod exekveras långsammt och splitfunktionen förmodligen är skriven som native kod i tillhörande scripttolk.
så det är ju inte alls omöjligt att det skulle gå långsammare att jämföra 1 av 20 tecken via scriptkod än att låta bakomliggande nativemetoder utföra en split som kollar alla tecken..
så det kan ju oxo vara något att tänka på , att olika lösningar kan lämpa sig olika bra beroende på språk..
//RogerSv: Veckans nöt #3