1 Востаннє редагувалося Logans (02.01.2014 23:55:50)

Тема: Зчитування з файлу

Вітаю всіх з Новим Роком :)

Власне назва теми це узагальнена тема питання. Питання стоїть трохи інше:
Як можна зчитувати з текстового файлу по декілька слів?

Власне потрібно зчитуванні слова формувати в шингли (послідовність слів).

Приклад шинглів:

Даний текст

Астероїди або малі планети, здебільшого обертаються між орбітами Марса 
і Юпітера й невидимі неозброєним оком.

Текст оброблюється (видаляються всі символи, та літери приводяться до нижнього регістру)

Текст поділений на шингли

астероїди або малі
або малі планети
малі планети здебільшого
планети здебільшого обертаються
здебільшого обертаються між 
між орбітами марса
орбітами марса і
марса і юпітера
юпітера й невидимі
й невидимі неозброєним
невидимі неозброєним оком


1. Є такий варіант: використати два цикла

  • перший, для зчитування доки не кінець файлу

  • другий, для посимвольної перевірки на наявність пробілу

Але такий варінт досить не ефективний при роботі з великими текстами.

2. Є інший варіант (яким я користувався):

  • зчитати весь текст у одну змінну

  • знаходити на кожній ітерації пробіли і вирізати слова формуючи їх в шингли

Також не влаштовує, тому що програмі потрібно буде зчитувати 2 тексти, обробити тексти, розбити на шигли і потім їх перевіряти між собою, що призведе до дуже довгої роботи.

Ось код працюючої програми (з використанням 2-го варіанту):

Прихований текст

#include <iostream>
#include <string>
#include <locale>
#include <fstream>

using namespace std;

// Приведення до нижнього регістру
string toLow(string inputText)
{
    for (int i = 0; i < inputText.length(); i++)
        inputText[i] = tolower(inputText[i]);
    return inputText;
}

// Видалення зайвих символів
string delSymbols(string inputText)
{
    for (int i = 0; i < inputText.length(); i++)
        if ((isalpha(inputText[i]) == false) && (inputText[i] != ' '))
        {
            inputText = inputText.erase(i, 1);
            i--;
        }
    return toLow(inputText);
}

// Видалення зайвих пропусків
string delSpace(string inputText)
{
    for (int i = 0; i < inputText.length(); i++)
        if (inputText[i] == ' ')
        {
            int j;
            j = i + 1;
            while (inputText[j] == ' ')
                inputText.erase(j, 1);
        }
    return delSymbols(inputText);
}

int main()
{
    string stringText, textString;    
    
    ifstream fileUse("TextFile__.txt");

    while (!fileUse.eof())
    {
        getline(fileUse, textString);
        stringText += textString;
    }

    fileUse.close();
    

    //getline(cin, stringText);
    stringText = delSpace(stringText);
    
    // Розбиття тексту на шигли
    const int n = 250;
    string str[n], strWork_01, strWork_02;

    int wordCount = 0;
    int firstSpace, lastSpace, secondSpace;

    stringText += " ";
    strWork_01 = stringText;
    strWork_02 = stringText;
    
    for(int i = 0; i < stringText.length(); i++)
    {
        firstSpace = strWork_01.find(" ");
        if (firstSpace != -1)
        {
            strWork_01.erase(0, firstSpace + 1);
            wordCount++;
        }
        else
            break;
    }

    strWork_01 = stringText;
    
    for(int i = 0; i < wordCount - 2; i++)
    {
        firstSpace = strWork_02.find(" ");
        strWork_02.erase(0, firstSpace + 1);
        secondSpace = firstSpace + strWork_02.find(" ");
        strWork_02.erase(0, secondSpace - firstSpace + 1);
        lastSpace = secondSpace + strWork_02.find(" ");
        secondSpace += 1;
        lastSpace += 2;
        str[i] = strWork_01.substr(0, lastSpace);
        strWork_01.erase(0, firstSpace + 1);
        strWork_02 = strWork_01;
        cout << str[i] << endl;
    }
    
    cout << endl;    
    system("pause");
    return 0;
}

