I am working with sensitive information in Word and would like to remove some of the information that breaches privacy legislation. This information is variable, so I cannot do a simple replace. I was able to remove the information from the file after saving it as a text file and looping through the file and saving it to a new text file. The lines where the information occurs always starts with the same information, so that is what I used to trigger the removal of the private info. Ideally, I would like to be able to do this without saving the file first. I am a little familiar with VBA in word, but this is beyond my skillset at the moment.
sorry for the ambiguity....the information that needs to be replaced is variable, similar to a Social Security Number, thus a simple search and replace is not possible. A sample is provided below. What I am looking for is when the beginning of the line contains ID#, the first 4 characters of the ID number be either x'd out or replaced with a blank. The last three characters would still be visible for identification purposes.
-INQFS-07A-26-------------------------------------DATE:111018-162442-
ID#: XXXXXXX NAME: XXXXXX, XXXX LOCATION : XXXXXXXXXXXXXXXXXXXXXXXXX NUMBER : XXX PAGE: 1
--------------C U R R E N T -------------- OPENING BALANCE --------------S A V I N G S -------------- C A N T E E N
CASH HOLDS FREE BALANCE CASH HOLDS FREE BALANCE
306.52 7.50 299.02 DATE 11/10/11 91.68 0.00 91.68 90.00
This seems to do what I want it to do.
Sub MaskIDNUM()
'
' MaskIDNUM Macro
' Blank out first four characters of the ID# in a printout
'
Selection.WholeStory
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "ID#: ????"
.Replacement.Text = "ID# "
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
Related
I have a sort of long document with all parentheses toward one side; like (out of stock(.
Now I'm wondering how I can write a replace order replacing every other parenthesis.
I already know that the replace order is like:
Set myRange = ActiveDocument.Content myRange.Find.Execute FindText:="(", _ ReplaceWith:=")", Replace:=wdReplaceAll
but unfortunately I don't know how to apply
If i Mod 2 = 0 Then
to the code above.
Is there a way in VBA to search and delete cells that are only in a certain table, maybe based on the table title?
I have code written to delete cells that match the word I am looking for but this searches all tables in the document.
Dim myString As String
myString = "5"
Selection.Find.ClearFormatting
With Selection.Find
.Text = myString
.Wrap = wdFindContinue
.MatchWholeWord = True
End With
Do While Selection.Find.Execute
If Selection.Information(wdWithInTable) Then
Selection.Rows.Delete
End If
What would be the best way to limit this to only 1 table?
Also this would delete any cell that contains the word I am looking for, is there a way to limit it to cells that only contain what I am searching for.
Thanks for the help.
I have a macro that enables the user to double-click a given cell range on a Summary worksheet and access a hidden Data worksheet containing related data. When the user returns to the Summary worksheet, the Data worksheet is re-hidden.
The macro works perfectly for range D10:G15, but doesn't work for cell range C21:G26.
Summary worksheet:
VBA:
Private Sub Worksheet_Activate()
Dim sh As Worksheet
For Each sh In ThisWorkbook.Sheets
If sh.Name <> "Group Scorecard_Usage" Then
sh.Visible = xlSheetHidden
End If
Next sh
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Select Case Target.Address
Case "$D$10"
Sheets("John C - Total Applicants").Visible = True
Sheets("John C - Total Applicants").Activate
Case "$D$11"
Sheets("Doug D - Total Applicants").Visible = True
Sheets("Doug D - Total Applicants").Activate
Case "$D$12"
Sheets("Lesia - Total Applicants").Visible = True
Sheets("Lesia - Total Applicants").Activate
Case "$D$13"
Sheets("Jim Elder - Total Applicants").Visible = True
Sheets("Jim Elder - Total Applicants").Activate
Case "$D$14"
Sheets("Kevin Byrnes - Total Applicants").Visible = True
Sheets("Kevin Byrnes - Total Applicants").Activate
Case "$D$15"
Sheets("Chelsea W - Total Applicants").Visible = True
Sheets("Chelsea W - Total Applicants").Activate
Case "$E$10"
Sheets("Total_Candidates_Scott").Visible = True
Sheets("Total_Candidates_Scott").Activate
Case "$E$11"
Sheets("Total_Candidates_Doug").Visible = True
Sheets("Total_Candidates_Doug").Activate
Case "$E$12"
Sheets("Total_Candidates_Lesia").Visible = True
Sheets("Total_Candidates_Lesia").Activate
Case "$E$13"
Sheets("Total_Candidates_Jim Elder").Visible = True
Sheets("Total_Candidates_Jim Elder").Activate
Case "$E$14"
Sheets("Total_Candidates_Mark").Visible = True
Sheets("Total_Candidates_Mark").Activate
Case "$E$15"
Sheets("Total_Candidates_Chelsea").Visible = True
Sheets("Total_Candidates_Chelsea").Activate
Case "$G$10"
Sheets("Unreviewed Applicants - Scott Z").Visible = True
Sheets("Unreviewed Applicants - Scott Z").Activate
Case "$G$11"
Sheets("Unreviewed Applicants - Doug").Visible = True
Sheets("Unreviewed Applicants - Doug").Activate
Case "$G$12"
Sheets("Unreviewed Applicants - Lesia O").Visible = True
Sheets("Unreviewed Applicants - Lesia O").Activate
Case "$G$13"
Sheets("Unreviewed Applicants - Jim").Visible = True
Sheets("Unreviewed Applicants - Jim").Activate
Case "$G$14"
Sheets("Unreviewed Applicants - Mark").Visible = True
Sheets("Unreviewed Applicants - Mark").Activate
Case "$C$21”"
Sheets("Scott_Hires_wo_ps").Visible = True
Sheets("Scott_Hires_wo_ps").Activate
Case "$C$22”"
Sheets("Doug_Hires_wo_ps").Visible = True
Sheets("Doug_Hires_wo_ps").Activate
Case "$C$23”"
Sheets("Lesia_Hires_wo_ps").Visible = True
Sheets("Lesia_Hires_wo_ps").Activate
Case "$C$24”"
Sheets("Jim_Hires_wo_ps").Visible = True
Sheets("Jim_Hires_wo_ps").Activate
Case "$C$25”"
Sheets("Mark_Hires_wo_ps").Visible = True
Sheets("Mark_Hires_wo_ps").Activate
Case "$C$26”"
Sheets("Chelsea_Hires_wo_ps").Visible = True
Sheets("Chelsea_Hires_wo_ps").Activate
Case "$D$21”"
Sheets("Scott_non_scheduled_inpersons").Visible = True
Sheets("Scott_non_scheduled_inpersons").Activate
Case "$D$22”"
Sheets("Doug_non_scheduled_inperson").Visible = True
Sheets("Doug_non_scheduled_inperson").Activate
Case "$D$23”"
Sheets("Lesia_non_scheduled_inpersons").Visible = True
Sheets("Lesia_non_scheduled_inpersons").Activate
Case "$D$24”"
Sheets("Jim_non_scheduled_inperson").Visible = True
Sheets("Jim_non_scheduled_inperson").Activate
Case "$D$25”"
Sheets("Mark_non_scheduled_inpersons").Visible = True
Sheets("Mark_non_scheduled_inpersons").Activate
Case "$D$26”"
Sheets("Chelsea_ns_inpersons").Visible = True
Sheets("Chelsea_ns_inpersons").Activate
Case "$E$21”"
Sheets("Scott_nc_inpersons").Visible = True
Sheets("Scott_nc_inpersons").Activate
Case "$E$23”"
Sheets("Lesia_nc_inpersons").Visible = True
Sheets("Lesia_nc_inpersons").Activate
Case "$E$26”"
Sheets("Chelsea_nc_inpersons").Visible = True
Sheets("Chelsea_nc_inpersons").Activate
Case "$F$22”"
Sheets("Doug_Reference_Checks").Visible = True
Sheets("Doug_Reference_Checks").Activate
Case "$F$23”"
Sheets("Lesia_Reference_Checks").Visible = True
Sheets("Lesia_Reference_Checks").Activate
Case "$F$24”"
Sheets("Jim_Elder_Reference_Checks").Visible = True
Sheets("Jim_Elder_Reference_Checks").Activate
Case "$F$25”"
Sheets("Mark_Reference_Checks").Visible = True
Sheets("Mark_Reference_Checks").Activate
Case "$F$26”"
Sheets("Chelsea_Reference_Checks").Visible = True
Sheets("Chelsea_Reference_Checks").Activate
Case "$G$23”"
Sheets("Lesia_BGCs").Visible = True
Sheets("Lesia_BGCs").Activate
Case "$G$25”"
Sheets("Mark_BGCs").Visible = True
Sheets("Mark_BGCs").Activate
Case "$G$26”"
Sheets("Chelsea_BGCs").Visible = True
Sheets("Chelsea_BGCs").Activate
End Select
End Sub
I'm trying to get a handle on what I'm doing incorrectly. Any help would be tremendously appreciated.
You should never, ever have to write different lines of code for each bit of data you might get. This is the opposite of the purpose of coding.
I suspect there's relevant data in columns A & B that you haven't included... like perhaps, the person's name, in column A perhaps? If not, do that (insert a column if necessary). Pick a naming convention and stick with it. (ie., hyphens, underscores or spaces, not a combination) You'll save yourself (and others) a lot of headaches.
Avoid super-long sheet names. They're hidden anyway so you can make them simpler and more standard. Perhaps: APP, CAN, UNR, WOP, NSI, NCI, REF, BGC.
Why do all these sheets need to be hidden? It's not preventing people from accessing them. Perhaps it's because there's just too darn many of them that it's cluttered? Instead of hiding/showing them constantly you might as well hide all the tabs with:
ActiveWindow.DisplayWorkbookTabs = False
After these changes, you could replace almost all your code with a couple lines similar to:
personName = cells(Target.Row,1)
Sheets(personname & "NSI")
I bet there are hours spent on maintaining this workbook every week. If this data is at all important, what you really should do is revamp is=t completely.
Ideally, you would move it to Microsoft Access since it's made for managing databases (as opposed to creating your own database in Excel). Even if you've never used Access, you'd still find it simpler than what you're doing here.
Short of that, at the very least: put all of this data on one tab. Excel has numerous simple to use features for filter and analyzing data that are useless when you split up the data like this. Keep it all together and make Excel do the work of display what you want, when you want. You're attempting to create functionality from scratch, that Microsoft perfected for you a long time ago. AutoFilter, Grouping, Pivot Tables could all save you so much time and make this workbook easier for everyone else to use.
It would be a good idea to learn Excel's built-in worksheet functionality inside and out before trying to get into VBA. There are a ton of great (free/easy) resources and forums out there that could help you, and I guarantee you'd be glad you did.
One indication that you might be getting ahead of yourself, happens to also be the solution your actual question..
"...works perfectly fine for range D10:G15, but doesn't work for cell range C21:G26..."
Details Matter.
So, you know something changes between G15 and C21. It makes sense to inspect that part of the code closely.
First off, there is no G15 -- but besides that, if we look closely:
...looking even closer:
Beginning there, until the end of your code, you somehow switched to the wrong type of quotation mark on half of your functions.
Final couple tips that will help prevent problems like this:
Put, as the first line of every module: Option Explicit. This will force you to properly declare and handle variables, properties, and more.
Before making changes, BACKUP. If you're making 10 major changes in a day, you should keep all 10 versions, for at least a few weeks. You'd be surprised how handy they come in, especially while learning.
Test your code after every change you make. It might seem time consuming, but this is an example of how it would have saved time in the long run.
I hope didn't sound like I'm insulting you or your work, and it's definitely not my intention to discourage you from learning, but it is very important to be 100% thorough, accurate and organized in this field, and to understand a skill completely before moving on to the next one, especially in the earlier stages. Be sure to check out some of the other great Excel forums out there too. Good luck!
Edit: Example solution
This is an example of what a difference some basic organization would make. The code below has the same functionality of all of the code in the question (except this one's bug free, error handling, and automatically adaptable to new names/reports).
Required Assumptions/Organization:
Column A contains the the manager name (worksheet name suffix): FirstName LastInitial ex. Jim E is in A13 and A24
Rows 9 & 20 are hidden and contain the "report code" (worksheet name prefix) : ex. D9 = TotApp & G20 = RefChk
Worksheets are renamed accordingly (ex: TotApp Jim E, RefChk Jim E)
Therefore we have simple logic:
worksheet prefix is always column A of the clicked row.
if row is between 10-15 then row 9 of clicked column contains suffix.
if row is between 21-26 then row 20 of clicked column contains suffix.
therefore, the entire clickable range is C10:G15 and C21:G26
Code:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim destSht As String
'make sure double-clicked cell is within range
If Application.Intersect(Range("C10:G26"), Target) Is Nothing Then
Cancel = True 'cancel the double-click
Exit Sub
End If
'find the worksheet name suffix
Select Case Target.Row
Case 10 - 15
destsht = Cells(9, Target.Column)
Case 21 - 26
destsht = Cells(20, Target.Column)
Case Else
Cancel = True 'cancel the double-click
Exit Sub
End Select
'find the worksheet name prefix
destsht = Cells(Target.Row, 1) & destsht
'Unhide & activate worksheet
With Worksheets(destsht)
.Visible = True
.Activate
End With
End Sub
Length: 700 char (compared to 4700 char) (1/7th the size of the original)
I have several rows of information pulled from a report in Column C and D, its basically a description someone wants to do with an account they also of course use give you the account number what I want to do is extract that substring the criteria I'm using is that it must start with the letter A and should be as a minimum 17 characters long, Account numbers have a combination of letter and numbers but they all start with letter A i.e A8H66P66FHDSJ2YNTP some of this account numbers have up to 25 characters some have 19 some 17 so again I'm looking to extract a substring from a string that starts with letter A and its atleast 17 characters long
Try to use RegEx as shown in the below example:
Sub Test()
Dim oCell, oMatch
With CreateObject("VBScript.RegExp")
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = "\bA[A-Z0-9]{16,24}\b"
For Each oCell In ThisWorkbook.Sheets("Sheet1").Range("C1:D1000")
For Each oMatch In .Execute(oCell.Value)
Debug.Print oMatch.Value
Next
Next
End With
End Sub
Formula solution:
=IFERROR(TRIM(MID(SUBSTITUTE(C1," ",REPT(" ",LEN(C1))),LEN(C1)*(MATCH(TRUE,INDEX(ISNUMBER(SEARCH("A"&REPT("?",17),TRIM(MID(SUBSTITUTE(C1," ",REPT(" ",LEN(C1))),LEN(C1)*(ROW($1:$100)-1)+1,LEN(C1))))),),0)-1)+1,LEN(C1))),"No Account Number")
The macro below is supposed to pull the average words per sentence, then turn the text red in all sentences that are >=150% of that.
The problem is, it turns some shorter sentences red, as well. For example, it colored these sentences (edited to add: in the source doc, 150% of average length is 35 words):
31 words: The FSAIPs provide the basis for evaluation of the adequacy of the regulatory implementation of the design based on this assumed operational process and supports the preparation of prospective dose assessments.
29 words: (In accordance with 10 CFR 835.2, the equivalent dose rate criteria are applicable at 30 cm from the radiation source or 30 cm from any surface the radiation penetrates.)
(I'd share more examples, but this is a radiation control procedure on a Federal nuclear project, so I'm having to choose carefully.)
Those word counts for the sentences above are from the status bar at the bottom of the window. So Word appears to be counting the number of words differently depending on what part of Word is counting. I think.
Are there any suggestions on how to make the count more accurate, or at least the same for both situations? Oh, and a final note: it's not counting visible deleted words. It may be counting things like nonbreaking hyphens in some instances, but not in the ones shared above.
Sub Mark_Long()
'''''''''''''''''''
' Adapted from "Allen Wyatt's Word Tips, wordribbon.tips.net.
' I added to it so it pulls the avg sentence length from
' the readability stats, and only marks the sentences that are 150%
' of the average.
''''''''''''''''''''
Dim iMyCount As Integer
Dim iWords As Integer
Dim bTrackingAsWas As Boolean
If Not ActiveDocument.Saved Then
ActiveDocument.Save
End If
Set myRange = ActiveDocument.Content
wordval = myRange.ReadabilityStatistics(6).Value
bTrackingAsWas = ActiveDocument.TrackRevisions
'Turn off tracked changes
ActiveDocument.TrackRevisions = False
'Reset counter
iMyCount = 0
'Set number of words
iWords = (wordval * 1.5)
For Each MySent In ActiveDocument.Sentences
If MySent.Words.Count > iWords Then
MySent.Font.Color = wdColorRed
iMyCount = iMyCount + 1
End If
Next
'Restore tracked changes
ActiveDocument.TrackRevisions = bTrackingAsWas
'Report results
MsgBox iMyCount & " sentences longer than " & _
iWords & " words."
End Sub
you should use .Range.ComputeStatistics(wdStatisticWords) instead of .Words.Count.
The first returns a filtered value, the second an unfiltered
See:
http://www.vbaexpress.com/forum/archive/index.php/t-21723.html
The property .Words returns real words but also punctuation marks and paragraph marks. To get the real word count you can use this - a little bit weird - method.
Set dlg = Dialogs(wdDialogToolsWordCount)
For Each MySent In ActiveDocument.Sentences
MySent.Select
Set dlg = Dialogs(wdDialogToolsWordCount)
dlg.Execute
Count = dlg.Words
' Count is the number you are looking for
Next
You just simulate the 'Word Count' dialog.