Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Лучший алгоритм переноса слов?
Перенос слов-это одна из обязательных функций современного текстового редактора.
Как можно обрабатывать перенос слов? Каков наилучший алгоритм для переноса слов?
Если текст состоит из нескольких миллионов строк, как я могу сделать перенос слов очень быстро?
Зачем мне нужно это решение? Потому что мои проекты должны рисовать текст с различным уровнем масштабирования и одновременно красивым внешним видом.
Рабочая среда - это Windows мобильных устройств. Максимальная скорость 600 MHz при очень малом объеме памяти.
Как я должен обрабатывать информацию о линии? Предположим, что исходные данные имеют три строки.
THIS IS LINE 1.
THIS IS LINE 2.
THIS IS LINE 3.
После этого текст разрыва будет показан следующим образом:
THIS IS
LINE 1.
THIS IS
LINE 2.
THIS IS
LINE 3.
Должен ли я выделить еще три строки? Или еще какие-нибудь предложения?
Вот алгоритм переноса слов, который я написал в C#., он должен быть довольно легко переведен на другие языки (за исключением, возможно, IndexOfAny ).
static char[] splitChars = new char[] { ' ', '-', '\t' };
private static string WordWrap(string str, int width)
{
string[] words = Explode(str, splitChars);
int curLineLength = 0;
StringBuilder strBuilder = new StringBuilder();
for(int i = 0; i < words.Length; i += 1)
{
string word = words[i];
// If adding the new word to the current line would be too long,
// then put it on a new line (and split it up if it's too long).
if (curLineLength + word.Length > width)
{
// Only move down to a new line if we have text on the current line.
// Avoids situation where wrapped whitespace causes emptylines in text.
if (curLineLength > 0)
{
strBuilder.Append(Environment.NewLine);
curLineLength = 0;
}
// If the current word is too long to fit on a line even on it's own then
// split the word up.
while (word.Length > width)
{
strBuilder.Append(word.Substring(0, width - 1) + "-");
word = word.Substring(width - 1);
strBuilder.Append(Environment.NewLine);
}
// Remove leading whitespace from the word so the new line starts flush to the left.
word = word.TrimStart();
}
strBuilder.Append(word);
curLineLength += word.Length;
}
return strBuilder.ToString();
}
private static string[] Explode(string str, char[] splitChars)
{
List<string> parts = new List<string>();
int startIndex = 0;
while (true)
{
int index = str.IndexOfAny(splitChars, startIndex);
if (index == -1)
{
parts.Add(str.Substring(startIndex));
return parts.ToArray();
}
string word = str.Substring(startIndex, index - startIndex);
char nextChar = str.Substring(index, 1)[0];
// Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to.
if (char.IsWhiteSpace(nextChar))
{
parts.Add(word);
parts.Add(nextChar.ToString());
}
else
{
parts.Add(word + nextChar);
}
startIndex = index + 1;
}
}
Это довольно примитивно-он разбивается на пробелы, табуляции и тире. Он действительно гарантирует, что тире прилипнет к слову перед ним (так что вы не закончите с stack\n-overflow), хотя он не поддерживает перемещение маленьких дефисных слов в новую строку, а не их разделение. Он действительно разбивает слова, если они слишком длинны для строки.
Это также довольно специфично с точки зрения культуры, поскольку я не очень хорошо знаю правила обертывания слов в других культурах.
Дональд Э. кнут проделал большую работу над алгоритмом разрыва строк в своей системе набора TeX. Это, пожалуй, один из лучших алгоритмов для разрыва строки - "best" с точки зрения визуального отображения результата.
Его алгоритм позволяет избежать проблем жадного заполнения линий, когда вы можете получить очень плотную линию, а затем очень свободную линию.
Эффективный алгоритм может быть реализован с помощью динамического программирования.
Я не знаю, будет ли кто-нибудь когда-нибудь читать это, видя, насколько стар этот вопрос, но недавно мне довелось написать функцию переноса слов, и я хочу поделиться тем, что я придумал. Я использовал подход TDD почти такой же строгий, как и в Примере Go. Я начал с теста, что обертывание строки "Hello, world!" при ширине 80 должно ясно возвращать "Hello, World!", самое простое, что работает, - это вернуть входную строку нетронутой. Начиная с этого, я делал все более и более сложные тесты и в конечном итоге получил рекурсивное решение, которое (по крайней мере, для моих целей) довольно эффективно справляется с задачей.
Псевдокод для рекурсивного решения:
Function WordWrap (inputString, width)
Trim the input string of leading and trailing spaces.
If the trimmed string's length is <= the width,
Return the trimmed string.
Else,
Find the index of the last space in the trimmed string, starting at width
If there are no spaces, use the width as the index.
Split the trimmed string into two pieces at the index.
Trim trailing spaces from the portion before the index,
and leading spaces from the portion after the index.
Concatenate and return:
the trimmed portion before the index,
a line break,
and the result of calling WordWrap on the trimmed portion after
the index (with the same width as the original call).
Это только обертывание в пробелах, и если вы хотите обернуть строку, которая уже содержит разрывы строк, вам нужно разделить ее на разрывы строк, отправить каждую часть в эту функцию, а затем снова собрать строку. Тем не менее, в VB.NET, работающем на быстрой машине, это может обрабатывать около 20 mb/sec.
Я не знаю никаких конкретных алгоритмов, но не будет ли Ниже приведен примерный план того, как он должен работать:
- Для текущего размера текста, шрифта, размера дисплея, размера окна, полей и т. д. определите, сколько символов может поместиться в строке (если фиксированный тип) или сколько пикселей может поместиться в строке (если не фиксированный тип).
- Пройдите через строку символ за символом, вычисляя, сколько символов или пикселей было записано с начала строки.
- Когда вы пройдете по максимальным символам / пикселям для строки, вернитесь к последнему пробелу / знаку препинания, переместите весь текст на следующую строку.
- Повторяйте, пока не пройдете весь текст в документе.
Вопрос: в .net функция переноса слов встроена в такие элементы управления, как TextBox. Я уверен, что подобная встроенная функциональность существует и для других языков. Есть ли причина, по которой вы не хотите использовать предварительно построенное решение? Это похоже на изобретение нового колеса.
С переносами или без них?
Без этого все просто. Просто инкапсулируйте свой текст как wordobjects per word и дайте им метод getWidth(). Затем начните с первого слова, складывая длину строки до тех пор, пока она не будет больше, чем доступное пространство. Если это так, оберните последнее слово и снова начните считать для следующей строки, начиная с этой, и т. д.
С переносом вам нужны правила переноса в общем формате, например: hy-phen-a-tion
Тогда это то же самое, что и выше, за исключением того, что вам нужно разделить последнее слово, которое вызвало переполнение.
Хороший пример и учебник того, как структурировать свой код для отличного текстового редактора, приведен в книге Gang of Four Design Patterns . Это один из основных образцов, на котором они показывают узоры.
Я задавался тем же вопросом для моего собственного проекта редактора. Мое решение было двухэтапным процессом:
- Найдите концы линий и сохраните их в массиве.
- Для очень длинных линий найдите подходящие точки разрыва примерно через 1K интервалов и сохраните их в линейном массиве. Это для того, чтобы поймать "4MB text without a single line break".
Когда вам нужно отобразить текст, найдите соответствующие строки и оберните их на лету. Запомните эту информацию в кэше для быстрого перерисовывания. Когда пользователь прокручивает целую страницу, очистите кэш и повторите процедуру.
Если вы можете, сделайте загрузку / анализ всего текста в фоновом потоке. Таким образом, вы уже можете отобразить первую страницу текста, в то время как rest документа все еще рассматривается. Самое простое решение здесь-вырезать первые 16 КБ текста и запустить алгоритм на подстроке. Это очень быстро и позволяет мгновенно отрисовать первую страницу, даже если ваш редактор все еще загружает текст.
Вы можете использовать аналогичный подход, когда курсор изначально находится в конце текста; просто прочитайте последние 16 КБ текста и проанализируйте их. В этом случае используйте два буфера редактирования и загрузите все, кроме последних 16 КБ, в первый, пока пользователь заблокирован во втором буфере. И вы, вероятно, захотите вспомнить, сколько строк имеет текст, когда вы закрываете редактор, чтобы полоса прокрутки не выглядела странно.
Он становится волосатым, когда пользователь может запустить редактор с курсором где-то посередине, но в конечном счете, это только расширение конечной проблемы. Только вам нужно запомнить позицию байта, текущий номер строки и общее количество строк из последней сессии плюс вам нужно три буфера редактирования или вам нужен буфер редактирования, где вы можете вырезать 16 КБ в середине.
Кроме того, можно заблокировать полосу прокрутки и другие элементы интерфейса во время загрузки текста; это позволяет пользователю просматривать текст во время его полной загрузки.
Вот мой, над которым я сегодня работал для удовольствия в C:
Вот мои соображения:
1) Отсутствие копирования символов, просто печать в stdout. Поэтому, поскольку я не люблю изменять аргументы argv[x] и потому что мне нравится вызов, я хотел бы сделать это без изменения. Я не пошел на идею вставки '\n' .
2) я не хочу
This line breaks here
становиться
This line breaks
here
поэтому изменение символов на '\n' не является вариантом, учитывая эту цель.
3) Если ширина строки установлена на уровне, скажем, 80, а 80-й символ находится в середине слова, то все слово должно быть помещено на следующую строку. Поэтому, когда вы сканируете, вы должны помнить положение конца последнего слова, которое не превышало 80 символов.
Итак, вот мой, он не чист; я ломал голову в течение последнего часа, пытаясь заставить его работать, добавляя что-то здесь и там. Это работает для всех крайних случаев, о которых я знаю.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int isDelim(char c){
switch(c){
case '\0':
case '\t':
case ' ' :
return 1;
break; /* As a matter of style, put the 'break' anyway even if there is a return above it.*/
default:
return 0;
}
}
int printLine(const char * start, const char * end){
const char * p = start;
while ( p <= end ) putchar(*p++);
putchar('\n');
}
int main ( int argc , char ** argv ) {
if( argc <= 2 ) exit(1);
char * start = argv[1];
char * lastChar = argv[1];
char * current = argv[1];
int wrapLength = atoi(argv[2]);
int chars = 1;
while( *current != '\0' ){
while( chars <= wrapLength ){
while ( !isDelim( *current ) ) ++current, ++chars;
if( chars <= wrapLength){
if(*current == '\0'){
puts(start);
return 0;
}
lastChar = current-1;
current++,chars++;
}
}
if( lastChar == start )
lastChar = current-1;
printLine(start,lastChar);
current = lastChar + 1;
while(isDelim(*current)){
if( *current == '\0')
return 0;
else
++current;
}
start = current;
lastChar = current;
chars = 1;
}
return 0;
}
Итак, в принципе, у меня есть start и lastChar , которые я хочу установить в качестве начала строки и последнего символа строки. Когда они установлены, я вывожу в stdout все символы от начала до конца, затем выводим '\n' и переходим к следующей строке.
Сначала все указывает на начало, затем я пропускаю слова с while(!isDelim(*current)) ++current,++chars; . При этом я вспоминаю последний символ, который был до 80 символов (lastChar).
Если в конце слова я передал свое число символов (80), то я выхожу из блока while(chars <= wrapLength) . Я вывожу все символы между start и lastChar и A newline .
Затем я устанавливаю current на lastChar+1 и пропускаю разделители (и если это приведет меня к концу строки, мы закончим, return 0). Установите start, lastChar и current в начало следующей строки.
То
if(*current == '\0'){
puts(start);
return 0;
}
часть предназначена для струн, которые слишком коротки, чтобы обернуть их хотя бы один раз. Я добавил Это непосредственно перед написанием этого поста, потому что я попробовал короткую строку, и она не сработала.
Я чувствую, что это может быть выполнимо более элегантным способом. Если у кого-то есть что предложить, я бы с удовольствием попробовал.
И когда я писал это, я спрашивал себя: "что произойдет, если у меня будет строка, состоящая из одного слова, которое длиннее, чем моя длина оболочки?" Ну, это не работает. Поэтому я добавил:
if( lastChar == start )
lastChar = current-1;
перед операцией printLine() (если lastChar не сдвинулся, то у нас есть слово, которое слишком длинно для одной строки, поэтому мы просто должны поместить все это в строку в любом случае).
Я взял комментарии из кода, так как я пишу это, но я действительно чувствую, что должен быть лучший способ сделать это, чем то, что у меня есть, что не нуждалось бы в комментариях.
Вот и вся история о том, как я написал эту вещь. Я надеюсь, что это может быть полезно людям, и я также надеюсь, что кто-то будет недоволен моим кодом и предложит более элегантный способ его выполнения.
Следует отметить, что он работает для всех крайних случаев: слова слишком длинные для строки, строки, которые короче единицы wrapLength, и пустые строки.
Вот решение в C#. пролилось единственное слово с превышением данного предела и остальные слова остались как обычно.
/// <summary>
/// Word wraps the given text to fit within the specified width.
/// </summary>
/// <param name="text">Text to be word wrapped</param>
/// <param name="width">Width, in characters, to which the text
/// should be word wrapped</param>
/// <returns>The modified text</returns>
public static string WordWrap(string text, int width)
{
int pos, next;
StringBuilder sb = new StringBuilder();
// Lucidity check
if (width < 1)
return text;
// Parse each line of text
for (pos = 0; pos < text.Length; pos = next)
{
// Find end of line
int eol = text.IndexOf(Environment.NewLine, pos);
if (eol == -1)
next = eol = text.Length;
else
next = eol + Environment.NewLine.Length;
// Copy this line of text, breaking into smaller lines as needed
if (eol > pos)
{
do
{
int len = eol - pos;
if (len > width)
len = BreakLine(text, pos, width);
sb.Append(text, pos, len);
sb.Append(Environment.NewLine);
// Trim whitespace following break
pos += len;
while (pos < eol && Char.IsWhiteSpace(text[pos]))
pos++;
} while (eol > pos);
}
else sb.Append(Environment.NewLine); // Empty line
}
return sb.ToString();
}
/// <summary>
/// Locates position to break the given line so as to avoid
/// breaking words.
/// </summary>
/// <param name="text">String that contains line of text</param>
/// <param name="pos">Index where line of text starts</param>
/// <param name="max">Maximum line length</param>
/// <returns>The modified line length</returns>
private static int BreakLine(string text, int pos, int max)
{
// Find last whitespace in line
int i = max;
while (i >= 0 && !Char.IsWhiteSpace(text[pos + i]))
i--;
// If no whitespace found, break at maximum length
if (i < 0)
return max;
// Find start of whitespace
while (i >= 0 && Char.IsWhiteSpace(text[pos + i]))
i--;
// Return length of text before whitespace
return i + 1;
}
Я не могу претендовать на bug-free-ness из этого, но мне нужен был тот, что слово обернуто и подчиняется границам отступа. Я ничего не утверждаю об этом коде, кроме того, что он работал для меня до сих пор. Это метод расширения и нарушает целостность StringBuilder, но он может быть сделан с любыми входами/выходами, которые вы хотите.
public static void WordWrap(this StringBuilder sb, int tabSize, int width)
{
string[] lines = sb.ToString().Replace("\r\n", "\n").Split('\n');
sb.Clear();
for (int i = 0; i < lines.Length; ++i)
{
var line = lines[i];
if (line.Length < 1)
sb.AppendLine();//empty lines
else
{
int indent = line.TakeWhile(c => c == '\t').Count(); //tab indents
line = line.Replace("\t", new String(' ', tabSize)); //need to expand tabs here
string lead = new String(' ', indent * tabSize); //create the leading space
do
{
//get the string that fits in the window
string subline = line.Substring(0, Math.Min(line.Length, width));
if (subline.Length < line.Length && subline.Length > 0)
{
//grab the last non white character
int lastword = subline.LastOrDefault() == ' ' ? -1 : subline.LastIndexOf(' ', subline.Length - 1);
if (lastword >= 0)
subline = subline.Substring(0, lastword);
sb.AppendLine(subline);
//next part
line = lead + line.Substring(subline.Length).TrimStart();
}
else
{
sb.AppendLine(subline); //everything fits
break;
}
}
while (true);
}
}
}
@ICR, спасибо, что поделились примером C#. Мне не удалось использовать его, но я придумал другое решение. Если есть какой-либо интерес к этому, пожалуйста, не стесняйтесь использовать это: https://web.archive.org/web/20160403050733/http://johan.andersson.net/2010/11/03/wordwrap-function-in-c/ . Источник доступен на GitHub .
Я включил модульные тесты / образцы.
С тем же успехом я могу вмешаться в решение perl, которое я сделал, потому что gnu fold -s оставляла trailing пробелы и другое плохое поведение. Это решение не обрабатывает (должным образом) текст, содержащий вкладки или обратные интервалы, встроенные возвраты каретки или тому подобное, хотя оно обрабатывает CRLF окончание строки, преобразуя их все только в LF. Он вносит минимальные изменения в текст, в частности, он никогда не разбивает слово (не изменяет wc -w), а для текста с не более чем одним пробелом в строке (и без CR) он не изменяет wc -c (потому что заменяет пробел на LF, а не вставляет LF).
#!/usr/bin/perl
use strict;
use warnings;
my $WIDTH = 80;
if ($ARGV[0] =~ /^[1-9][0-9]*$/) {
$WIDTH = $ARGV[0];
shift @ARGV;
}
while (<>) {
s/\r\n$/\n/;
chomp;
if (length $_ <= $WIDTH) {
print "$_\n";
next;
}
@_=split /(\s+)/;
# make @_ start with a separator field and end with a content field
unshift @_, "";
push @_, "" if @_%2;
my ($sep,$cont) = splice(@_, 0, 2);
do {
if (length $cont > $WIDTH) {
print "$cont";
($sep,$cont) = splice(@_, 0, 2);
}
elsif (length($sep) + length($cont) > $WIDTH) {
printf "%*s%s", $WIDTH - length $cont, "", $cont;
($sep,$cont) = splice(@_, 0, 2);
}
else {
my $remain = $WIDTH;
{ do {
print "$sep$cont";
$remain -= length $sep;
$remain -= length $cont;
($sep,$cont) = splice(@_, 0, 2) or last;
}
while (length($sep) + length($cont) <= $remain);
}
}
print "\n";
$sep = "";
}
while ($cont);
}