Advertisement
Guest User

Untitled

a guest
Nov 17th, 2019
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.49 KB | None | 0 0
  1. /*
  2. Project Name: json_file_simplifier
  3. Project Purpose:
  4.     - Will simplify JSON files to make them easier to read from.
  5.  
  6. Program Name: main3.cpp
  7. Version: 3.0.2
  8. Author: Tommy Weber
  9. Date: 11/17/2019
  10.  
  11. Latest Changes / Notes:
  12.     - On 10/15/2019 I added support for storing integers that are outside of double quotes.
  13.     - On 10/16/2019 I added support for storing doubles (like 0.0) that are outside of double quotes.
  14.     - On 10/18/2019 I added support for recursive directory iterating.
  15.         * BUG: It will only get data from files within "InputFiles/<AFolderName>/<filename>.<ext>" but will NOT store the data of files in "InputFiles/<filename>.<ext>" or in a folder depth of greater than 1 folder inside InputFiles. It WILL create the files but they will be empty.
  16.     - On 10/20/2019 I made some code improvements. However, the bug from the previous version still exists.
  17.         * BUG: It will only get data from files within "InputFiles/<AFolderName>/<filename>.<ext>" but will NOT store the data of files in "InputFiles/<filename>.<ext>" or in a folder depth of greater than 1 folder inside InputFiles. It WILL create the files but they will be empty.
  18.          
  19.     - Algorithm Steps and comments are up to date I think as of 10/20/2019 at 2:59 pm MDT.
  20.  
  21. -------------------------------
  22. Follow Good Practices:
  23.     1. Use algorithm steps (in English) first.
  24.     2. Use compiler flags.
  25.     3. Use static analyzers to test for bugs.
  26.     4. Use >> and << when dividing and multiplying by 2. Find the file that explains it.
  27.     5. A function should only do one thing, not two (like printInstructionsAndGetInput() is bad)
  28.     6. Make sure function names are specific to EXACTLY what it does. openFiles() is not correct for something that iterates through a directory recursively
  29.     #. Etc...
  30.  
  31. Overall Algorithm Steps: (Not numbered in code like other algorithm steps are)
  32.     1.
  33.         a. Tell user to input the desired files to be converted into the "InputFiles" folder path. Done in printInstructions()
  34.         b. Tell user to input 'Y' when ready or 'n' to exit. Done in getUserConfirmation()
  35.  
  36.     2. Open all files one at a time in the file path with the "InputFiles" folder.
  37.         - Done in iterateInDirectory()
  38.  
  39.     ---
  40.     For Each File (#3-#8):
  41.     3. In each file, read each char and keep track of where there are:
  42.         a. Double quotes
  43.             - Things within double quotes are saved.
  44.             - Example: "minecraft:type"
  45.         b. Numbers
  46.             - We will save all numbers in the file (even if they are outside double quotes).
  47.  
  48.     4. Store everything that matches what we want in #3 into a vector of alphabetical & number contents.
  49.    
  50.     5. Close input file.
  51.    
  52.     6. Create an output file in "OutputFiles" with same name but with preferred extension (.txt by default) instead of .json.
  53.    
  54.     7. In output file write each item in the vector to the file in a new line.
  55.         - Example vector: {""type"", ""minecraft:crafting_shaped"", ""group"", ""bark"", "333", ""444""}
  56.         - Writing example vector to file:
  57.             "type"
  58.             "minecraft:crafting_shaped"
  59.             "group"
  60.             "bark"
  61.             333
  62.             "444"
  63.  
  64.     8. Close output file.
  65.     ---
  66.  
  67.     9. Tell user that program has finished.
  68.  
  69. HOW TO COMPILE:
  70.     $ cppcheck --enable=all main3.cpp
  71.     $ g++ -std=c++17 main.cpp -lstdc++fs -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic
  72.     $ ./a.out
  73.  
  74.  
  75. [Note to self] Helpful REGEX Sources:
  76.     * https://stackoverflow.com/questions/2912894/how-to-match-any-character-in-regular-expression/2912904
  77.     * https://www.informit.com/articles/article.aspx?p=2064649&seqNum=3
  78.     * https://en.cppreference.com/w/cpp/regex/regex_match
  79. */
  80. #include <iostream>
  81. #include <iomanip> // output formatting in console
  82. #include <string>
  83. #include <fstream>
  84. #include <filesystem>
  85. #include <vector> // for storing words from json file and writing them to output files.
  86. #include <regex> // std::regex_replace() & std::regex()
  87.  
  88. namespace fs = std::filesystem; // for convenience
  89.  
  90. void printInstructions(const int width);
  91. void getUserConfirmation(const int width); // confirms user has followed instructions & program can parse files
  92. void iterateInDirectory(); // Recursively iterate through the filesystem "InputFiles" directory
  93. void parseJsonFile(const fs::path &inputFilePath, std::vector<std::string> &alphaNums);
  94. void writeJsonAsTxt(const fs::path &outputFilePath, const std::vector<std::string> &alphaNums);
  95.  
  96. int main() {
  97.     const int printWidth = 50; // the num of '-' or '=' in a line output to terminal
  98.     printInstructions(printWidth);
  99.     getUserConfirmation(printWidth);
  100.  
  101.     iterateInDirectory();
  102.  
  103.     std::cout << "The program has completed successfully!\n";
  104.    
  105.     return 0;
  106. }
  107.  
  108.  
  109. void printInstructions(const int width) {
  110.     const char sym = '-';
  111.  
  112.     std::cout << std::setfill(sym) << std::setw(width) << "\n"
  113.         << "Hello, thank you for using JSON File Simplifier!\n\n"
  114.         << "Note(s):\n"
  115.         << "- This program does not support decimals in folder names. If you have decimals, change them to '_'.\n"
  116.         << '\n'
  117.         << "Usage Steps:\n"
  118.         << "1. To get started, open the file path this program is located at on your computer.\n"
  119.         << "2. Now move the desired files you would like to simplify into the " << std::quoted("InputFiles") << " directory.\n";
  120. }
  121.  
  122. void getUserConfirmation(const int width) {
  123.     std::cout << "3. Input 'y' when ready or input 'n' to quit: "
  124.               << std::setw(width) << std::setfill(' ');
  125.    
  126.     char ready = 'n';
  127.     std::cin >> ready;
  128.     std::cout << '\n';
  129.     if (ready != 'y' && ready != 'Y') {
  130.         std::cout << "Program has ended. You may now close the program.\n";
  131.         exit(0);
  132.     }
  133. }
  134.  
  135. /*
  136. Algorithm Steps of this Function:
  137.     1. Create the path variable.
  138.         - Make sure it includes the current path and InputFiles directory.
  139.  
  140.     2. For each file (recursively) in the InputFiles directory:
  141.         a. Create 2 path variables that both equal entry variable:
  142.             - inputFilePath
  143.                 * Have this equal entry.
  144.             - outputFilePath
  145.                 * Have this equal entry but replace "InputFiles" part with "OutputFiles". We do this using #include <regex> code.
  146.         b. Check if entry is a directory file type.
  147.             - If it is, check if the outputFilePath exists already (such as the 1_13 folder inside InputFiles path). If the directories don't already exist, create them.
  148.         c. Check if inputFilePath is a regular file (as opposed to a directory or symlink). If so:
  149.             - Create an empty vec called alphaNums which will contain all words and numbers for a SINGLE file at a time.
  150.             - Call parseJsonFile()
  151.             - Because the file path is a regular file, we will need to replace the output file path's extension with "txt" before calling writeJsonAsTxt()
  152.             - Call writeJsonAsTxt()
  153. */
  154. void iterateInDirectory() {
  155.     // 1.
  156.     fs::path fullInPath = fs::current_path();
  157.     fullInPath /= "InputFiles";
  158.  
  159.     // 2.
  160.     for (auto &entry : fs::recursive_directory_iterator(fullInPath)) {
  161.         // 2a.
  162.         fs::path inputFilePath = entry; // Ex 1: /mnt/c/Users/Tom/Desktop/json_file_simplifier/InputFiles/1_13
  163.                                         // Ex 2: /mnt/c/Users/Tom/Desktop/json_file_simplifier/InputFiles/1_13/acacia_boat.json
  164.         fs::path outputFilePath = std::regex_replace(inputFilePath.string(), std::regex("InputFiles"), "OutputFiles");
  165.                                         // Ex 1: /mnt/c/Users/Tom/Desktop/json_file_simplifier/OutputFiles/1_13
  166.                                         // Ex 2: /mnt/c/Users/Tom/Desktop/json_file_simplifier/OutputFiles/1_13/acacia_boat.json
  167.  
  168.         // 2b.
  169.         if (entry.is_directory()) {
  170.             if (!fs::exists(outputFilePath)) {
  171.                 fs::create_directories(outputFilePath);
  172.             }
  173.         }
  174.  
  175.         // 2c.
  176.         if (entry.is_regular_file()) {
  177.  
  178.             std::vector<std::string> alphaNums;
  179.  
  180.             parseJsonFile(inputFilePath, alphaNums);
  181.            
  182.             outputFilePath.replace_extension("txt"); // If file path is a regular file, convert the extension to txt.
  183.             writeJsonAsTxt(outputFilePath, alphaNums);
  184.         }
  185.     }
  186. }
  187.  
  188.  
  189. /*
  190. Algorithm Steps of this Function:
  191.     1. Open the path of the current file.
  192.  
  193.     2. If the file couldn't be opened, print error and stop program.
  194.  
  195.     3. Define some variables that will be used for getting the proper data from the input file inside the while loop.
  196.  
  197.     4. Read the file one char at a time. (I wonder if there is a better way?)
  198.         a. If the char is a " then:
  199.             - add to the number of quotes (keeping track with a var)
  200.         b. If the count of quotes is equal to 1 then:
  201.             - add the current char to the word variable
  202.         c. If the char is a " again then:
  203.             - Add to the number of quotes (so it now equals 2)
  204.         d. If the count of quotes is greater than or equal to 2 then:
  205.             - add the current char (the " symbol) to the word variable.
  206.             - then add the word to a vector.
  207.             - then clear the word variable to prep for next word.
  208.    
  209.     5. Close the input file.
  210.        
  211.     Important Note of This Method:
  212.         - This code assumes there are no quotes within quotes.
  213.             * Ex: "  "minecraft:word" " in the files.
  214. */
  215. void parseJsonFile(const fs::path &inputFilePath, std::vector<std::string> &alphaNums) {
  216.         // 1.
  217.         std::ifstream inFile(inputFilePath);
  218.  
  219.         // 2.
  220.         if (!inFile.is_open()) {
  221.             std::cout << "The input file path below couldn't be opened:\n"
  222.                       << inputFilePath
  223.                       << "\n\n"
  224.                       << "Program stopped.\n";
  225.                 exit(1);
  226.         }
  227.  
  228.  
  229.         /*
  230.             Goal:
  231.                 - We want to remove everything except ints, doubles, and contents inside " " symbols.
  232.  
  233.             Algorithm Steps:
  234.                 1. Get one line at a time from input file.
  235.                 2. Search for matches of the regex "contents like this" (with double quotes) in the line. Use an iterator of std::regex_iterator() for this.
  236.                     - If line has any instances of the format then store it in container.
  237.                     - Note: The current regex explained:
  238.                         * std::regex desiredFormat(R"("[^"]*")");
  239.                         * R"(<contents>)" means raw literals. Removes the need to use \ all the time.
  240.                         * [] means character class.
  241.                         * [^"] means any character except: "
  242.                         * [^"]* means any number of any characters (except ")
  243.                         * "[^"]*" means ^ but INSIDE quotation marks.
  244.                         * So R"("[^"]*")" means "this format." is "supported!" --> Will take the first match AKA "this format." then iterating it will allow it to take any other matches.
  245.                     - Sources:
  246.                         * VERY HELPFUL:             https://www.regular-expressions.info/dot.html
  247.                         * Cheat Sheet:              https://www.rexegg.com/regex-quickstart.html
  248.                         * std::regex_search():      https://en.cppreference.com/w/cpp/regex/regex_search
  249.                         * Cubbi's post explains each
  250.                           regex_search() generates 1
  251.                           match. To get more, you must
  252.                           iterate:                  http://www.cplusplus.com/forum/general/99302/#msg534005
  253.                         * std::regex_iterator():    https://en.cppreference.com/w/cpp/regex/regex_iterator
  254.  
  255.                 3. Else if line has #.# format (outside of quotes), then store it in container.
  256.                 4. Else if line has # format (outside of quotes), then store it container.
  257.                 5. Else don't store anything into container as it is actually symbols. And close file.
  258.         */
  259.         std::string line;
  260.         //std::regex desiredFormat(R"("[^"]*")"); // Original line when I was only detecting "contents. like this!" and not ints or doubles outside of quotes.
  261.        
  262.         // Attempted Solution 1:
  263.         std::regex r1(R"("[^"]*")");
  264.        std::regex r2(R"([0-9]*.[0-9]*)");
  265.        std::regex r3("[0-9]*");
  266.        //std::regex desiredFormat(r1|r2|r3);
  267.  
  268.        // Attempted Solution 2:
  269.        std::regex desiredFormat(R"("[^"]*")" | R"([0-9]*.[0-9]*)" | "[0-9]*");
  270.  
  271.         // Attempted Solution 3:
  272.         //std::regex desiredFormat((R"("[^"]*")") (R"([0-9]*.[0-9]*)") ("[0-9]*"));
  273.        
  274.         // 1.
  275.         while (std::getline(inFile, line)) {
  276.             std::cout << "[Debug] Line = \t" << line << "\n";
  277.  
  278.             // 2, 3, 4. (Need to do together or it won't act properly.)
  279.             // For example:
  280.             // "The Word" 1 is 3.4 "Hello"
  281.             // Would be stored as: (WRONG ORDER)
  282.             // - "The Word"
  283.             // - "Hello"
  284.             // - 1
  285.             // - 3.4
  286.             // Setup Regex Iterator:
  287.             auto lineBegin = std::sregex_iterator(line.begin(), line.end(), desiredFormat);
  288.             auto lineEnd = std::sregex_iterator();
  289.             // Iterate through line using regex_search():
  290.             for (std::sregex_iterator iter = lineBegin; iter != lineEnd; ++iter) {
  291.                 std::smatch match = *iter;
  292.                 std::string matchStr = match.str();
  293.                 std::cout << matchStr << "\n";
  294.                 alphaNums.push_back(matchStr);
  295.             }
  296.         }
  297.         // 5.
  298.         // Closes File:
  299.         inFile.close();
  300. }
  301.  
  302.  
  303. /*
  304. Algorithm Steps of this Function:
  305.     1. Open the filename using the outputFilePath variable.
  306.  
  307.     2. Check if outFilePath properly opened. If not, give error and exit program.
  308.  
  309.     3. Write to the output file.
  310.         - For every word in the vec from the function argument, write it into the output file. 1 word per line (enclosed in "").
  311.  
  312.     4. Close output file.
  313. */
  314. void writeJsonAsTxt(const fs::path &outFilePath, const std::vector<std::string> &alphaNums) {
  315.     // 1.
  316.     std::ofstream outFile(outFilePath);
  317.  
  318.     // 2.
  319.     if (!outFile.is_open()) {
  320.         std::cerr << "\nError! Could not open output file properly.\n"
  321.             << "Output file path:\n"
  322.             << outFilePath << '\n'
  323.             << "Program is exiting...\n";
  324.         exit(1);
  325.     }
  326.  
  327.     // 3.
  328.     for (std::string str : alphaNums) {
  329.         outFile << str << std::endl;
  330.     }
  331.  
  332.     // 4.
  333.     outFile.close();
  334. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement