Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML 2.X] Wrapper tekstu - dzieleniu tekstu na linie

Ostatnio zmodyfikowano 2025-04-23 17:23
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Wrapper tekstu - dzieleniu tekstu na linie
» 2025-04-22 16:17:42
Witam. Mam algorytm na dzielenie tekstu ale nie działa dobrze z tekstem "test\ntest". Algorytm pomija ostatnią linię. ChatGPT nie pomógł ... :-/

Chodzi o to by podzielić tekst na linie o określonej długości z uwzględnieniem enterów.

Wejście Wyjście
test\ntest
test
test
test test
test test
test\n test
test
 test
 test\ntest
 test
test

C/C++
std::vector < std::wstring > wrapText( std::wstring text, const sf::Font & font, short characterSize, short maxWidth ) {
   
   
std::vector < std::wstring > wrappedText;
   
   
std::wistringstream wordsStream( text );
   
std::wstring word;
   
std::wstring currentLine;
   
   
while( std::getline( wordsStream, word, L' ' ) ) {
       
       
// przetwarzanie enterów
       
std::size_t newLinePos;
       
while(( newLinePos = word.find( L'\n' ) ) != std::wstring::npos ) {
           
           
std::wstring beforeNewLine = word.substr( 0, newLinePos );
           
std::wstring testLine =( currentLine.empty() ) ? beforeNewLine
                : currentLine + L" " + beforeNewLine;
           
sf::Text testText( testLine, font, characterSize );
           
           
if( !testLine.empty() && testText.getLocalBounds().width >= maxWidth ) {
               
wrappedText.push_back( currentLine );
               
currentLine = beforeNewLine;
           
}
           
else {
               
currentLine = testLine;
           
}
           
           
wrappedText.push_back( currentLine );
           
currentLine.clear();
           
word = word.substr( newLinePos + 1 );
       
}
       
       
// przetwarzanie tekstu
       
std::wstring testLine =( currentLine.empty() ) ? word
            : currentLine + L" " + word;
       
sf::Text testText( testLine, font, characterSize );
       
       
if( maxWidth > 0.0f && testText.getLocalBounds().width >= maxWidth ) {
           
wrappedText.push_back( currentLine );
           
currentLine = word;
       
}
       
else {
           
currentLine = testLine;
       
}
    }
   
   
if( !currentLine.empty() ) {
       
wrappedText.push_back( currentLine );
   
}
   
   
if( wrappedText.size() == 0 )
       
 wrappedText.push_back( L"" );
   
   
return wrappedText;
}
P-182270
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-22 18:03:26
Ok mocno pomęczyłem ChatGPT z Deep research i się udało..
Oto kod. Niech Wam służy :-)

C/C++
std::vector < std::wstring > wrapText( std::wstring text, const sf::Font & font, short characterSize, short maxWidth ) {
   
std::vector < std::wstring > result;
   
std::wistringstream lineStream( text );
   
std::wstring paragraph;
   
std::wstring currentLine;
   
   
while( std::getline( lineStream, paragraph, L'\n' ) ) {
       
std::wistringstream wordStream( paragraph );
       
std::wstring word;
       
       
while( wordStream >> word ) {
           
std::wstring testLine = currentLine.empty() ? word
                : currentLine + L" " + word;
           
           
if( maxWidth > 0 ) {
               
sf::Text sfText( testLine, font, characterSize );
               
float width = sfText.getLocalBounds().width;
               
               
if( width > maxWidth ) {
                   
if( !currentLine.empty() ) {
                       
result.push_back( currentLine );
                       
currentLine = word;
                   
}
                   
else {
                       
result.push_back( word );
                       
currentLine.clear();
                   
}
                }
               
else {
                   
currentLine = testLine;
               
}
            }
           
else {
               
currentLine = testLine; // brak ograniczenia szerokości
           
}
        }
       
       
// Dodaj linię po każdym enterze (nawet jeśli pusta lub kończy się spacją)
       
result.push_back( currentLine );
       
currentLine.clear();
   
}
   
   
// Dodaj pozostałą zawartość jeśli nie zakończyło się na '\n'
   
if( !currentLine.empty() ) {
       
result.push_back( currentLine );
   
}
   
   
// Jeśli tekst kończył się na \n, dodaj pustą linię
   
if( !text.empty() && text.back() == L'\n' ) {
       
result.push_back( L"" );
   
}
   
   
if( result.empty() ) {
       
result.push_back( L"" );
   
}
   
   
return result;
}
P-182271
nanoant20
» 2025-04-22 20:18:32
well done. widać, że zależy Ci na jakości i detalu, skorzystałem z Twojej hojności
P-182272
pekfos
» 2025-04-22 23:15:15
C/C++
// Dodaj linię po każdym enterze (nawet jeśli pusta lub kończy się spacją)
result.push_back( currentLine );
currentLine.clear();
Nie kończy się spacją nigdy, wynika z konstrukcji wartości currentLine. Natomiast dodanie pustej linii jest dziwnym wyborem, bo to tak nigdy nie działa. Gdy masz dwa paragrafy podzielone na kilka linii przez ograniczenie szerokości, nie ma takiej magicznej szerokości okna przy której pojawi się pusta linia między nimi.

C/C++
// Dodaj pozostałą zawartość jeśli nie zakończyło się na '\n'
if( !currentLine.empty() ) {
   
result.push_back( currentLine );
}
Ten warunek jest zawsze fałszywy.
P-182273
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-22 23:32:51
Ale algorytm działa dla wszystkich przypadków... i działa w Edytorze

P-182274
nanoant20
» 2025-04-23 09:04:40
@tBane tam gdzie @pekfos pisze że warunek
if( !currentLine.empty() )
 jest zawsze fałszywy możesz usunąć.
Dla tego przypadku "test\n test" gdzie zaczyna się od spacji też nie działa. Dobrze by było dodać logikę obsługę białych znaków spacji czy tabulatorów
P-182275
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-23 09:40:17
Jak usunę ten warunek to doda mi pustą linię ... ale faktycznie ze spacją na początku nie działa. Nie wiem jak to naprawić.
P-182276
nanoant20
» 2025-04-23 15:49:30
do sprawdzenia, funkcja std::vector<std::wstring> wrapText przebudowana przez copilot.microsoft.com

 

copilot
Funkcja wrapText dzieli tekst na linie, starając się, aby szerokość każdej linii nie przekraczała maxWidth.
Zachowuje również początkowe spacje (indent - wcięcie) w akapitach.


C/C++
#include <SFML/Graphics.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <locale>
#include <codecvt>
#include <sstream>  
// std::wistringstream

