NCBI C++ ToolKit
text_util.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: text_util.cpp 54603 2012-05-24 13:12:58Z vasilche $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Mike DiCuccio
27  *
28  * File Description:
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <algo/text/text_util.hpp>
34 #include <algo/text/vector.hpp>
36 #include <util/dictionary_util.hpp>
37 #include <util/static_set.hpp>
38 #include <math.h>
39 
40 
42 
43 
44 /**
45 static bool sc_IsTokenStop[256];
46 static char sc_Lower[256];
47 static const string sc_ClauseStop(".?!;:\"{}[]()");
48 **/
49 
50 
51 enum {
52  eAlpha = 0x0100,
53  eNumeric = 0x0200,
54  ePunctuation = 0x0400,
55  ePrintable = 0x0800,
56  eSpace = 0x1000,
57  eClauseEnd = 0x2000
58 };
59 static Uint2 sc_Tokens[256];
60 
62 {
64  {
65  /**
66  int isalnum(int c);
67  int isalpha(int c);
68  int isascii(int c);
69  int isblank(int c);
70  int iscntrl(int c);
71  int isdigit(int c);
72  int isgraph(int c);
73  int islower(int c);
74  int isprint(int c);
75  int ispunct(int c);
76  int isspace(int c);
77  int isupper(int c);
78  int isxdigit(int c);
79  **/
80 
81 
82 
83  /// seed our tokens with their lower-case equivalents
84  /// by default all tokens are considered clause stops
85  for (int i = 0; i < 256; ++i) {
86  sc_Tokens[i] = tolower(i);
87 
88  /// add additional properties
89  if (isprint(i)) {
91  }
92  if (isalpha(i)) {
93  sc_Tokens[i] |= eAlpha;
94  }
95  if (isdigit(i)) {
96  sc_Tokens[i] |= eNumeric;
97  }
98  if (ispunct(i)) {
100  }
101  if (isspace(i)) {
102  sc_Tokens[i] |= eSpace;
103  }
104  }
105 
106  /// other special pieces: clause ends
107  string clause_ends(".?!;:\"{}[]()");
108  ITERATE (string, it, clause_ends) {
109  sc_Tokens[(unsigned char)*it] |= eClauseEnd;
110  }
111 
112  /// funkyness: "'" counts as an alphanumeric
113  sc_Tokens[(int)'\''] |= eAlpha;
114  }
115 };
117 
118 
119 //////////////////////////////////////////////////////////////////////////////
120 
121 
122 static bool s_IsAlphaNumeric(unsigned char c)
123 {
124  return (sc_Tokens[c] & (eAlpha|eNumeric));
125 }
126 
127 /*static bool s_IsAlpha(unsigned char c)
128 {
129  return (sc_Tokens[c] & (eAlpha));
130 }*/
131 
132 static bool s_IsNumeric(unsigned char c)
133 {
134  return (sc_Tokens[c] & (eNumeric));
135 }
136 
137 static bool s_IsNumeric(const string& s)
138 {
139  bool is_alpha = false;
140  ITERATE (string, i, s) {
141  is_alpha |= !s_IsNumeric(*i);
142  if (is_alpha) {
143  break;
144  }
145  }
146  return !is_alpha;
147 }
148 
149 /*static bool s_IsPrintable(unsigned char c)
150 {
151  return (sc_Tokens[c] & (ePrintable));
152 }*/
153 
154 static char s_ToLower(unsigned char c)
155 {
156  return sc_Tokens[c] & 0xff;
157 }
158 
159 static string::size_type s_NextTokenStart(const string& s,
160  string::size_type i)
161 {
162  for ( ; i < s.size(); ++i) {
163  if (s_IsAlphaNumeric(s[i])) {
164  break;
165  }
166  }
167  return (i == s.size() ? string::npos : i);
168 }
169 
170 static string::size_type s_NextTokenStop(const string& s,
171  string::size_type i)
172 {
173  for ( ; i < s.size(); ++i) {
174  if ( !s_IsAlphaNumeric(s[i]) ) {
175  break;
176  }
177  }
178  return (i == s.size() ? string::npos : i);
179 }
180 
181 static string::size_type s_NextClauseStop(const string& s,
182  string::size_type i)
183 {
184  for ( ; i < s.size(); ++i) {
185  if (sc_Tokens[(unsigned char)s[i]] & eClauseEnd) {
186  break;
187  }
188  }
189  return (i == s.size() ? string::npos : i);
190 }
191 
192 //////////////////////////////////////////////////////////////////////////////
193 
194 
195 /// split a set of word frequencies into phrase and non-phrase frequencies
196 /// this is done to treat the two separately
198  TWordFreq& wf_out, TWordFreq& phrase_out)
199 {
200  ITERATE (TWordFreq, iter, wf_in) {
201  if (iter->first[0] == 'p' && iter->first.find("phrase: ") == 0) {
202  phrase_out.insert(phrase_out.end(), *iter);
203  } else {
204  wf_out.insert(wf_out.end(), *iter);
205  }
206  }
207 }
208 
209 
210 /// add a set of frequencies into another set
212  TFlags flags)
213 {
214  ITERATE (TWordFreq, iter, wf) {
215  if ((flags & fNoNumeric) && s_IsNumeric(iter->first)) {
216  continue;
217  }
218  freq.Add(iter->first, iter->second);
219  }
220 }
221 
222 
224  const string& prefix,
225  TFlags flags)
226 {
227  ITERATE (TWordFreq, iter, wf) {
228  if (iter->first.find_first_of(":") != string::npos) {
229  continue;
230  }
231  if ((flags & fNoNumeric) && s_IsNumeric(iter->first)) {
232  continue;
233  }
234  freq.Add(prefix + ": " + iter->first, iter->second);
235  }
236 }
237 
238 
240 {
241  return NStr::IntToString(i);
242 }
243 
245 {
246  return NStr::Int8ToString(i);
247 }
248 
250 {
251  return NStr::UIntToString(i);
252 }
253 
255 {
256  return NStr::UInt8ToString(i);
257 }
258 
259 string s_ValToString(double i)
260 {
261  return NStr::DoubleToString(i);
262 }
263 
264 string s_ValToString(float i)
265 {
266  return NStr::DoubleToString(i);
267 }
268 
269 string s_ValToString(const string& i)
270 {
271  return i;
272 }
273 
274 
275 template <class T>
276 void s_NumericToFreq(const T& val, CTextUtil::TWordFreq& freq)
277 {
278  freq.Add(s_ValToString(val), 1);
279 
280  /**
281  const size_t kNumericStringLimit = 40;
282  string str = s_ValToString(val);
283  if (str.size() <= kNumericStringLimit) {
284  freq.Add(str, 1);
285  } else {
286  string::const_iterator it1 = str.begin();
287  string::const_iterator it2 = str.begin() + kNumericStringLimit;
288  for ( ; ; ++it1, ++it2) {
289  string s(it1, it2);
290  freq.Add(s, 1);
291  if (it2 == str.end()) {
292  break;
293  }
294  }
295  }
296  **/
297 }
298 
299 
301 {
302  s_NumericToFreq(i, freq);
303 }
304 
305 
307 {
308  s_NumericToFreq(i, freq);
309 }
310 
311 
313 {
314  s_NumericToFreq(i, freq);
315 }
316 
317 
319 {
320  s_NumericToFreq(i, freq);
321 }
322 
323 
325 {
326  s_NumericToFreq(i, freq);
327 }
328 
329 
331 {
332  s_NumericToFreq(i, freq);
333 }
334 
335 
337  TWordFreq& freq,
338  TFlags flags)
339 {
340  _TRACE("CTextUtil::GetWordFrequencies(): text = " << text);
341  string::size_type clause_start = 0;
342  string::size_type clause_end = text.size();
343 
344  list<string> prev_words;
345  string phrase;
346  string word;
347  string stem;
348 
349  /// we process one clause at a time
350  while (clause_start != clause_end) {
351  clause_end = text.size();
352  string::size_type pos = s_NextClauseStop(text, clause_start);
353  if (pos != string::npos) {
354  clause_end = pos;
355  }
356 
357  prev_words.clear();
358  _TRACE("clause: |" << text.substr(clause_start, clause_end - clause_start) << "|");
359  for ( ; clause_start != clause_end; clause_start = pos) {
360  /// find the current starting point
361  clause_start =
362  min(clause_end, s_NextTokenStart(text, clause_start));
363  if (clause_start == clause_end) {
364  break;
365  }
366 
367  /// determine the next word end boundary
368  pos = min(clause_end,
369  s_NextTokenStop(text, clause_start));
370 
371  ///
372  /// we've found a word
373  ///
374 
375  /// extract it and lower-case it
376  word.assign(text, clause_start, pos - clause_start);
377 
378  /// make sure it's not entirely numeric
379  string::size_type pos1 =
380  word.find_first_not_of("0123456789");
381  if (pos1 == string::npos) {
382  if ((flags & fNoNumeric) == 0) {
383  s_NumericToFreq(word, freq);
384  }
385  continue;
386  }
387 
388 
389  /// remove single-quotes
390  string::iterator copy_to = word.begin();
391  NON_CONST_ITERATE (string, copy_from, word) {
392  if (*copy_from == '\'') {
393  continue;
394  }
395  *copy_to++ = s_ToLower(*copy_from);
396  }
397  if (copy_to != word.end()) {
398  word.erase(copy_to, word.end());
399  }
400 
401  if ((flags & fTrimStops) && IsStopWord(word)) {
402  continue;
403  }
404 
405  if (word.empty()) {
406  continue;
407  }
408 
409  _TRACE(" word: " << word);
410 
411  /// we may want to fix common british diphthongs
412  if (flags & fDiphthongReplace) {
413  typedef pair<string, string> TDiphPair;
414  static const TDiphPair sc_DiphPairs[] = {
415  TDiphPair("oe", "e"),
416  TDiphPair("ae", "e")
417  };
418 
419  string s;
420  for (size_t i = 0; i < sizeof(sc_DiphPairs) / sizeof(TDiphPair); ++i) {
421  if (word.find(sc_DiphPairs[i].first) != string::npos) {
422  NStr::Replace(word,
423  sc_DiphPairs[i].first,
424  sc_DiphPairs[i].second,
425  s);
426  word.swap(s);
427  }
428  }
429  }
430 
431  ///
432  /// add the word
433  ///
434  freq.Add(word, 1);
435 
436  if (flags & fIncludePhrases) {
437  ///
438  /// add a phrase including the previous word
439  ///
440  if (flags & fPhrase_NoStems) {
441  prev_words.push_back(word);
442  } else {
443  CDictionaryUtil::Stem(word, &stem);
444  prev_words.push_back(stem);
445  }
446 
447  /// we permit a phrase to be a proximity search, looking for
448  /// wors within two words of the current
449  while (prev_words.size() > 3) {
450  prev_words.pop_front();
451  }
452  if (prev_words.size() > 1) {
453  list<string>::iterator pit = prev_words.begin();
454  list<string>::iterator end = prev_words.end();
455  --end;
456  for ( ; pit != end; ++pit) {
457  /// this looks odd, but we want to make sure our
458  /// phrases are not things like 'cancer cancer'
459  if (*pit == *end) {
460  continue;
461  }
462 
463  phrase.erase();
464  if ( !(flags & fPhrase_NoPrefix) ) {
465  phrase = "phrase: ";
466  }
467  if (*pit < *end) {
468  phrase += *pit;
469  phrase += " ";
470  phrase += *end;
471  } else {
472  phrase += *end;
473  phrase += " ";
474  phrase += *pit;
475  }
476  _TRACE(" phrase: |" << phrase << "|");
477 
478  freq.Add(phrase, 1);
479  /**
480  TWordFreq::iterator it = freq.find(phrase);
481  if (it == freq.end()) {
482  freq[phrase] = 1;
483  } else {
484  ++it->second;
485  }
486  **/
487  }
488  }
489  }
490  }
491 
492  _TRACE("clause end");
493 
494  clause_start = s_NextTokenStart(text, clause_start);
495  if (clause_start == string::npos) {
496  break;
497  }
498  }
499 
500 #ifdef _DEBUG
501  ITERATE (TWordFreq, it, freq) {
502  _TRACE(" word: " << it->first << " count: " << it->second);
503  }
504 #endif
505 }
506 
507 
509  TWordFreq& stem_freq,
510  TFlags flags)
511 {
512  string stem;
513  ITERATE (TWordFreq, iter, freq) {
514  if (iter->first.find_first_of(":") != string::npos) {
515  stem = iter->first;
516  } else {
517  CDictionaryUtil::Stem(iter->first, &stem);
518  }
519 
520  TWordFreq::iterator it = stem_freq.find(stem);
521  if (it != stem_freq.end()) {
522  it->second += iter->second;
523  } else {
524  stem_freq.insert
525  (stem_freq.end(),
526  CTextUtil::TWordFreq::value_type(stem, iter->second));
527  }
528  }
529 
530  if (flags & fTrimStops) {
531  TrimStopWords(stem_freq);
532  }
533 }
534 
535 
537  TWordFreq& freq,
538  TFlags flags)
539 {
540  string line;
541  while (NcbiGetlineEOL(istr, line)) {
542  GetWordFrequencies(line, freq, flags);
543  }
544 }
545 
546 
547 /////////////////////////////////////////////////////////////////////////////
548 ///
549 /// Stop Word Pruning
550 ///
551 ///
552 
553 static const char* const sc_StopWordArray[] = {
554  "a",
555  "about",
556  "again",
557  "am",
558  "an",
559  "and",
560  "any",
561  "anybody",
562  "anyhow",
563  "anyone",
564  "anything",
565  "anyway",
566  "are",
567  "as",
568  "at",
569  "be",
570  "because",
571  "been",
572  "being",
573  "but",
574  "by",
575  "did",
576  "do",
577  "does",
578  "doing",
579  "done",
580  "for",
581  "from",
582  "had",
583  "has",
584  "have",
585  "having",
586  "he",
587  "her",
588  "him",
589  "his",
590  "how",
591  "i",
592  "if",
593  "in",
594  "into",
595  "is",
596  "isnt",
597  "it",
598  "make",
599  "me",
600  "my",
601  "myself",
602  "no",
603  "none",
604  "not",
605  "now",
606  "of",
607  "off",
608  "on",
609  "or",
610  "our",
611  "than",
612  "that",
613  "the",
614  "their",
615  "them",
616  "then",
617  "there",
618  "these",
619  "they",
620  "this",
621  "those",
622  "to",
623  "too",
624  "two",
625  "very",
626  "was",
627  "we",
628  "went",
629  "were",
630  "what",
631  "whatever",
632  "whats",
633  "when",
634  "where",
635  "who",
636  "whom",
637  "whose",
638  "why",
639  "will",
640  "with",
641  "wont",
642  "would",
643  "wouldnt",
644  "yet",
645  "you",
646  "your",
647 };
650 
651 
652 bool CTextUtil::IsStopWord(const string& str)
653 {
654  TStopWords::const_iterator iter = sc_StopWords.find(str.c_str());
655  return (iter != sc_StopWords.end());
656 }
657 
658 
660 {
661  /// eliminate stop words
662  TStopWords::const_iterator stop_it = sc_StopWords.begin();
663  TStopWords::const_iterator stop_end = sc_StopWords.end();
664 
666  CTextUtil::TWordFreq::iterator end = freq.end();
667 
668  for ( ; stop_it != stop_end && it != end; ) {
669  if (it->first == *stop_it) {
670  freq.erase(it++);
671  ++stop_it;
672  } else {
673  if (it->first < *stop_it) {
674  ++it;
675  } else {
676  ++stop_it;
677  }
678  }
679  }
680 }
681 
682 
683 void CTextUtil::CleanJournalTitle(string& title)
684 {
685  string::size_type pos = 0;
686  while ( (pos = title.find_first_of(".,[](){};:'\"/?<>", pos)) != string::npos) {
687  title.erase(pos, 1);
688  }
689  title = NStr::ToLower(title);
690 }
691 
692 
693 /// encode word frequencies in a serializable blob of data
695  vector<unsigned char>& data)
696 {
697  Encode(freq, data);
698 }
699 
701  vector<char>& data)
702 {
703  Encode(freq, data);
704 }
705 
706 
709 {
710  Encode(freq, data);
711 }
712 
713 
714 /// decode from a serializable blob of data
716  const vector<unsigned char>& data)
717 {
718  Decode(data, freq);
719 }
720 
721 
723  const vector<char>& data)
724 {
725  Decode(data, freq);
726 }
727 
728 
730  const CSimpleBuffer& data)
731 {
732  Decode(data, freq);
733 }
734 
735 
737  const void* data, size_t data_len)
738 {
739  Decode(data, data_len, freq);
740 }
741 
742 
743 
static void Stem(const string &in_str, string *out_str)
Compute the Porter stem for a given word.
iterator find(const Key &key)
iterator begin()
iterator end()
pair< iterator, bool > insert(const value_type &val)
void erase(iterator it)
void Add(Key idx, Score weight=Score(1))
TVector::value_type value_type
Definition: vector.hpp:152
TVector::iterator iterator
Definition: vector.hpp:153
Reallocable memory buffer (no memory copy overhead) Mimics vector<>, without the overhead of explicit...
TBase::const_iterator const_iterator
Definition: static_set.hpp:828
static void GetStemFrequencies(const TWordFreq &freq, TWordFreq &stems, TFlags flags=fDefaults)
retrieve stem frequencies from a set of word frequencies
Definition: text_util.cpp:508
static bool IsStopWord(const string &str)
return true if the provided word is a stop word
Definition: text_util.cpp:652
static void TrimStopWords(TWordFreq &freq)
eliminate the stop words frm a set of word frequencies
Definition: text_util.cpp:659
static void CleanJournalTitle(string &title)
perform a set of punctuational clean-ups on a string suitable for a journal or book title
Definition: text_util.cpp:683
static void SplitWordFrequencies(const TWordFreq &wf_in, TWordFreq &wf_out, TWordFreq &phrase_out)
split a set of word frequencies into phrase and non-phrase frequencies this is done to treat the two ...
Definition: text_util.cpp:197
@ fPhrase_NoStems
Definition: text_util.hpp:127
@ fDiphthongReplace
Definition: text_util.hpp:122
@ fPhrase_NoPrefix
Definition: text_util.hpp:128
@ fIncludePhrases
Definition: text_util.hpp:126
static void EncodeFreqs(const TWordFreq &freq, vector< char > &data)
Definition: text_util.cpp:700
static void AddWordFrequencies(TWordFreq &freq, const TWordFreq &wf, TFlags flags=0)
add a set of frequencies into another set
Definition: text_util.cpp:211
static void GetWordFrequencies(const string &text, TWordFreq &freq, TFlags flags=fDefaults)
retrieve word frequencies for a given piece of text
Definition: text_util.cpp:336
static void DecodeFreqs(TWordFreq &freq, const vector< char > &data)
Definition: text_util.cpp:722
static uch flags
#define T(s)
Definition: common.h:230
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
static const char * str(char *buf, int n)
Definition: stats.c:84
char data[12]
Definition: iconv.c:80
double wf(double lambda, double D_LR, double D_LU, double D_LD, double D_RU, double D_RD, double D_DU)
Definition: gme.cpp:78
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NON_CONST_ITERATE(Type, Var, Cont)
Non constant version of ITERATE macro.
Definition: ncbimisc.hpp:822
#define _TRACE(message)
Definition: ncbidbg.hpp:122
int32_t Int4
4-byte (32-bit) signed integer
Definition: ncbitype.h:102
uint32_t Uint4
4-byte (32-bit) unsigned integer
Definition: ncbitype.h:103
uint16_t Uint2
2-byte (16-bit) unsigned integer
Definition: ncbitype.h:101
int64_t Int8
8-byte (64-bit) signed integer
Definition: ncbitype.h:104
uint64_t Uint8
8-byte (64-bit) unsigned integer
Definition: ncbitype.h:105
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
CNcbiIstream & NcbiGetlineEOL(CNcbiIstream &is, string &str, string::size_type *count=NULL)
Read from "is" to "str" the next line (taking into account platform specifics of End-of-Line)
IO_PREFIX::istream CNcbiIstream
Portable alias for istream.
Definition: ncbistre.hpp:146
static string Int8ToString(Int8 value, TNumToStringFlags flags=0, int base=10)
Convert Int8 to string.
Definition: ncbistr.hpp:5153
static string DoubleToString(double value, int precision=-1, TNumToStringFlags flags=0)
Convert double to string.
Definition: ncbistr.hpp:5181
static string IntToString(int value, TNumToStringFlags flags=0, int base=10)
Convert int to string.
Definition: ncbistr.hpp:5078
static string & Replace(const string &src, const string &search, const string &replace, string &dst, SIZE_TYPE start_pos=0, SIZE_TYPE max_replace=0, SIZE_TYPE *num_replace=0)
Replace occurrences of a substring within a string.
Definition: ncbistr.cpp:3305
static string UIntToString(unsigned int value, TNumToStringFlags flags=0, int base=10)
Convert UInt to string.
Definition: ncbistr.hpp:5103
static string & ToLower(string &str)
Convert string to lower case – string& version.
Definition: ncbistr.cpp:405
static string UInt8ToString(Uint8 value, TNumToStringFlags flags=0, int base=10)
Convert UInt8 to string.
Definition: ncbistr.hpp:5162
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
int i
static void text(MDB_val *v)
Definition: mdb_dump.c:62
int isalpha(Uchar c)
Definition: ncbictype.hpp:61
int isspace(Uchar c)
Definition: ncbictype.hpp:69
int tolower(Uchar c)
Definition: ncbictype.hpp:72
int isdigit(Uchar c)
Definition: ncbictype.hpp:64
int isprint(Uchar c)
Definition: ncbictype.hpp:67
int ispunct(Uchar c)
Definition: ncbictype.hpp:68
T min(T x_, T y_)
CStaticArraySet< const char *, PCase_CStr > TStopWords
Definition: text_util.cpp:648
static string::size_type s_NextClauseStop(const string &s, string::size_type i)
Definition: text_util.cpp:181
static bool s_IsNumeric(unsigned char c)
Definition: text_util.cpp:132
static SLoadTokens s_ForceTokenLoad
Definition: text_util.cpp:116
void s_NumericToFreq(const T &val, CTextUtil::TWordFreq &freq)
Definition: text_util.cpp:276
static const char *const sc_StopWordArray[]
Stop Word Pruning.
Definition: text_util.cpp:553
static bool s_IsAlphaNumeric(unsigned char c)
Definition: text_util.cpp:122
string s_ValToString(Int4 i)
Definition: text_util.cpp:239
static string::size_type s_NextTokenStart(const string &s, string::size_type i)
Definition: text_util.cpp:159
static char s_ToLower(unsigned char c)
Definition: text_util.cpp:154
DEFINE_STATIC_ARRAY_MAP(TStopWords, sc_StopWords, sc_StopWordArray)
static string::size_type s_NextTokenStop(const string &s, string::size_type i)
Definition: text_util.cpp:170
static Uint2 sc_Tokens[256]
Definition: text_util.cpp:59
@ eAlpha
Definition: text_util.cpp:52
@ ePunctuation
Definition: text_util.cpp:54
@ eNumeric
Definition: text_util.cpp:53
@ eClauseEnd
Definition: text_util.cpp:57
@ ePrintable
Definition: text_util.cpp:55
@ eSpace
Definition: text_util.cpp:56
void Encode(const CRawScoreVector< Key, Score > &, vector< char > &)
void Decode(const vector< char > &, CRawScoreVector< Key, Score > &)
Modified on Fri Sep 20 14:57:57 2024 by modify_doxy.py rev. 669887