Advertisement
Guest User

Untitled

a guest
Oct 20th, 2019
233
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.77 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: main.cpp
  7. Version: 3
  8. Author: Tommy Weber
  9. Date: 10/18/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: When 3 folders depth into InputFolder & OutputFolder, it will create the folders & file still but won't output data to the file. Example in: OutputFiles\firstDirectTest\2ndDirectTest\3rdDirectTest on local Desktop PC.
  16.     - Algorithm Steps and comments are up to date as of 10/18/2019 at 6:30 pm MDT.
  17.  
  18. -------------------------------
  19. Follow Good Practices:
  20.     1. Use algorithm steps (in English) first.
  21.     2. Use compiler flags.
  22.     3. Use static analyzers to test for bugs.
  23.     4. Use >> and << when dividing and multiplying by 2. Find the file that explains it.
  24.     5. A function should only do one thing, not two (like printInstructionsAndGetInput() is bad)
  25.     6. Make sure function names are specific to EXACTLY what it does. openFiles() is not correct for something that iterates through a directory recursively
  26.     #. Etc...
  27.  
  28. Overall Algorithm Steps: (Not numbered in code like other algorithm steps are)
  29.     1.
  30.         a. Tell user to input the desired files to be converted into the "InputFiles" folder path. Done in printInstructions()
  31.         b. Tell user to input 'Y' when ready or 'n' to exit. Done in getUserConfirmation()
  32.  
  33.     2. Open all files one at a time in the file path with the "InputFiles" folder.
  34.         - Done in iterateInDirectory()
  35.  
  36.     ---
  37.     For Each File (#3-#8):
  38.     3. In each file, read each char and keep track of where there are:
  39.         a. Double quotes
  40.             - Things within double quotes are saved.
  41.             - Example: "minecraft:type"
  42.         b. Numbers
  43.             - We will save all numbers in the file (even if they are outside double quotes).
  44.  
  45.     4. Store everything that matches what we want in #3 into a vector of alphabetical & number contents.
  46.    
  47.     5. Close input file.
  48.    
  49.     6. Create an output file in "OutputFiles" with same name but with preferred extension (.txt by default) instead of .json.
  50.    
  51.     7. In output file write each item in the vector to the file in a new line.
  52.         - Example vector: {""type"", ""minecraft:crafting_shaped"", ""group"", ""bark"", "333", ""444""}
  53.         - Writing example vector to file:
  54.             "type"
  55.             "minecraft:crafting_shaped"
  56.             "group"
  57.             "bark"
  58.             333
  59.             "444"
  60.  
  61.     8. Close output file.
  62.     ---
  63.  
  64.     9. Tell user that program has finished.
  65.  
  66. HOW TO COMPILE:
  67.     $ cppcheck --enable=all main.cpp
  68.     $ g++ -std=c++17 main.cpp -lstdc++fs -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic
  69.     $ ./a.out
  70. */
  71. #include <iostream>
  72. #include <iomanip> // output formatting in console
  73. #include <string>
  74. #include <fstream>
  75. #include <filesystem>
  76. #include <vector> // for storing words from json file and writing them to output files.
  77.  
  78. namespace fs = std::filesystem; // for convenience
  79.  
  80. void printInstructions(const int width);
  81. void getUserConfirmation(const int width); // confirms user has followed instructions & program can parse files
  82. void iterateInDirectory(); // Recursively iterate through the filesystem "InputFiles" directory
  83. void parseJsonFile(const std::string &entryStr, std::vector<std::string> &alphaNums);
  84. void writeJsonAsTxt(std::string &filename, const std::vector<std::string> &alphaNums);
  85.  
  86. int main() {
  87.     const int printWidth = 50; // the num of '-' or '=' in a line output to terminal
  88.     printInstructions(printWidth);
  89.     getUserConfirmation(printWidth);
  90.  
  91.     iterateInDirectory();
  92.  
  93.     std::cout << "The program has completed successfully!\n";
  94.    
  95.     return 0;
  96. }
  97.  
  98.  
  99. void printInstructions(const int width) {
  100.     const char sym = '-';
  101.  
  102.     std::cout << std::setfill(sym) << std::setw(width) << "\n"
  103.         << "Hello, thank you for using JSON File Simplifier!\n\n"
  104.         << "Note(s):\n"
  105.         << "- This program does not support decimals in folder names. If you have decimals, change them to '_'.\n"
  106.         << '\n'
  107.         << "Usage Steps:\n"
  108.         << "1. To get started, open the file path this program is located at on your computer.\n"
  109.         << "2. Now move the desired files you would like to simplify into the " << std::quoted("InputFiles") << " directory.\n";
  110. }
  111.  
  112. void getUserConfirmation(const int width) {
  113.     std::cout << "3. Input 'y' when ready or input 'n' to quit: "
  114.               << std::setw(width) << std::setfill(' ');
  115.    
  116.     char ready = 'n';
  117.     std::cin >> ready;
  118.     std::cout << '\n';
  119.     if (ready != 'y' && ready != 'Y') {
  120.         std::cout << "Program has ended. You may now close the program.\n";
  121.         exit(0);
  122.     }
  123. }
  124.  
  125. /*
  126. Algorithm Steps of this Function:
  127.     1. Create the path variable.
  128.         - Make sure it includes the current path and InputFiles directory.
  129.  
  130.     2. For each file (recursively) in the InputFiles directory:
  131.         a. Create a string called outputEntryStr & set it to the entry path.
  132.         b. Check if entry is a directory file type. If so, we want to replace "Input" with "Output" in the ouputEntryStr so later we can create these directories in the "OutputFiles" folder. To do this:
  133.             * Find the position of the 'I' in "InputFiles" in the outputEntryStr then set an int variable to this position.
  134.             * Replace "Input" with "Output" in the outputEntryStr so the total path now looks like this example: /mnt/c/Users/Tom/Desktop/json_file_simplifier/OutputFiles/1_13
  135.             * Now check if the outputEntryStr path exists because that's where we will write output files to. If it doesn't exist, create it.
  136.         c. Create a string called entryStr & set it to the entry path.
  137.     d. Check if entryStr is a regular file. If so:
  138.             * Create an empty vec called alphaNums which will contain all words and numbers for a SINGLE file at a time.
  139.             * Call parseJsonFile()
  140.             * Call writeJsonAsTxt()
  141. */
  142. void iterateInDirectory() {
  143.     // 1.
  144.     fs::path fullInPath = fs::current_path();
  145.     fullInPath /= "InputFiles";
  146.  
  147.     // 2.
  148.     // Note: This for loop line is not my code, got it from a guide. Idk how to do it in my own code.
  149.     for (auto entry = fs::recursive_directory_iterator(fullInPath);
  150.               entry != fs::recursive_directory_iterator(); // (entry does not equal no entry, similar to EOF)
  151.               ++entry ) { // iterate to next entry
  152.        
  153.         // --------------------------
  154.         // outputEntryStr section:
  155.         // --------------------------
  156.         // 2a.
  157.         std::string outputEntryStr = entry->path().string();
  158.         int inputPosInEntry = outputEntryStr.find("InputFiles");
  159.         outputEntryStr.replace(outputEntryStr.begin()+inputPosInEntry, outputEntryStr.begin()+inputPosInEntry+2, "Out");
  160.        
  161.         // 2b.
  162.         if (entry->is_directory()) {
  163.             if (!fs::exists(outputEntryStr)) {
  164.                 fs::create_directories(outputEntryStr);
  165.             }
  166.         }
  167.  
  168.         // ----------------------
  169.         // entryStr section:
  170.         // ----------------------
  171.         // 2c.
  172.         std::string entryStr = entry->path().string(); // Ex 1: /mnt/c/Users/Tom/Desktop/json_file_simplifier/InputFiles/1_13
  173.                                                        // Ex 2: /mnt/c/Users/Tom/Desktop/json_file_simplifier/InputFiles/1_13/acacia_boat.json
  174.         // 2d.
  175.         if (entry->is_regular_file()) {
  176.             std::vector<std::string> alphaNums = {};
  177.  
  178.             parseJsonFile(entryStr, alphaNums);
  179.            
  180.             writeJsonAsTxt(outputEntryStr, alphaNums);
  181.         }
  182.     }
  183. }
  184.  
  185.  
  186. /*
  187. Algorithm Steps of this Function:
  188.     1. Open the path of the current file.
  189.  
  190.     2. If the file couldn't be opened, print error and stop program.
  191.  
  192.     3. Define some variables that will be used for getting the proper data from the input file inside the while loop.
  193.  
  194.     4. Read the file one char at a time. (I wonder if there is a better way?)
  195.         a. If the char is a " then:
  196.             - add to the number of quotes (keeping track with a var)
  197.         b. If the count of quotes is equal to 1 then:
  198.             - add the current char to the word variable
  199.         c. If the char is a " again then:
  200.             - Add to the number of quotes (so it now equals 2)
  201.         d. If the count of quotes is greater than or equal to 2 then:
  202.             - add the current char (the " symbol) to the word variable.
  203.             - then add the word to a vector.
  204.             - then clear the word variable to prep for next word.
  205.    
  206.     5. Close the input file.
  207.        
  208.     Important Note of This Method:
  209.         - This code assumes there are no quotes within quotes.
  210.             * Ex: "  "minecraft:word" " in the files.
  211. */
  212. void parseJsonFile(const std::string &entryStr, std::vector<std::string> &alphaNums) {
  213.         // 1.
  214.         std::ifstream inFile(entryStr);
  215.  
  216.         // 2.
  217.         if (!inFile.is_open()) {
  218.             std::cout << entryStr << " couldn't be opened!\n"
  219.                 << "Program stopped.\n";
  220.                 exit(1);
  221.         }
  222.  
  223.         // 3.
  224.         char c;
  225.         std::string alphaNum;
  226.         bool quoteBlockStarted = false;
  227.         bool numStarted = false;
  228.         bool numHasDecimalAlready = false;
  229.  
  230.         // 4.
  231.         /*
  232.         Goal:
  233.             - Anything inside " " should be saved.
  234.             - Any integer numbers outside of " " should be saved. Examples: 30 or 334
  235.             - Any double numbers outside of " " should be saved. Examples: 3.14 or 55.12
  236.         */
  237.         // CODE CURRENTLY CHECKS EVERY CHAR OF FILE ONE AT A TIME. Is it possible to improve the efficiency / performance of this? Algorithm analysis.
  238.         while (inFile.get(c)) {
  239.             // Program does not support adding numbers that are like: .23, only 0.23
  240.             // STARTING NUM HANDLER:
  241.             if (quoteBlockStarted == false && (std::isdigit(c)) && numStarted == false) {
  242.                 numStarted = true; // Shows the previous char was a num too, and thus is still adding digits to alphaNum to equal 1 number of multiple digits.
  243.                 alphaNum += c;
  244.             }
  245.  
  246.             // NUM ALREADY STARTED HANDLER:
  247.             else if (quoteBlockStarted == false && (std::isdigit(c) || c == '.') && numStarted == true) {          
  248.                 // add num or decimal to alphaNum:
  249.                 // - add decimal to alphaNum:
  250.                 //   * Makes sure it is not adding a second decimal to a number that already has one.
  251.                 if (c == '.' && numHasDecimalAlready == false) {
  252.                     alphaNum += c;
  253.                     numHasDecimalAlready = true; // preps for next iteration
  254.                 }
  255.                 // if it looks like: 55... and we're at the 2nd decimal so alphaNum = 55. atm, then remove the dot.
  256.                 else if (c == '.' && numHasDecimalAlready == true) {
  257.                     alphaNum.resize(alphaNum.size()-1);
  258.                 }
  259.                 // - add num to alphaNum:
  260.                 else if (std::isdigit(c)) {
  261.                     alphaNum += c;
  262.                 }
  263.             }
  264.  
  265.             // NUM ENDED HANDLER & CHECKING CHAR FOR QUOTE HANDLER (WHEN NUM HAS JUST ENDED):
  266.             else if (quoteBlockStarted == false && (!std::isdigit(c) || c != '.') && numStarted == true) {
  267.                 alphaNums.push_back(alphaNum);
  268.                 // Resets:
  269.                 alphaNum = "";
  270.                 numStarted = false;
  271.                 numHasDecimalAlready = false;
  272.  
  273.                 // Checks if current char is a " symbol.
  274.                 // - If so, will add it to alphaNum and change quoteBlockStarted to true.
  275.                 if (c == '"') {
  276.                     alphaNum += c;
  277.                     quoteBlockStarted = true;
  278.                 }
  279.             }
  280.  
  281.             // STARTING QUOTE HANDLER:
  282.             else if (quoteBlockStarted == false && c == '"') {
  283.                 alphaNum += c;
  284.                 quoteBlockStarted = true;
  285.             }
  286.             // DURING QUOTE HANDLER (has not reached end of quote yet):
  287.             else if (quoteBlockStarted == true && c != '"') {
  288.                 alphaNum += c;
  289.             }
  290.             // QUOTE ENDED HANDLER:
  291.             else if (quoteBlockStarted == true && c == '"') {
  292.                 alphaNum += c;
  293.                 alphaNums.push_back(alphaNum);
  294.                
  295.                 // Resets:
  296.                 alphaNum = "";
  297.                 quoteBlockStarted = false;
  298.             }
  299.         }
  300.  
  301.         /*
  302.         // DEBUG Testing - Print Vector Contents
  303.         std::cout << "[DEBUG] alphaNums = \n";
  304.         for (std::string str : alphaNums) {
  305.             std::cout << str << "\n";
  306.         }
  307.         */
  308.        
  309.  
  310.         // 5.
  311.         // Closes File:
  312.         inFile.close();
  313. }
  314.  
  315.  
  316. /*
  317. Algorithm Steps of this Function:
  318.     1. Replace the old extension of the file in outputEntryStr to the desired output file extension (default: txt) if it isn't already.
  319.         - Determine position of last '.' in string and set that to an int variable.
  320.         - Declare a string variable containing the desired output file extension (default: txt).
  321.         - if program does not find the desired output file extension (default: txt) then erase the old extension and set the new one.
  322.    
  323.     2. Open the filename using the outputEntryStr variable.
  324.  
  325.     3. Check if outFile properly opened. If not, give error and exit program.
  326.  
  327.     4. Write to the output file.
  328.         - For every word in the vec from the function argument, write it into the output file. 1 word per line (enclosed in "").
  329.  
  330.     5. Close output file.
  331. */
  332. void writeJsonAsTxt(std::string &filename, const std::vector<std::string> &alphaNums) {
  333.  
  334.     // 1.
  335.     int posOfextensionStart = filename.find_last_of('.');
  336.     std::string desiredOutputExtension = "txt";
  337.    
  338.     // Remove old extension if not desired output extension then add desired output extension.
  339.     if (filename.find(desiredOutputExtension) == std::string::npos) { // if desired output extension not found in string:
  340.         filename.erase(filename.begin()+posOfextensionStart+1, filename.end());
  341.         filename.append(desiredOutputExtension);
  342.     }
  343.  
  344.     // 2.
  345.     std::ofstream outFile(filename);
  346.  
  347.     // 3.
  348.     if (!outFile.is_open()) {
  349.         std::cerr << "\nError! Could not open output file properly.\n"
  350.             << "Output file path: " << filename << '\n'
  351.             << "Program is exiting...\n";
  352.         exit(1);
  353.     }
  354.  
  355.     // 4.
  356.     for (std::string str : alphaNums) {
  357.         outFile << str << std::endl;
  358.     }
  359.  
  360.     // 5.
  361.     outFile.close();
  362. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement