2
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string.h>
#include <conio.h>

int main()  
{  
    char mass [80][80];  
    char slovo[80];  
    int i=0;  
    int a,k=0;  
    setlocale(LC_ALL, "Russian");  
    FILE *f;  
    f=fopen("text.txt", "r");  
    if(f==NULL) printf("файл text.txt не открыт\n");

    a=i;  
    printf("Введите слово для поиска: ");  
    // Здесь должен быть линейный поиск  
}
  • кому не ясна суть вопроса, посмотрите на ответы, если ответы не ясны, оставьте комментарий под соответствующим ответом – jfs Dec 13 '17 at 17:09

3 Answers3

5

Вот ответ, разбирайтесь:

#include "iostream"
#include <string>
#include <fstream>

using namespace std; int main(){ ifstream file("C://1.txt"); // открыли файл с текстом string s, find; char c;

while (!file.eof()){  // прочитали его и заполнили им строку
    file.get(c);
    s.push_back(c);
}

file.close(); // обязательно закрыли

cout &lt;&lt; "enter a world for find: ";
cin &gt;&gt; find;

int pos = s.find(find); // поиск

if (pos == -1)
    cout &lt;&lt; "not finded" &lt;&lt; endl;
else
    cout &lt;&lt; "finded in " &lt;&lt; pos &lt;&lt; " simvol position" &lt;&lt; endl;

return 0;

}

perfect
  • 10,021
  • Большое спасибо, теперь разобрался. – АльфаЧ Dec 19 '14 at 12:25
  • 1
    Данный код ищет подстроку, а не слово в файле, т.е., он найдёт "фон" если файл содержит "телефон", хотя самого слова "фон" в файле может и не быть.

    Необходимо проверять на ошибки после file.get(c), в противном случае можно мусор записать в s строку. Вот рабочий способ считывания файла в строку целиком.

    Можно ещё упомянуть, что необходимо изменить код, чтобы можно было передать Юникодные имена файлов и входное слово для поиска на Винде.

    – jfs Dec 19 '14 at 17:47
  • @jfs счас подумал, можно добавить пару пробелов по обеим концам, чтоб слово полностью совпало, это несложно и загружать можно тоже по словам, ориентируясь по пробелам – perfect Dec 24 '14 at 19:33
  • 1
    @perfect добавить вокруг слова пробелы не достаточно, если слово находится в начале, конце файла или если слова разделены одним из других шести стандартных пробельных символов: " \t\n\r\f\v" (наиболее вероятно новой строкой '\n'). – jfs Dec 25 '14 at 00:13
5

istream_iterator<string> + find алгоритм являются простым способом определить, содержит ли файл, данное отделённое пробелами слово, используя линейный поиск:

ifstream file("input.txt");
istream_iterator<string> eof;
bool found = find(istream_iterator<string>(file), eof, word) != eof;

Например, если слово задано с коммандной строки, а файл передаётся на стандартном вводе:

/** $ g++ *.cxx -o find-word && <input.txt ./find-word word

    Exit status:

      0 -- found word
      1 -- not found
      2 -- error
*/
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <string>

int main(int argc, char* argv[])
{
  using namespace std;

  if (argc != 2) {
    cerr << "Usage: find-word WORD <input.txt\n";
    exit(2);
  }
  string word(argv[1]); // word to search
  istream_iterator<string> words(cin), eof; 
  bool found = find(words, eof, word) != eof;
  return found ? 0 : (cin.eof() ? 1 : 2);
}

Поиск работает, потому что istream_iterator<string> вызывает cin >> next_word внутри, который пропускает пробелы по умолчанию (skipws флаг установлен) и find алгоритм затем просто сравнивает next_word == word.

Что такое пробел, а значит и что такое слово может зависеть от текущей локали.

На системах с utf-8 локалью, код работает как есть с произвольным Юникодным текстом (поддержка нескольких языков в одном документе, поддержка эмотиконов и т.д., правда Юникодные пробелы не распознаются). Windows может испортить входной поток за счёт неявных (codepage) преобразований байтового потока -- как прочитать Юникодный текст на Windows лучше задать как отдельный вопрос.

Код читает только одно слово за раз и возвращается как только входное слово обнаружено, то есть код может работать с очень большими файлами и если заданное слово присутствует во вводе, то программа может вернуться раньше -- без считывания всего ввода. Код также информирует об ошибках ввода (cin.eof() тест).

Существует множество строковых алгоритмов, которые помогают найти подстроку в строке, например, алгоритм Ахо—Корасик (мог быть использован для реализации fgrep) может быть эффективнее в некоторых случаях чем наивный линейный поиск. Связанный вопрос: Поиск повторяющихся строк.

jfs
  • 52,361
  • Ответ ичерпывающий и точный. Давно уж пора создать предмет как философия программирования, в коем вы были бы, не побоюсь этого слова гуру. – АльфаЧ Dec 21 '14 at 11:26
4

Читайте в цикле файл по словам функцией fscanf() и используйте strcmp() для сравнения.

--

См. man fscanf. Что будет непонятно -- спрашивайте.

Update

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char* argv[]) { FILE fp; const char fname; int rc = 1; // like grep return code

if (!(fp = fopen(fname = (argv[1] ? argv[1]: "1.txt"),"r"))) { perror(fname); exit(2); }

char input[81], word[81]; printf("Enter word: "); fflush(stdout); if (scanf("%80s", input) == 1) { printf("search [%s] in %s\n", input, fname); while(fscanf(fp, "%80s", word) == 1) if (strcmp(word, input) == 0) { rc = 0; printf("Found in pos: %ld\n", ftell(fp)); } } else puts("nothing to search");

return rc; }

Windows 7, emacs e-shell:

c:/Users/avp/src/cc/hashcode $ g++ b.cpp -o b
c:/Users/avp/src/cc/hashcode $ ./b
Enter word: ERRDEMO
search [ERRDEMO] in 1.txt
Found in pos: 591
c:/Users/avp/src/cc/hashcode $
avp
  • 46,098
  • 6
  • 48
  • 116
  • Я воспользовался вашим советом. Но в отместку появилась иная проблема, как задать массив, чтобы в него влез весь текст txt в 500 кб. При попытке скомпилировать, ругается. Собственно, вот мой код:

    int main()
    {
    char mass [100][100];<-- как задать массив, чтобы поместился весь текст?
    char slovo[80];
    int i=0;
    int a,k=0;
    setlocale(LC_ALL, "Russian");
    FILE *f;
    f=fopen("text.txt", "r");
    if(f==NULL) printf("файл text.txt не открыт\n");

    while(!feof(f))
    {
    fscanf(f,"%s",&mass[i]);
    printf("%s\n", mass[i]);
    i++; }

    // написал поиск

    }

    – АльфаЧ Dec 18 '14 at 10:40
  • Используйте лучше тип string для загрузки текста в оперативную память, а потом ищите строку в подстроке http://hashcode.ru/questions/107089/c-%D0%BF%D0%BE%D0%B8%D1%81%D0%BA-%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8-%D0%B2-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5 – perfect Dec 18 '14 at 11:45
  • @АльфаЧ, а зачем читать в массив весь текст?
    char word[80], slovo[80];
    FILE *f = fopen(...);
    ...
    while (fscanf(f, "%s", word) == 1)
       if (strcmp(word, slovo) == 0) {
         puts("Found");
         break; // или чего Вы хотите делать с этом словом
       }
    
    

    вот и все.

    – avp Dec 18 '14 at 13:24
  • Читать в массив текст нужно для того, чтобы произвести линейный поиск. С размерами файла, к примеру, 80 слов, поиск работает, если текст увеличить до 500 Кб, программа отказывается работать. – АльфаЧ Dec 18 '14 at 14:35
  • 1
    @АльфаЧ, думаю, проблема у Вас в чем-то другом.

    Кстати, что значит -- отказывается работать?

    Опишите подробно все симптомы, сообщения на экране и т.п. (я-то даром ясновиденья обладаю не в полной степени).

    --

    Попробуйте мой фрагмент с файлом любого размера.

    (если опять не работает -- положите код куда-нибудь (например на pastebin.com))

    – avp Dec 18 '14 at 14:59
  • Отказывался работать, когда я выводил на экран весь текст, по вашему совету, я убрал выведения массива на экран. Теперь у меня не работает поиск. Я ввожу слово (оно там есть), выводится на экран, "слово не найдено". Мне лишь бы понять вот что, если я увеличу в char mass[100][100]<-- эти значения, выходит ошибка, мол, размер массива превышает допустимый. Ведь если текст у меня состоит из 100 слов, программа работает корректно, размер напрямую зависит от char mass [100][100]. – АльфаЧ Dec 18 '14 at 15:44
  • @АльфаЧ, еще раз прямо говорю, для линейного поиска слова в файле не надо читать его целиком в память.

    Читаете файл по одному слову и сравниваете с заданным.

    --

    Не понимаю, что именно тут непонятно?
    (сейчас обновлю ответ примером)

    – avp Dec 18 '14 at 18:39
  • Весьма благодарен за советы. – АльфаЧ Dec 19 '14 at 12:24
  • Вместо argv[1] ?, нужно argc > 1 ? написать, чтобы избежать UB. Нет необходимости fname присваивание внутрь fopen() запихивать.

    В 2014 году не хорошо жёстко записывать ограничения, такие как 80s. То есть либо нужно динамически под текущее слово память выделять, например, как в моём ответе, либо использовать алгоритм, который умеет сравнивать слово по частям (можно даже по одному байту читать getchar()).

    Следует также упомянуть, что данный код может иметь проблемы с Юникодом на Винде (имя файла, само входное слово).

    – jfs Dec 19 '14 at 18:04
  • @jfs, в винде вообще столько проблем, что лучше от нее держаться подальше (хотя данный код в ней (локализованной для cp1251) иногда (ascii или эмулятор терминала, работающий в cp1251) работает).

    Что касается систем с UB для av[1] (я, правда, таких не знаю), то IMHO их лучше явно выделять, используя #ifdef.

    Что же касается %80s, то не стал я грузить автора %m вместе с освобождением динамического буфера или какой-нибудь get_word_file() использующей внутри getline() и возвращающей указатель и длину (нет смысла как для примера, так и для кучи практических приложений (даже в 2015 году))

    – avp Dec 19 '14 at 20:52