Форматирование строки в несколько строк определенной длины в C / C ++ - PullRequest
5 голосов
/ 31 июля 2011

Существует ли общая библиотека C / C ++ (или общая методика) для выделения строки (строк) входного текста и разбиения слов на отдельные строки. Где каждая строка вывода имеет максимальную ширину, а слова не разбиты на строки. Пробел, свернутый или сохраненный, в порядке. Пунктуация должна быть сохранена. Небольшая и компактная библиотека предпочтительнее.

Я мог бы легко провести день, собирая что-то, что работает, но хотел бы знать, есть ли что-то общее, поэтому я не изобретаю колесо заново. Бонусные баллы, если строка ввода может содержать спецификатор формата для указания уровня отступа для строк вывода.

Пример ввода: "Голень голень" солонина, говядина, куриная свиная отбивная, оленина, говяжья говяжья вырезка, говяжья вырезка, стейк из коровьей говядины. Хвост с короткой поясничной частью плеча, крупа голени с голени. короткая поясница andouille.

Пример вывода (целевая ширина = 60)

123456789012345678901234567890123456789012345678901234567890   Line added to show where 60 is
Shankle drumstick corned beef, chuck turkey chicken pork
chop venison beef strip steak cow sausage. Tail short loin
shoulder ball tip, jowl drumstick rump. Tail tongue ball tip
meatloaf, bresaola short loin tri-tip fatback pork loin
sirloin shank flank biltong. Venison short loin andouille.

Ответы [ 8 ]

1 голос
/ 01 августа 2011

Если вы хотите выполнить работу в C, вы можете попробовать w_wrap.c и w_wrap.h , которые я опубликовал в Fidonet C_ECHO 20 лет назад или около того.

Если выЕсли вы хотите выполнить работу в C ++, кажется, что вы могли бы немного упростить код:

#include <sstream>
#include <string>
#include <iostream>

void wrap(std::string const &input, size_t width, std::ostream &os, size_t indent = 0)
{ 
    std::istringstream in(input);

    os << std::string(indent, ' '); 
    size_t current = indent;
    std::string word;

    while (in >> word) {
        if (current + word.size() > width) {
            os << "\n" << std::string(indent, ' ');
            current = indent;
        }
        os << word << ' ';
        current += word.size() + 1;
    }
}

#ifdef TEST 
int main() { 
    char *in = "Shankle drumstick corned beef, chuck turkey chicken pork chop"
               " venison beef strip steak cow sausage. Tail short loin shoulder"
               " ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf,"
               " bresaola short loin tri-tip fatback pork loin sirloin shank"
               " flank biltong. Venison short loin andouille.";

    wrap(in, 60, std::cout);
    return 0;
}
#endif

Чтобы добавить отступ, вы должны использовать что-то вроде:

wrap(in, 60, std::cout, 5);

Учитывая, чтовы делаете ввод / вывод, это, вероятно, не имеет большого значения в этом случае, но если вы делали это при других обстоятельствах, вы можете рассмотреть другой алгоритм.Вместо того, чтобы копировать по одному слову за раз, пока вы не превысите указанную ширину, вы можете перейти непосредственно к максимальной ширине строки во входных данных и пройти назад по входной строке оттуда, пока не найдете пробел.По крайней мере, учитывая типичную длину слова, вы пройдете только где-то около 3 символов в среднем, а не пройдете вперед в среднем (скажем) 60 символов.Это было бы особенно актуально, если бы вы использовали что-то вроде строки C, где вы сохраняли указатель на начало каждой строки, не копируя содержимое.

1 голос
/ 31 июля 2011

Я думаю, что вы можете искать:

char temp[60];
int cnt, x = 0;
do
{
    cnt = 59;
    strncpy(temp, src + x, 60); //Assuming the original is stored in src
    while(temp[cnt] != ' ') cnt --;
    temp[cnt] = (char) 0;
    x += cnt + 1;
    printf("%s\n", temp);
}while (x < strlen(src));
1 голос
/ 31 июля 2011

Вот небольшая функция, с помощью которой вы можете делать, что хотите. Возвращает list строк. Вы можете удалить все std::, если хотите, на using namespace std; или лучше using std::list; using std::string; using std::size_t;, но я не хотел предполагать, что вы это сделали.