Ось текстовий файл з яким працює програма:

Post's attachments

TextFile__.txt 1.43 kb, 278 downloads since 2014-01-02 

"Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program."
- Linus Torvalds
Подякували: Chemist-i1

2

Re: Зчитування з файлу

Почитайте про istringstream (є гарні приклади, у т.ч. - на StackOverflow).

Як варіант економії нервів - зчитати через istringstream всі слова до списку/вектора/абощо, а потім обробляти згідно власного фен-шуя. :)

З.І: власне, якщо потрібно саме одразу і потроху - то от:

istringstream ss( myRowOrMyText);

string word1, word2, word3;
ss >> word1 >> word2 >> word3;
I belong to the Dead Generation.
Подякували: Logans1

3

Re: Зчитування з файлу

Дякую за допомогу з першою частиною питання, але як бути з наступною? Яким чином можна зробити, щоб кожен раз зчитувало наступне слово. Зараз в мене працює так:

Прихований текст
Текст

Астероїди або малі планети, здебільшого обертаються між орбітами Марса 
і Юпітера й невидимі неозброєним оком.

Розбиття

астероїди або малі
астероїди або малі
астероїди або малі
астероїди або малі
і так до кінця змінної тексту
Ось частина програми

    string firstWord, secondWord, thirtWord;
    string shingle[250];

    for (int i = 0; i < stringText.length(); i++)
    {
        istringstream searchWord(stringText);
        searchWord >> firstWord >> secondWord >> thirtWord;
        shingle[i] = firstWord + " " + secondWord + " " + thirtWord + " ";
        cout << shingle[i] << endl;
    }

"Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program."
- Linus Torvalds

4

Re: Зчитування з файлу

Якщо я правильно зрозумів, алгоритм шинглів використовується для порівняння тексту з певною бібліотекою, і цю бібліотеку зчитати в пам'ять неможливо (вона завелика за визначенням), тому треба оптимізувати роботу з нею. Як саме? Хешуванням і сортуванням. Тобто:
1. Придумуєте хеш-функцію. Скажімо, сумуєте коди всіх символів рядку, після додавання кожного коду циклічно зсуваєте суму праворуч. Або ще якось - аби результат був досить випадковим. До речі, можна скористатися ГПВЧ - ініціалізуючи його, знову ж таки, кодами символів рядку. Головне - рядок перетворюється на число.
2. Створюєте бібліотеку хешів - скажімо структурами з хешу, коду файла і позиції шингла в файлі.
3. Сортуєте цю бібліотеку (чи робите індекс, неважливо, для початківця легше сортувати). Або навіть розміщуєте в хеш-таблиці, тоді хеш зберігати не треба, але знову ж, не для початківця.
4. Зчитуєте з файла для перевірки по слову, обчислюєте хеш поточного шингла, шукаєте його бінарним пошуком (якщо сортували) чи прямо запитуєте (якщо робили хеш-таблицю) в бібліотеці. Якщо такий хеш знайдено - перевіряєте, чи це не колізія (два однакових хеші в різних шинглів) безпосередньо в файлі (у нас є позиція, не забувайте!)

Десь так...

Подякували: Logans1

5 Востаннє редагувалося koala (03.01.2014 01:06:53)

Re: Зчитування з файлу

А, то проблема просто в зчитуванні?

Прихований текст

#include <iostream>
#include <list>
#include <string>
#include <sstream>
using namespace std;

int main() {
  list< string > words;
  stringstream input( "Астероїди або малі планети, здебільшого обертаються між орбітами Марса \
і Юпітера й невидимі неозброєним оком." );
  string cur;
  while( input >> cur )
  {
    words.push_back( cur );
    if( words.size() > 3 ) words.pop_front();
    if( words.size() == 3 )
    {
      string shingle;
      for( list< string >::iterator s = words.begin(); s != words.end(); s++ )
        shingle += *s + " ";
      cout << shingle << endl;
    }
  }
  return 0;
}

list тут - оптимальний контейнер, але цілком можливо скористатися чимось іншим (навіть самописним), аби лиш можна було додавати в кінець і видаляти з початку.

Подякували: Logans1