std::vector < std::wstring > wrapText( const std::wstring & text, const sf::Font & font, short characterSize, short maxWidth )
{
   
std::vector < std::wstring > result;
   
std::wistringstream lineStream( text );
   
std::wstring paragraph;
   
   
// Przetwarzamy tekst linia po linii (rozdzielone znakiem '\n')
   
while( std::getline( lineStream, paragraph, L'\n' ) )
   
{
       
std::wstring currentLine;
       
       
// Sprawdzamy, czy akapit rozpoczyna się spacjami (np. indent)
       
if( !paragraph.empty() && paragraph[ 0 ] == L' ' )
       
{
           
// Znajdź liczbę początkowych spacji.
           
size_t firstNonSpace = paragraph.find_first_not_of( L' ' );
           
if( firstNonSpace == std::wstring::npos )
           
{
               
// Akapit zawiera tylko spacje – dodajemy go do wyniku i przechodzimy do kolejnej linii
               
result.push_back( paragraph );
               
continue;
           
}
           
// Zachowujemy indent w currentLine i usuwamy je z akapitu.
           
currentLine = paragraph.substr( 0, firstNonSpace );
           
paragraph = paragraph.substr( firstNonSpace );
       
}
       
       
// Jeśli po usunięciu indentu akapit jest pusty, dodajemy go (np. linia składająca się tylko ze spacji)
       
if( paragraph.empty() )
       
{
           
result.push_back( currentLine );
           
continue;
       
}
       
       
// Używamy stringstream do dzielenia akapitu na "słowa" – operator >> nie zachowa spacji,
        // ale indent już umieszczony w currentLine zostaje.
       
std::wistringstream wordStream( paragraph );
       
std::wstring word;
       
       
while( wordStream >> word )
       
{
           
std::wstring testLine;
           
// Jeśli currentLine jest puste, to testLine to po prostu słowo
            // w przeciwnym razie, wstawiamy spację między bieżącą linią a kolejnym słowem.
           
if( currentLine.empty() )
               
 testLine = word;
           
else
           
{
               
// Jeśli currentLine kończy się spacją (np. indent), nie trzeba dodawać dodatkowej spacji
               
if( currentLine.back() == L' ' )
                   
 testLine = currentLine + word;
               
else
                   
 testLine = currentLine + L" " + word;
               
           
}
           
           
if( maxWidth > 0 )
           
{
               
// Tworzymy obiekt sf::Text, aby zmierzyć szerokość wygenerowanej linii.
               
sf::Text sfText( testLine, font, characterSize );
               
float width = sfText.getLocalBounds().width;
               
               
if( width > maxWidth )
               
{
                   
// Jeśli bieżąca linia (bez nowego słowa) nie jest pusta, dodajemy ją do wyniku i rozpoczynamy nową linię
                   
if( !currentLine.empty() )
                   
{
                       
result.push_back( currentLine );
                       
currentLine = word; // rozpoczęcie nowej linii od nowego słowa
                   
}
                   
else
                   
{
                       
// Jeśli pojedyncze słowo przekracza maxWidth to i tak je dodajemy osobno
                       
result.push_back( word );
                       
currentLine.clear();
                   
}
                }
               
else
               
{
                   
currentLine = testLine;
               
}
            }
           
else
           
{
               
// Brak ograniczenia szerokości – po prostu łączymy słowa
               
currentLine = testLine;
           
}
        }
       
       
// Po przetworzeniu akapitu dodajemy ostatnią linie (nawet jeśli jest pusta albo zawiera tylko indent)
       
result.push_back( currentLine );
       
// currentLine jest czyszczone na potrzeby kolejnego akapitu
       
currentLine.clear();
   
}
   
   
// Jeśli oryginalny tekst kończył się znakiem nowej linii, dodajemy pustą linię.
   
if( !text.empty() && text.back() == L'\n' )
   
{
       
result.push_back( L"" );
   
}
   
   
// Jeśli wynik jest pusty, zwracamy jedną pustą linię.
   
if( result.empty() )
   
{
       
result.push_back( L"" );
   
}
   
   
return result;
}

int main()
{
   
std::setlocale( LC_ALL, "pl_PL.UTF-8" ); // lub "Polish"
   
   
sf::RenderWindow app( sf::VideoMode( 300, 620 ), "Text Wrapping" );
   
   
sf::Font font;
   
if( !font.loadFromFile( "arial.ttf" ) )
   
{
       
std::cout << "Nie udało się załadować czcionki!" << std::endl;
       
return - 1;
   
}
   
   
short characterSize = 24;
   
short maxWidth = 200;
   
   
sf::Text sfText;
   
sfText.setFont( font );
   
sfText.setCharacterSize( characterSize );
   
sfText.setFillColor( sf::Color::White );
   
   
std::vector < std::wstring > TestText =
   
{
       
L"Apetyt rośnie w miarę jedzenia.", L"", L"Bez pracy nie ma kołaczy.\n"
        L"test\n test\n     test"
, L"        ",
       
L"Audaces fortuna iuvat – Odważnym szczęście sprzyja",
       
L"\n    ęóąśłżźćń \n    ĘÓĄŚŁŻŹĆŃ"
   
};
   
   
std::vector < std::wstring > wrappedText;
   
   
for( const auto & text: TestText )
   
{
       
auto wrappedLines = wrapText( text, font, characterSize, maxWidth );
       
wrappedText.insert( wrappedText.end(), wrappedLines.begin(), wrappedLines.end() );
   
}
   
   
while( app.isOpen() )
   
{
       
float xPosition = 25.0f;
       
float yPosition = 20.0f; //
       
       
sf::Event event;
       
while( app.pollEvent( event ) )
       
{
           
if( event.type == sf::Event::Closed )
               
 app.close();
           
       
}
       
       
app.clear( sf::Color::Black );
       
       
for( const auto & line: wrappedText )
       
{
           
sfText.setString( line );
           
sfText.setPosition( xPosition, yPosition );
           
app.draw( sfText );
           
yPosition += characterSize + 5;
       
}
       
       
app.display();
   
}
   
   
return 0;
}


 
P-182277
« 1 » 2
  Strona 1 z 2 Następna strona