list<string> wraptext(string input, size_t width) {
    size_t curpos = 0;
    size_t nextpos = 0;

    list<string> lines;
    string substr = input.substr(curpos, width + 1);

    while (substr.length() == width + 1 && (nextpos = substr.rfind(' ')) != input.npos) {
        lines.push_back(input.substr(curpos, nextpos));
        curpos += nextpos + 1;
        substr = input.substr(curpos, width + 1);
    }

    if (curpos != input.length())
        lines.push_back(input.substr(curpos, input.npos));

    return lines;
}

Эта программа использует эту функцию:

int main() {
    string input = "Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille.";

    list<string> l = wraptext(input, 60);

    for (auto i = l.begin(); i != l.end(); ++i)
        cout << *i << endl;

    cin.get();
}

Печатает ваш пример текста:

Shankle drumstick corned beef, chuck turkey chicken pork
chop venison beef strip steak cow sausage. Tail short loin
shoulder ball tip, jowl drumstick rump. Tail tongue ball tip
meatloaf, bresaola short loin tri-tip fatback pork loin
sirloin shank flank biltong. Venison short loin andouille.
0 голосов
/ 13 января 2019

Вот подход, основанный на регулярных выражениях.В отличие от подходов в других ответах, он также корректно обрабатывает символы новой строки во входной строке.

#include <regex>
#include <iostream>
#include <string>

int main() {
  auto test = std::string{"Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille."};

  // Consume 60 characters that are followed by a space or the end of the input string
  auto line_wrap = std::regex{"(.{1,60})(?: +|$)"};

  // Replace the space or the end of the input string with a new line
  test = regex_replace(test, line_wrap, "$1\n");

  // Trim the new line added for the end of the input string
  test.resize(test.size() - 1);

  std::cout << test << std::endl;
}
0 голосов
/ 01 августа 2011

Возможно, вы могли бы использовать подстановку регулярных выражений: замените /(.*){,60}? +/ на $1\n, продвиньте указатель строки и повторите (примечание: ? должно означать не жадное сопоставление).

При правильном выполнении преобразование может быть даже выполнено на месте.

0 голосов
/ 01 августа 2011

Вот мой подход, он, конечно, не самый быстрый, но я старался сделать его максимально читабельным.Результат такой же, как в вашем примере.

#include <iostream>
#include <string>


std::string splitInLines(std::string source, std::size_t width, std::string whitespace = " \t\r")
{
    std::size_t  currIndex = width - 1;
    std::size_t  sizeToElim;
    while ( currIndex < source.length() )
    {
        currIndex = source.find_last_of(whitespace,currIndex + 1); 
        if (currIndex == std::string::npos)
            break;
        currIndex = source.find_last_not_of(whitespace,currIndex);
        if (currIndex == std::string::npos)
            break;
        sizeToElim = source.find_first_not_of(whitespace,currIndex + 1) - currIndex - 1;
        source.replace( currIndex + 1, sizeToElim , "\n");
        currIndex += (width + 1); //due to the recently inserted "\n"
    }
    return source;
}

int main() {
    std::string source = "Shankle drumstick corned beef, chuck turkey chicken pork chop venison beef strip steak cow sausage. Tail short loin shoulder ball tip, jowl drumstick rump. Tail tongue ball tip meatloaf, bresaola short loin tri-tip fatback pork loin sirloin shank flank biltong. Venison short loin andouille.";
    std::string result = splitInLines(source , 60);
    std::cout << result;
    return 0;
}
0 голосов
/ 01 августа 2011

возьмите функцию для вашей работы, как:

void put_multiline(const char *s,int width)
{
  int n,i=0;
  char t[100];
  while( 1==sscanf(s,"%99s%n",t,&n) )
  {
    if( i+strlen(t)>width ) puts(""),i=0;
    printf("%s%s",i?++i," ":"",t);i+=strlen(t);
    s+=n;
  }
}

strtok уничтожит вашу строку, этого решения нет. Эта функция также будет работать с всеми пробелами, а не только пробелом / табуляцией.

0 голосов
/ 31 июля 2011

Да, загрузите его в массив символов, затем используйте strtok , чтобы разбить его на слова, используя пробел в качестве разделителя слов.

...